diff --git a/ARCH.md b/ARCH.md new file mode 100644 index 0000000..800300c --- /dev/null +++ b/ARCH.md @@ -0,0 +1,12 @@ +# State Management + +We use a MultiProvider to distribute state to the underlying widgets. Right now there are 2 top +level Providers: FlwtchState (the app) and OpaqueTheme. + +## Theme + +OpaqueTheme extends ChangeProvider. SetLight and SetDark are functions that call notifyListeners() + +ChangeNotiferProvider is used to package OpaqueTheme into a provider which is a top level +provider (as every widget in the app needs to be re-rendered on a theme switch). + diff --git a/android/app/src/main/kotlin/com/example/flutter_app/MainActivity.kt b/android/app/src/main/kotlin/com/example/flutter_app/MainActivity.kt index bdb1330..6c1c92d 100644 --- a/android/app/src/main/kotlin/com/example/flutter_app/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/flutter_app/MainActivity.kt @@ -71,11 +71,11 @@ class MainActivity: FlutterActivity() { Log.i("MainActivity.kt", "got event chan: " + eventbus_chan + " launching corouting...") GlobalScope.launch(Dispatchers.IO) { while(true) { - val jsonEvent = Cwtch.getAppBusEvent() - Log.i("MainActivity.kt", "got appbusEvent: " + jsonEvent) - launch(Dispatchers.Main) { - eventbus_chan.invokeMethod("AppbusEvent", jsonEvent) - } + //val jsonEvent = Cwtch.getAppBusEvent() + // Log.i("MainActivity.kt", "got appbusEvent: " + jsonEvent) + // launch(Dispatchers.Main) { + // //eventbus_chan.invokeMethod("AppbusEvent", jsonEvent) + //} } } @@ -90,8 +90,12 @@ class MainActivity: FlutterActivity() { val pass = (call.argument("pass") as? String) ?: ""; Cwtch.createProfile(nick, pass) } + "LoadProfiles" -> { + val pass = (call.argument("pass") as? String) ?: ""; + Cwtch.loadProfiles(pass) + } "GetProfiles" -> result.success(Cwtch.getProfiles()) - "ACNEvents" -> result.success(Cwtch.acnEvents()) + // "ACNEvents" -> result.success(Cwtch.acnEvents()) "ContactEvents" -> result.success(Cwtch.contactEvents()) "GetContacts" -> { val onion = (call.argument("profile") as? String) ?: ""; diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index d1df79d..850bb13 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -3,6 +3,7 @@ abstract class Cwtch { void SelectProfile(String onion); void CreateProfile(String nick, String pass); + void LoadProfiles(String pass); Future ACNEvents(); Future ContactEvents(); diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index b8356ca..e2953f0 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -18,6 +18,9 @@ typedef VoidFromStringStringFn = void Function(Pointer, int, Pointer typedef access_cwtch_eventbus_function = Void Function(); typedef NextEventFn = void Function(); +typedef string_to_void_function = Void Function(Pointer str, Int32 length); +typedef StringFn = void Function(Pointer dir, int); + typedef get_json_blob_void_function = Pointer Function(); typedef GetJsonBlobVoidFn = Pointer Function(); @@ -80,6 +83,14 @@ class CwtchFfi implements Cwtch { CreateProfile(Utf8.toUtf8(nick), nick.length, Utf8.toUtf8(pass), pass.length); } + // ignore: non_constant_identifier_names + void LoadProfiles(String pass) { + var loadProfileC = library.lookup>("c_LoadProfiles"); + // ignore: non_constant_identifier_names + final LoadProfiles = loadProfileC.asFunction(); + LoadProfiles(Utf8.toUtf8(pass), pass.length); + } + Future ACNEvents() async { var acnEventsC = library.lookup>( "c_ACNEvents"); diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index a0816d6..0570d66 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -32,8 +32,8 @@ class CwtchGomobile implements Cwtch { androidHomeDirectory = getApplicationDocumentsDirectory(); androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir'); - final appbusEventChannel = MethodChannel(appbusEventChannelName); - appbusEventChannel.setMethodCallHandler(this._handleAppbusEvent); + // final appbusEventChannel = MethodChannel(appbusEventChannelName); + // appbusEventChannel.setMethodCallHandler(this._handleAppbusEvent); } Future Start() async { @@ -57,6 +57,10 @@ class CwtchGomobile implements Cwtch { cwtchPlatform.invokeMethod("CreateProfile", {"nick": nick, "pass": pass}); } + void LoadProfiles(String pass) { + cwtchPlatform.invokeMethod("LoadProfiles", {"pass": pass}); + } + Future ACNEvents() { return cwtchPlatform.invokeMethod("ACNEvents"); } @@ -87,5 +91,4 @@ class CwtchGomobile implements Cwtch { return cwtchPlatform.invokeMethod("GetMessage", {"profile" : profile, "contact": handle, "start": start, "end": end}); } - } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index acc5fcc..2ce42d3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -32,17 +32,17 @@ class FlwtchState extends State { // mergenotes: ui stuff ProfileModel selectedProfile; String selectedConversation = ""; - var columns = [1];// default or 'single column' mode + var columns = [1]; // default or 'single column' mode //var columns = [1, 1, 2]; AppModel appStatus; HashMap profiles; - @override initState() { super.initState(); cwtchInit = false; + profiles = new HashMap(); print("FlwtchState.initState()"); @@ -80,32 +80,55 @@ class FlwtchState extends State { }); } + ChangeNotifierProvider getOpaqueProvider() { + return ChangeNotifierProvider(create: (context) => OpaqueTheme(Opaque.dark)); + } + + Provider getFlwtchStateProvider() { + return Provider( + create: (_) => this, + ); + } + @override Widget build(BuildContext context) { appStatus = AppModel(cwtch: cwtch); final newTextTheme = Theme.of(context).textTheme.apply( - bodyColor: Opaque.current().mainTextColor(), - displayColor: Opaque.current().mainTextColor(), - ); + bodyColor: Opaque.current().mainTextColor(), + displayColor: Opaque.current().mainTextColor(), + ); print("FlwtchState.build() cwtchInit: $cwtchInit"); - return Provider( - create: (_) => this, - child: MaterialApp( - title: 'Cwtch', - theme: ThemeData( - visualDensity: VisualDensity.adaptivePlatformDensity, - primarySwatch: Colors.red, - primaryColor: Opaque.current().backgroundMainColor(), - canvasColor: Opaque.current().backgroundPaneColor(), - accentColor: Opaque.current().defaultButtonColor(), - buttonColor: Opaque.current().defaultButtonColor(), - textTheme: newTextTheme, - ), - // from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(), - // from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(), - home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ProfileMgrView()) : SplashView(), - )); + return MultiProvider( + providers: [getFlwtchStateProvider(), getOpaqueProvider()], + builder: (context, widget) { return MaterialApp( + title: 'Cwtch', + theme: ThemeData( + visualDensity: VisualDensity.adaptivePlatformDensity, + primarySwatch: Colors.red, + primaryColor: Provider.of(context) + .current() + .backgroundMainColor(), + canvasColor: Provider.of(context) + .current() + .backgroundPaneColor(), + accentColor: Provider.of(context) + .current() + .defaultButtonColor(), + buttonColor: Provider.of(context) + .current() + .defaultButtonColor(), + textTheme: newTextTheme, + ), + // from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(), + // from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(), + home: cwtchInit == true + ? (columns.length == 3 + ? TripleColumnView() + : ProfileMgrView()) + : SplashView(), + );}, + ); } -} \ No newline at end of file +} diff --git a/lib/opaque.dart b/lib/opaque.dart index fa81b31..0282dcd 100644 --- a/lib/opaque.dart +++ b/lib/opaque.dart @@ -5,6 +5,8 @@ import 'dart:ui'; import 'dart:core'; + +import 'package:flutter/material.dart'; abstract class OpaqueThemeType { static final Color red = Color(0xFFFF0000); Color backgroundMainColor(){return red;} @@ -421,9 +423,12 @@ class Opaque extends OpaqueThemeType { int chatPaneMinSize() { return chatPaneMinSizeBase[p[scale]]; } int doublePaneMinSize() { return sidePaneMinSize() + chatPaneMinSize(); } + static OpaqueThemeType _current; static final OpaqueThemeType dark = CwtchDark(); static final OpaqueThemeType light = CwtchLight(); - static OpaqueThemeType current() { return dark; } + static void setDark() { _current = dark; } + static void setLight() { _current = light; } + static OpaqueThemeType current() { if (_current == null) {setDark();} return _current; } int scale = 2; @@ -507,3 +512,15 @@ int scale = 2; } + + + +/// TODO: Wire into libCwtch saveSettings / updateTheme etc... +class OpaqueTheme extends ChangeNotifier { + OpaqueThemeType theme; + void setDark() { theme = Opaque.dark; notifyListeners();} + void setLight() { theme = Opaque.light; notifyListeners();} + OpaqueThemeType current() { return theme; } + + OpaqueTheme(this.theme); +} \ No newline at end of file diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index d61a981..bdbe5df 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_app/main.dart'; +import 'package:flutter_app/opaque.dart'; +import 'package:provider/provider.dart'; class GlobalSettingsView extends StatefulWidget { @override @@ -35,7 +38,18 @@ class _GlobalSettingsViewState extends State { }, ), Text("Zoom"), - Text("Theme"), + SwitchListTile( + title: const Text('Theme'), + value: Provider.of(context).current() == Opaque.light, + onChanged: (bool value) { + if (value) { + Provider.of(context, listen: false).setLight(); + } else { + Provider.of(context, listen: false).setDark(); + } + }, + secondary: const Icon(Icons.lightbulb_outline), + ), Text("Experiments enabled"), Text("Text magnification reference"), Text("Acknowledgements"), diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index 029e399..a00f77d 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -19,7 +19,6 @@ class ProfileMgrView extends StatefulWidget { } class _ProfileMgrViewState extends State { - HashMap _profiles; final ctrlrPassword = TextEditingController(); @override @@ -30,24 +29,6 @@ class _ProfileMgrViewState extends State { @override Widget build(BuildContext context) { - if (_profiles == null) { - _profiles = new HashMap(); - } - - if (_profiles.length < 1) { - Provider.of(context).cwtch.GetProfiles().then((profilesJson) { - jsonDecode(profilesJson).forEach((profile) { - ProfileModel profile1 = new ProfileModel(); - profile1.onion = profile['onion']; - profile1.nickname = profile['name']; - profile1.creationDate = "4 jan 2020"; - profile1.contacts = new HashMap(); - profile1.imagePath = profile['imagePath']; - - _profiles.putIfAbsent(profile1.onion, () => profile1); - }); - }); - } return Scaffold ( appBar: AppBar( @@ -129,7 +110,12 @@ class _ProfileMgrViewState extends State { ElevatedButton( child: const Text('Unlock'), onPressed: () { - setState(() => _profiles.clear()); + Provider + .of(context, listen: false) + .cwtch + .LoadProfiles(ctrlrPassword.value.text); + Provider.of(context, listen: false) + .loadProfiles(); Navigator.pop(context); }, ),