Add EveFlutter provider picker proof
This commit is contained in:
parent
2709061fd8
commit
e2e9b21e2f
6 changed files with 235 additions and 4 deletions
|
|
@ -122,6 +122,126 @@ class EveSurfaceState {
|
|||
}
|
||||
}
|
||||
|
||||
class EveProviderCatalog {
|
||||
const EveProviderCatalog({required this.providers});
|
||||
|
||||
final List<EveProviderEntry> providers;
|
||||
|
||||
factory EveProviderCatalog.fromConformanceExport(Map<String, dynamic> json) {
|
||||
final providers = ((json['providers'] as List<dynamic>?) ?? const [])
|
||||
.map((provider) {
|
||||
return EveProviderEntry.fromJson(provider as Map<String, dynamic>);
|
||||
})
|
||||
.toList(growable: false);
|
||||
return EveProviderCatalog(providers: providers);
|
||||
}
|
||||
|
||||
EveProviderEntry? findProvider(String providerId) {
|
||||
for (final provider in providers) {
|
||||
if (provider.providerId == providerId) return provider;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class EveProviderEntry {
|
||||
const EveProviderEntry({
|
||||
required this.providerId,
|
||||
required this.status,
|
||||
required this.ownerRepo,
|
||||
required this.advertisementPath,
|
||||
required this.surfaces,
|
||||
required this.commands,
|
||||
required this.pluginRequirements,
|
||||
});
|
||||
|
||||
final String providerId;
|
||||
final String status;
|
||||
final String ownerRepo;
|
||||
final String advertisementPath;
|
||||
final List<String> surfaces;
|
||||
final List<String> commands;
|
||||
final List<EvePluginRequirement> pluginRequirements;
|
||||
|
||||
factory EveProviderEntry.fromJson(Map<String, dynamic> json) {
|
||||
return EveProviderEntry(
|
||||
providerId: (json['providerId'] ?? '').toString(),
|
||||
status: (json['status'] ?? '').toString(),
|
||||
ownerRepo: (json['ownerRepo'] ?? '').toString(),
|
||||
advertisementPath: (json['advertisementPath'] ?? '').toString(),
|
||||
surfaces: _stringList(json['surfaces']),
|
||||
commands: _stringList(json['commands']),
|
||||
pluginRequirements:
|
||||
((json['pluginRequirements'] as List<dynamic>?) ?? const [])
|
||||
.map(
|
||||
(requirement) => EvePluginRequirement.fromJson(
|
||||
requirement as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EvePluginRequirement {
|
||||
const EvePluginRequirement({
|
||||
required this.surfaceId,
|
||||
required this.pluginId,
|
||||
required this.requiredCapabilities,
|
||||
});
|
||||
|
||||
final String surfaceId;
|
||||
final String pluginId;
|
||||
final List<String> requiredCapabilities;
|
||||
|
||||
factory EvePluginRequirement.fromJson(Map<String, dynamic> json) {
|
||||
return EvePluginRequirement(
|
||||
surfaceId: (json['surfaceId'] ?? '').toString(),
|
||||
pluginId: (json['pluginId'] ?? '').toString(),
|
||||
requiredCapabilities: _stringList(json['requiredCapabilities']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _stringList(Object? value) {
|
||||
return ((value as List<dynamic>?) ?? const [])
|
||||
.map((item) => item.toString())
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
class EveProviderPicker extends StatelessWidget {
|
||||
const EveProviderPicker({
|
||||
required this.catalog,
|
||||
required this.selectedProviderId,
|
||||
required this.onSelected,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final EveProviderCatalog catalog;
|
||||
final String selectedProviderId;
|
||||
final ValueChanged<String> onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownButton<String>(
|
||||
value: catalog.findProvider(selectedProviderId) == null
|
||||
? null
|
||||
: selectedProviderId,
|
||||
hint: const Text('Select provider'),
|
||||
items: [
|
||||
for (final provider in catalog.providers)
|
||||
DropdownMenuItem<String>(
|
||||
value: provider.providerId,
|
||||
child: Text(provider.providerId),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) onSelected(value);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EveNode {
|
||||
EveNode({
|
||||
required this.id,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,49 @@ void main() {
|
|||
expect(intents.single.clientId, 'flutter-parity');
|
||||
expect(intents.single.payload['action']['operation'], 'focus_entity');
|
||||
});
|
||||
|
||||
testWidgets('provider picker consumes conformance export providers', (
|
||||
tester,
|
||||
) async {
|
||||
final catalog = EveProviderCatalog.fromConformanceExport(
|
||||
_providerCatalogExport,
|
||||
);
|
||||
var selected = 'aetheria';
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return EveProviderPicker(
|
||||
catalog: catalog,
|
||||
selectedProviderId: selected,
|
||||
onSelected: (providerId) {
|
||||
setState(() => selected = providerId);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(catalog.providers, hasLength(2));
|
||||
expect(catalog.findProvider('gamecult.home.vn')?.ownerRepo, 'Sai');
|
||||
expect(
|
||||
catalog
|
||||
.findProvider('gamecult.home.vn')
|
||||
?.pluginRequirements
|
||||
.map((requirement) => requirement.pluginId),
|
||||
containsAll(['sai.vn', 'norn.graph', 'tex.math']),
|
||||
);
|
||||
expect(find.text('aetheria'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(DropdownButton<String>));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('gamecult.home.vn').last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(selected, 'gamecult.home.vn');
|
||||
});
|
||||
}
|
||||
|
||||
const fixtureId = String.fromEnvironment(
|
||||
|
|
@ -142,3 +185,48 @@ class _Viewport {
|
|||
final double width;
|
||||
final double height;
|
||||
}
|
||||
|
||||
const _providerCatalogExport = {
|
||||
'schema': 'gamecult.eve.conformance_export.v1',
|
||||
'providers': [
|
||||
{
|
||||
'providerId': 'aetheria',
|
||||
'status': 'advertised',
|
||||
'ownerRepo': 'Aetheria',
|
||||
'advertisementPath': 'web/fixtures/aetheria.provider-advertisement.json',
|
||||
'surfaces': ['aetheria.daemon.game'],
|
||||
'commands': ['aetheria.daemon.commands'],
|
||||
'pluginRequirements': [],
|
||||
},
|
||||
{
|
||||
'providerId': 'gamecult.home.vn',
|
||||
'status': 'advertised',
|
||||
'ownerRepo': 'Sai',
|
||||
'advertisementPath': 'web/fixtures/sai-vn.provider-advertisement.json',
|
||||
'surfaces': ['sai.visual_novel.surface'],
|
||||
'commands': ['story.choose', 'story.continue', 'story.jump'],
|
||||
'pluginRequirements': [
|
||||
{
|
||||
'surfaceId': 'sai.visual_novel.surface',
|
||||
'pluginId': 'sai.vn',
|
||||
'requiredCapabilities': [
|
||||
'vn.stage',
|
||||
'story.choose',
|
||||
'story.continue',
|
||||
'story.jump',
|
||||
],
|
||||
},
|
||||
{
|
||||
'surfaceId': 'sai.visual_novel.surface',
|
||||
'pluginId': 'norn.graph',
|
||||
'requiredCapabilities': ['embed.norn'],
|
||||
},
|
||||
{
|
||||
'surfaceId': 'sai.visual_novel.surface',
|
||||
'pluginId': 'tex.math',
|
||||
'requiredCapabilities': ['embed.tex'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue