diff --git a/lib/api/grpc/creds.dart b/lib/api/grpc/creds.dart new file mode 100644 index 0000000..a3849db --- /dev/null +++ b/lib/api/grpc/creds.dart @@ -0,0 +1,17 @@ +import 'dart:html'; + +class SoftplayerCreds { + final String uuid; + final String token; + + SoftplayerCreds({ + required this.token, + required this.uuid, + }); +} + +class SoftplayerCredsHelpers { + String localStorageEntry(String key) => window.localStorage[key]!; + SoftplayerCreds fromLocalStorage() => SoftplayerCreds( + token: localStorageEntry("token"), uuid: localStorageEntry("uuid")); +} diff --git a/lib/api/grpc/environments.dart b/lib/api/grpc/environments.dart index bb8317b..2846a14 100644 --- a/lib/api/grpc/environments.dart +++ b/lib/api/grpc/environments.dart @@ -1,37 +1,52 @@ -import 'dart:html'; - -import 'package:grpc/grpc_web.dart'; import 'package:softplayer_dart_proto/main.dart'; +import 'package:softplayer_web/api/grpc/creds.dart'; class EnvironmentLocalData { EnvironmentLocalData({ required this.uuid, required this.token, }); + String uuid; String token; } class EnvironmentsGrpc { - final GrpcWebClientChannel channel; - late EnvironmentsClient envStub; - EnvironmentsGrpc({ - required this.channel, - }); + // final GrpcWebClientChannel channel; + final EnvironmentsClient envStub; - void init() { - envStub = EnvironmentsClient(channel); + // Init the grpc channel for environments + EnvironmentsGrpc(channel) : envStub = EnvironmentsClient(channel); + + // Get environments from the API + Future get(String name, SoftplayerCreds creds) async { + final request = GetOptions( + name: EnvironmentName(name: name), + ownerId: OwnerId(uuid: creds.uuid), + token: Token(token: creds.token), + ); + + try { + final response = await envStub.get(request); + return response; + } catch (e) { + rethrow; + } } - Stream> list() async* { - List envs = []; + Stream> list(SoftplayerCreds creds) async* { + List envs = []; try { - await for (var feature in envStub.list(Empty(), - options: CallOptions(metadata: { - "uuid": window.localStorage["uuid"]!, - "token": window.localStorage["token"]!, - }))) { - envs.add(feature.data.name); + await for (var feature in envStub.list( + ListOptions( + ownerId: OwnerId(uuid: creds.uuid), + token: Token(token: creds.token), + ), + )) { + envs.add(EnvironmentFull( + data: feature.data, + name: feature.name, + )); } } catch (e) { rethrow; diff --git a/lib/components/environment_card.dart b/lib/components/environment_card.dart new file mode 100644 index 0000000..363605f --- /dev/null +++ b/lib/components/environment_card.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class EnvirnomentCard extends StatelessWidget { + final String name; + + const EnvirnomentCard({ + super.key, + required this.name, + }); + + @override + Widget build(BuildContext context) { + return Card( + child: Column( + children: [ + Text(name), + Row( + children: [Text(name)], + ) + ], + ), + ); + } +} diff --git a/lib/components/environments.dart b/lib/components/environments.dart index f73c8a0..9b8a890 100644 --- a/lib/components/environments.dart +++ b/lib/components/environments.dart @@ -1,15 +1,14 @@ -import 'dart:js_interop'; - import 'package:flutter/material.dart'; import 'package:grpc/grpc_web.dart'; +import 'package:softplayer_web/api/grpc/creds.dart'; import 'package:softplayer_web/api/grpc/environments.dart'; +import 'package:softplayer_web/components/environment_card.dart'; class EnvirnomentList extends StatefulWidget { - EnvirnomentList({ + const EnvirnomentList({ super.key, required this.channel, }); - final GrpcWebClientChannel channel; @override State createState() => _EnvirnomentListState(); @@ -21,8 +20,7 @@ class _EnvirnomentListState extends State { @override void initState() { super.initState(); - envGrpc = EnvironmentsGrpc(channel: widget.channel); - envGrpc.init(); + envGrpc = EnvironmentsGrpc(widget.channel); } @override @@ -30,20 +28,24 @@ class _EnvirnomentListState extends State { return Scaffold( body: SafeArea( child: StreamBuilder( - stream: envGrpc.list(), + stream: + envGrpc.list(SoftplayerCredsHelpers().fromLocalStorage()), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return CircularProgressIndicator(); + return const CircularProgressIndicator(); } else if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { - return Text('Error!'); + return const Text('Error!'); } else { - return Column( - children: snapshot.data!.map((e) => Text(e)).toList(), + return GridView.count( + crossAxisCount: 4, + children: snapshot.data! + .map((e) => EnvirnomentCard(name: e.name.name)) + .toList(), ); } } - return Text("err"); + return const Text("err"); }))); } } diff --git a/lib/components/login_form.dart b/lib/components/login_form.dart index f205a9f..3a2eb40 100644 --- a/lib/components/login_form.dart +++ b/lib/components/login_form.dart @@ -8,8 +8,9 @@ class LoginForm extends StatefulWidget { const LoginForm({ super.key, required this.grpcChannel, + required this.notifyParent, }); - + final Function() notifyParent; final GrpcWebClientChannel grpcChannel; @override State createState() => _LoginFormState(); @@ -38,6 +39,7 @@ class _LoginFormState extends State { accountsGrpc.signUp(username, email, password).then((rs) { window.localStorage["token"] = rs.token; window.localStorage["uuid"] = rs.uuid; + widget.notifyParent(); Navigator.of(context, rootNavigator: true).pop(); }).catchError((e) { GrpcError error = e; @@ -66,7 +68,8 @@ class _LoginFormState extends State { accountsGrpc.signIn(username, "", password).then((rs) { window.localStorage["token"] = rs.token; window.localStorage["uuid"] = rs.uuid; - Navigator.of(context, rootNavigator: true).pop(); + widget.notifyParent(); + // Navigator.of(context, rootNavigator: true).pop(); }).catchError((e) { print(e); GrpcError error = e; diff --git a/lib/main.dart b/lib/main.dart index 9d669af..0cb0bea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,7 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Softplayer', - home: TestAlert(channel: channel), + home: RootWidget(channel: channel), theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, @@ -36,10 +36,22 @@ class MyApp extends StatelessWidget { } } -class TestAlert extends StatelessWidget { +class RootWidget extends StatefulWidget { final GrpcWebClientChannel channel; - TestAlert({super.key, required this.channel}); + + RootWidget({super.key, required this.channel}); late final AccountsGrpc accountsGrpc = AccountsGrpc(channel: channel); + + @override + @override + State createState() => _StateRootWidget(); +} + +class _StateRootWidget extends State { + refresh() { + setState(() {}); + } + bool isSignedIn() { return window.localStorage.containsKey("token"); } @@ -47,20 +59,34 @@ class TestAlert extends StatelessWidget { @override Widget build(BuildContext context) { if (!isSignedIn()) { - return Scaffold( - body: Container( + return Scaffold( + body: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/login_background.jpg"), fit: BoxFit.fill, ), ), - child: LoginForm(grpcChannel: channel), + child: LoginForm( + grpcChannel: widget.channel, + notifyParent: refresh, + ), )); } else { return Scaffold( - body: EnvirnomentList(channel: channel), - appBar: AppBar(), + body: EnvirnomentList(channel: widget.channel), + appBar: AppBar( + title: const Text("Softplayer"), + actions: [ + TextButton( + onPressed: () { + window.localStorage.remove("token"); + window.localStorage.remove("uuid"); + refresh(); + }, + child: const Text("sign out")) + ], + ), floatingActionButton: FloatingActionButton( onPressed: () => print("1"), ), diff --git a/pubspec.lock b/pubspec.lock index 5cc5cbf..aa17454 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373" url: "https://pub.dev" source: hosted - version: "3.4.10" + version: "3.5.0" args: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -57,14 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" crypto: dependency: transitive description: @@ -77,18 +69,18 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" dio: dependency: "direct main" description: name: dio - sha256: "0978e9a3e45305a80a7210dbeaf79d6ee8bee33f70c8e542dc654c952070217f" + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" url: "https://pub.dev" source: hosted - version: "5.4.2+1" + version: "5.4.3+1" fake_async: dependency: transitive description: @@ -135,10 +127,10 @@ packages: dependency: transitive description: name: googleapis_auth - sha256: "1401a9e55f9e0f565d3eebb18d990290f53a12d38a5f7f0230b112895778a85b" + sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938 url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.6.0" grpc: dependency: "direct main" description: @@ -171,14 +163,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf - url: "https://pub.dev" - source: hosted - version: "0.7.1" leak_tracker: dependency: transitive description: @@ -243,14 +227,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" - url: "https://pub.dev" - source: hosted - version: "3.7.4" protobuf: dependency: transitive description: @@ -269,7 +245,7 @@ packages: description: path: "." ref: main - resolved-ref: eb11f022be9c1fc5db6e87f3463a1ceb3e04501f + resolved-ref: "6b6325ac4b6c469e41c994d1045af79225a49d38" url: "https://git.badhouseplants.net/softplayer/softplayer-dart-proto.git" source: git version: "1.0.0+1"