diff --git a/lib/controller/stillbox.dart b/lib/controller/stillbox.dart index 3a17096..1371b24 100644 --- a/lib/controller/stillbox.dart +++ b/lib/controller/stillbox.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:web_socket_channel/io.dart'; +import 'package:http/http.dart' as http; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../pb/stillbox.pb.dart'; import 'play.dart'; @@ -16,6 +17,7 @@ class Stillbox extends ChangeNotifier { Filter? currentFilter; Call? _currentCall; Map headers = {}; + Uri? baseUri = Uri.base; set state(LiveState newState) { channel.sink.add(Live(state: newState, filter: currentFilter)); @@ -34,18 +36,56 @@ class Stillbox extends ChangeNotifier { } Stillbox() { - String socketUrl = 'ws://xenon:3050/ws'; - Uri baseUri = Uri.base; - if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { - final port = (baseUri.hasPort ? ':${baseUri.port}' : ''); + setUris(); + } + + void setUris() { + if (baseUri != null && (baseUri!.scheme == 'http' || baseUri!.scheme == 'https')) { + String socketUrl; + final port = (baseUri!.hasPort ? ':${baseUri!.port}' : ''); socketUrl = - '${baseUri.scheme == 'http' ? 'ws' : 'wss'}://${baseUri.host}$port/ws'; + '${baseUri!.scheme == 'http' ? 'ws' : 'wss'}://${baseUri!.host}$port/ws'; + _wsUri = Uri.parse(socketUrl); + } else { + baseUri = null; } - _wsUri = Uri.parse(socketUrl); + } + + void updateCookie(http.Response response) { + String? rawCookie = response.headers['set-cookie']; + if (rawCookie != null) { + int index = rawCookie.indexOf(';'); + headers['cookie'] = + (index == -1) ? rawCookie : rawCookie.substring(0, index); + } + } + + Future doLogin(String uri, String username, String password) async { + if(baseUri == null) { + baseUri = Uri.parse(uri); + setUris(); + } + Uri loginUri = Uri.parse(baseUri!.toString() + '/login'); + final form = {}; + form['username'] = username; + form['password'] = password; + http.Response response = await http.post( + loginUri, + body: form, + ); + if(response.statusCode == 200) { + updateCookie(response); + await storage.write(key: 'token', value: headers['cookie']); + await connect(); + return true; + } + return false; } Future connect() async { - await setBearer(); + if (connected = true) { + return; + } channel = IOWebSocketChannel.connect(_wsUri, headers: headers); channel.stream.listen((event) => _handleData(event), onDone: () { connected = false; diff --git a/lib/main.dart b/lib/main.dart index 61e88c1..93f9d9b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -50,7 +50,9 @@ class CallsHomeState extends State { Future loadData() async { // Ensure the navigation happens in the context of this widget's subtree try { - await Provider.of(context, listen: false).connect(); + final sb = Provider.of(context, listen: false); + await sb.setBearer(); + await sb.connect(); } catch (e) { if (mounted) { Navigator.pushReplacement( diff --git a/lib/views/login.dart b/lib/views/login.dart index bf8eb79..75da807 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -1,4 +1,7 @@ +import 'package:calls/main.dart'; import 'package:flutter/material.dart'; +import '../controller/stillbox.dart'; +import 'package:provider/provider.dart'; class Login extends StatefulWidget { const Login({super.key}); @@ -9,6 +12,7 @@ class Login extends StatefulWidget { class _LoginState extends State { final _formKey = GlobalKey(); + TextEditingController uriController = TextEditingController(); TextEditingController userController = TextEditingController(); TextEditingController passwordController = TextEditingController(); @@ -24,6 +28,31 @@ class _LoginState extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), child: Column(children: [ + Builder(builder: (context) { + if (!(Uri.base.scheme == 'http' || + Uri.base.scheme == 'https')) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 10), + child: TextFormField( + controller: uriController, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: "Server URL"), + validator: (value) { + if (value != null) { + return Uri.parse(value).isAbsolute + ? null + : 'Please enter a valid URL.'; + } else { + return 'Please enter a valid URL.'; + } + }, + )); + } + + return Container(); + }), Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10), @@ -60,7 +89,29 @@ class _LoginState extends State { child: ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { - // TODO do login here + Provider.of(context, listen: false) + .doLogin( + uriController.text, + userController.text, + passwordController.text) + .then((result) { + if (result == true && mounted) { + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation, + secondaryAnimation) => + const CallsHome(), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + return child; + }, + transitionDuration: + const Duration(milliseconds: 0), + ), + ); + } + }); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please login.')), diff --git a/macos/Podfile.lock b/macos/Podfile.lock index a83eba5..085d79b 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,20 +1,32 @@ PODS: + - audioplayers_darwin (0.0.1): + - FlutterMacOS + - flutter_secure_storage_macos (6.1.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS DEPENDENCIES: + - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) EXTERNAL SOURCES: + audioplayers_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin SPEC CHECKSUMS: + audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6ed6bd6..cb09b21 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -195,7 +195,6 @@ 2C35E72338A581BA4B223314 /* Pods-RunnerTests.release.xcconfig */, 308BAD42558871EC233F8F66 /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -574,6 +573,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -706,6 +706,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -726,6 +727,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a3..d769771 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,9 @@ com.apple.security.network.server + com.apple.security.network.client + + keychain-access-groups + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a..225aa48 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,9 @@ com.apple.security.app-sandbox + com.apple.security.network.client + + keychain-access-groups + diff --git a/pubspec.lock b/pubspec.lock index e37280f..2ea478d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -225,7 +225,7 @@ packages: source: hosted version: "6.2.1" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 diff --git a/pubspec.yaml b/pubspec.yaml index a3c57ed..4fb0728 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: provider: ^6.1.2 audioplayers: ^5.2.1 flutter_secure_storage: ^9.2.2 + http: ^1.2.2 dev_dependencies: flutter_test: