diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt index 93c740a..c82d2e4 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -136,7 +136,7 @@ class MainActivity: FlutterActivity() { super.onResume() Log.i("MainActivity.kt", "onResume") if (myReceiver == null) { - Log.i("MainActivity.kt", "onResume registering localbroadcastreceiver") + Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder") val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS) val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS") myReceiver = MyBroadcastReceiver(mc) @@ -163,7 +163,7 @@ class MainActivity: FlutterActivity() { override fun onDestroy() { super.onDestroy() - Log.i("MainActivity.kt", "onDestroy") + Log.i("MainActivity.kt", "onDestroy - cancelling all WORKER_TAG and pruning old work") WorkManager.getInstance(this).cancelAllWorkByTag(WORKER_TAG) WorkManager.getInstance(this).pruneWork() } @@ -188,6 +188,8 @@ class MainActivity: FlutterActivity() { val Data = this.optString("Data") } + // MainActivity.MyBroadcastReceiver receives events from the Cwtch service via im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS Android local broadcast intents + // then it forwards them to the flutter ui engine using the CWTCH_EVENTBUS methodchannel class MyBroadcastReceiver(mc: MethodChannel) : BroadcastReceiver() { val eventBus: MethodChannel = mc @@ -198,5 +200,4 @@ class MainActivity: FlutterActivity() { eventBus.invokeMethod(evtType, evtData) } } - } diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index bb564e6..567202b 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -97,7 +97,9 @@ class CwtchNotifier { break; case "NewMessageFromPeer": notificationManager.notify("New Message From Peer!"); - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++; + if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++; + } profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now()); break; @@ -122,7 +124,9 @@ class CwtchNotifier { case "NewMessageFromGroup": if (data["ProfileOnion"] != data["RemotePeer"]) { //not from me - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; + if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; + } profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now()); } else { diff --git a/lib/main.dart b/lib/main.dart index 6dd6b20..bb9fd44 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,10 +45,6 @@ class Flwtch extends StatefulWidget { class FlwtchState extends State { final TextStyle biggerFont = const TextStyle(fontSize: 18); late Cwtch cwtch; - late ProfileInfoState selectedProfile; - String selectedConversation = ""; - var columns = [1]; // default or 'single column' mode - //var columns = [1, 1, 2]; late ProfileListState profs; final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler'); final MethodChannel shutdownMethodChannel = MethodChannel('im.cwtch.flwtch/shutdown'); @@ -108,7 +104,7 @@ class FlwtchState extends State { supportedLocales: AppLocalizations.supportedLocales, title: 'Cwtch', theme: mkThemeData(settings), - home: appState.cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(), + home: appState.cwtchInit == true ? ShiftRightFixer(child: ProfileMgrView()) : SplashView(), ), ); }, @@ -128,24 +124,38 @@ class FlwtchState extends State { }); } + // Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt + // coder beware: args["RemotePeer"] is actually a handle, and could be eg a groupID Future _externalNotificationClicked(MethodCall call) async { var args = jsonDecode(call.arguments); var profile = profs.getProfile(args["ProfileOnion"])!; var contact = profile.contactList.getContact(args["RemotePeer"])!; contact.unreadMessages = 0; - navKey.currentState?.push( - MaterialPageRoute( - builder: (BuildContext builderContext) { - return MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: profile), - ChangeNotifierProvider.value(value: contact), - ], - builder: (context, child) => MessageView(), - ); - }, - ), - ); + + // single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation + var isLandscape = Provider.of(navKey.currentContext!, listen: false).isLandscape(navKey.currentContext!); + if (Provider.of(navKey.currentContext!, listen: false).uiColumns(isLandscape).length == 1) { + if (navKey.currentContext?.findAncestorWidgetOfExactType() != null) { + print("messageview already open; popping before pushing replacement"); + navKey.currentState?.pop(); + } + navKey.currentState?.push( + MaterialPageRoute( + builder: (BuildContext builderContext) { + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: profile), + ChangeNotifierProvider.value(value: contact), + ], + builder: (context, child) => MessageView(), + ); + }, + ), + ); + } else { //dual pane + Provider.of(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"]; + Provider.of(navKey.currentContext!, listen: false).selectedConversation = args["RemotePeer"]; + } } @override diff --git a/lib/main_test.dart b/lib/main_test.dart index 1f6b96e..3bdd05c 100644 --- a/lib/main_test.dart +++ b/lib/main_test.dart @@ -13,7 +13,7 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:glob/glob.dart'; -import 'package:glob/list_local_fs.dart'; +//import 'package:glob/list_local_fs.dart'; var globalSettings = Settings(Locale("en", ''), OpaqueDark()); var globalErrorHandler = ErrorHandler(); diff --git a/lib/model.dart b/lib/model.dart index a6ffb47..60aa478 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -64,6 +64,8 @@ class ProfileListState extends ChangeNotifier { class AppState extends ChangeNotifier { bool cwtchInit = false; String appError = ""; + String? _selectedProfile; + String? _selectedConversation; void SetCwtchInit() { cwtchInit = true; @@ -74,6 +76,20 @@ class AppState extends ChangeNotifier { appError = error; notifyListeners(); } + + String? get selectedProfile => _selectedProfile; + set selectedProfile(String? newVal) { + this._selectedProfile = newVal; + notifyListeners(); + } + + String? get selectedConversation => _selectedConversation; + set selectedConversation(String? newVal) { + this._selectedConversation = newVal; + notifyListeners(); + } + + bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height; } class ContactListState extends ChangeNotifier { @@ -491,6 +507,8 @@ class MessageState extends ChangeNotifier { } set loaded(bool newVal) { + // quickly-arriving messages get discarded before loading sometimes + if (!hasListeners) return; this._loaded = newVal; notifyListeners(); } diff --git a/lib/settings.dart b/lib/settings.dart index 137fcb7..d63333d 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -9,6 +9,13 @@ import 'opaque.dart'; const TapirGroupsExperiment = "tapir-groups-experiment"; +enum DualpaneMode { + Single, + Dual1to2, + Dual1to4, + CopyPortrait, +} + /// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments. /// We also provide access to the version information here as it is also accessed from the /// Settings Pane. @@ -19,8 +26,8 @@ class Settings extends ChangeNotifier { // explicitly set experiments to false until told otherwise... bool experimentsEnabled = false; HashMap experiments = HashMap.identity(); - String _uiColumnModePortrait = "Single"; - String _uiColumnModeLandscape = "Same"; + DualpaneMode _uiColumnModePortrait = DualpaneMode.Single; + DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait; bool blockUnknownConnections = false; @@ -75,6 +82,10 @@ class Settings extends ChangeNotifier { // Set the internal experiments map. Casting from the Map that we get from JSON experiments = new HashMap.from(settings["Experiments"]); + // single pane vs dual pane preferences + _uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]); + _uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]); + // Push the experimental settings to Consumers of Settings notifyListeners(); } @@ -136,18 +147,55 @@ class Settings extends ChangeNotifier { notifyListeners(); } - String get uiColumnModePortrait => _uiColumnModePortrait; - set uiColumnModePortrait(String newval) { + DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait; + set uiColumnModePortrait(DualpaneMode newval) { this._uiColumnModePortrait = newval; notifyListeners(); } - String get uiColumnModeLandscape => _uiColumnModeLandscape; - set uiColumnModeLandscape(String newval) { + DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape; + set uiColumnModeLandscape(DualpaneMode newval) { this._uiColumnModeLandscape = newval; notifyListeners(); } + List uiColumns(bool isLandscape) { + var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape; + switch(m) { + case DualpaneMode.Single: return [1]; + case DualpaneMode.Dual1to2: return [1, 2]; + case DualpaneMode.Dual1to4: return [1, 4]; + } + print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape"); + return [1]; + } + + static List uiColumnModeOptions(bool isLandscape) { + if (isLandscape) return [DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4,]; + else return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4]; + } + + static DualpaneMode uiColumnModeFromString(String m) { + switch(m) { + case "DualpaneMode.Single": return DualpaneMode.Single; + case "DualpaneMode.Dual1to2": return DualpaneMode.Dual1to2; + case "DualpaneMode.Dual1to4": return DualpaneMode.Dual1to4; + case "DualpaneMode.CopyPortrait": return DualpaneMode.CopyPortrait; + } + print("Error: ui requested translation of column mode [$m] which doesn't exist"); + return DualpaneMode.Single; + } + + static String uiColumnModeToString(DualpaneMode m) { + // todo: translate + switch(m) { + case DualpaneMode.Single: return "Single"; + case DualpaneMode.Dual1to2: return "Double (1:2)"; + case DualpaneMode.Dual1to4: return "Double (1:4)"; + case DualpaneMode.CopyPortrait: return "Same as portrait mode setting"; + } + } + /// Construct a default settings object. Settings(this.locale, this.theme); @@ -164,7 +212,9 @@ class Settings extends ChangeNotifier { "ExperimentsEnabled": this.experimentsEnabled, "Experiments": experiments, "StateRootPane": 0, - "FirstTime": false + "FirstTime": false, + "UIColumnModePortrait": uiColumnModePortrait.toString(), + "UIColumnModeLandscape": uiColumnModeLandscape.toString(), }; } } diff --git a/lib/views/doublecolview.dart b/lib/views/doublecolview.dart index 2b5ba8e..2737755 100644 --- a/lib/views/doublecolview.dart +++ b/lib/views/doublecolview.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../main.dart'; import '../model.dart'; +import '../settings.dart'; import 'contactsview.dart'; import 'messageview.dart'; @@ -14,24 +15,25 @@ class DoubleColumnView extends StatefulWidget { class _DoubleColumnViewState extends State { @override Widget build(BuildContext context) { - var flwtch = Provider.of(context); + var flwtch = Provider.of(context); + var cols = Provider.of(context).uiColumns(true); return Flex( direction: Axis.horizontal, children: [ Flexible( - flex: flwtch.columns[0], + flex: cols[0], child: ContactsView( key: widget.key, ), ), Flexible( - flex: flwtch.columns[1], - child: flwtch.selectedConversation == "" - ? Center(child: Text(AppLocalizations.of(context)!.addContactFirst)) + flex: cols[1], + child: flwtch.selectedConversation == null + ? Card(child:Center(child: Text(AppLocalizations.of(context)!.addContactFirst))) : //dev MultiProvider(providers: [ ChangeNotifierProvider.value(value: Provider.of(context)), - ChangeNotifierProvider.value(value: Provider.of(context).contactList.getContact(flwtch.selectedConversation)!), + ChangeNotifierProvider.value(value: Provider.of(context).contactList.getContact(flwtch.selectedConversation!)!), ], child: Container(child: MessageView())), ), ], diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index ee98396..9380965 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -80,24 +80,33 @@ class _GlobalSettingsViewState extends State { secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor()), ), ListTile( - title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())), + title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Portrait Mode", style: TextStyle(color: settings.current().mainTextColor())), leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()), trailing: DropdownButton( - value: "Single", + value: settings.uiColumnModePortrait.toString(), onChanged: (String? newValue) { - if (newValue == "Double (1:2)") { - Provider.of(context).columns = [1, 2]; - } else if (newValue == "Double (1:4)") { - Provider.of(context).columns = [1, 4]; - } else { - Provider.of(context).columns = [1]; - } + settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!); + saveSettings(context); }, - // TODO: Only allow in landscape? - items: (Platform.isAndroid ? ["Single"] : ["Single", "Double (1:2)", "Double (1:4)"]).map>((String value) { + items: Settings.uiColumnModeOptions(false).map>((DualpaneMode value) { return DropdownMenuItem( - value: value, - child: Text(value), + value: value.toString(), + child: Text(Settings.uiColumnModeToString(value)), + ); + }).toList())), + ListTile( + title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Landscape Mode", style: TextStyle(color: settings.current().mainTextColor())), + leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()), + trailing: DropdownButton( + value: settings.uiColumnModeLandscape.toString(), + onChanged: (String? newValue) { + settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!); + saveSettings(context); + }, + items: Settings.uiColumnModeOptions(true).map>((DualpaneMode value) { + return DropdownMenuItem( + value: value.toString(), + child: Text(Settings.uiColumnModeToString(value)), ); }).toList())), SwitchListTile( diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index c1498e1..56497a5 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -42,10 +42,13 @@ class _MessageViewState extends State { @override Widget build(BuildContext context) { + var appState = Provider.of(context); return WillPopScope( onWillPop: _onWillPop, child: Scaffold( appBar: AppBar( + // setting leading to null makes it do the default behaviour; container() hides it + leading: Provider.of(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null, title: Text(Provider.of(context).nickname), actions: [ //IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings), diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index ce2d23f..be700df 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -42,41 +42,45 @@ class _ProfileMgrViewState extends State { // Prevents Android back button from closing the app on the profile manager screen // (which would shutdown connections and all kinds of other expensive to generate things) // TODO pop up a dialogue regarding closing the app? - builder: (context, settings, child) => WillPopScope( - onWillPop: () async { - _showShutdown(); - return closeApp; - }, - child: Scaffold( - backgroundColor: settings.theme.backgroundMainColor(), - appBar: AppBar( - title: Row(children: [ - Image( - image: AssetImage("assets/core/knott-white.png"), - filterQuality: FilterQuality.medium, - isAntiAlias: true, - width: 32, - height: 32, - colorBlendMode: BlendMode.dstIn, - color: Provider.of(context).theme.backgroundHilightElementColor(), + builder: (context, settings, child) => + WillPopScope( + onWillPop: () async { + _showShutdown(); + return closeApp; + }, + child: Scaffold( + backgroundColor: settings.theme.backgroundMainColor(), + appBar: AppBar( + title: Row(children: [ + Image( + image: AssetImage("assets/core/knott-white.png"), + filterQuality: FilterQuality.medium, + isAntiAlias: true, + width: 32, + height: 32, + colorBlendMode: BlendMode.dstIn, + color: Provider + .of(context) + .theme + .backgroundHilightElementColor(), + ), + SizedBox( + width: 10, + ), + Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor()))) + ]), + actions: getActions(), ), - SizedBox( - width: 10, + floatingActionButton: FloatingActionButton( + onPressed: _pushAddEditProfile, + tooltip: AppLocalizations.of(context)!.addNewProfileBtn, + child: Icon( + Icons.add, + semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn, + ), ), - Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor()))) - ]), - actions: getActions(), - ), - floatingActionButton: FloatingActionButton( - onPressed: _pushAddEditProfile, - tooltip: AppLocalizations.of(context)!.addNewProfileBtn, - child: Icon( - Icons.add, - semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn, - ), - ), - body: _buildProfileManager(), - )), + body: _buildProfileManager(), + )), ); } diff --git a/lib/views/triplecolview.dart b/lib/views/triplecolview.dart index a4f1e44..4156c73 100644 --- a/lib/views/triplecolview.dart +++ b/lib/views/triplecolview.dart @@ -3,9 +3,12 @@ import 'package:cwtch/views/profilemgrview.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../main.dart'; +import '../model.dart'; +import '../settings.dart'; import 'contactsview.dart'; import 'messageview.dart'; +// currently unused but maybe one day? class TripleColumnView extends StatefulWidget { @override _TripleColumnViewState createState() => _TripleColumnViewState(); @@ -14,19 +17,22 @@ class TripleColumnView extends StatefulWidget { class _TripleColumnViewState extends State { @override Widget build(BuildContext context) { - var flwtch = Provider.of(context); + var appState = Provider.of(context); + var settings = Provider.of(context); + var columns = settings.uiColumns(appState.isLandscape(context)); + return Flex(direction: Axis.horizontal, children: [ Flexible( - flex: flwtch.columns[0], + flex: columns[0], child: ProfileMgrView(), ), Flexible( - flex: flwtch.columns[1], - child: flwtch.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev + flex: columns[1], + child: appState.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev ), Flexible( - flex: flwtch.columns[2], - child: flwtch.selectedConversation == "" + flex: columns[2], + child: appState.selectedConversation == null ? Center(child: Text(AppLocalizations.of(context)!.addContactFirst)) : //dev Container(child: MessageView()), diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index b3e5bbb..1a35fe5 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -80,17 +80,19 @@ class _ContactRowState extends State { ]), onTap: () { setState(() { - var flwtch = Provider.of(context, listen: false); - flwtch.setState(() => flwtch.selectedConversation = contact.onion); - // case 2/3 handled by Double/TripleColumnView respectively - if (flwtch.columns.length == 1) _pushMessageView(contact.onion); + // requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts + Provider.of(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0; + // triggers update in Double/TripleColumnView + Provider.of(context, listen: false).selectedConversation = contact.onion; + // if in singlepane mode, push to the stack + var isLandscape = Provider.of(context, listen: false).isLandscape(context); + if (Provider.of(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion); }); }, )); } void _pushMessageView(String handle) { - Provider.of(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0; var profileOnion = Provider.of(context, listen: false).onion; Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index 36345c3..0d05070 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -30,29 +30,43 @@ class _ProfileRowState extends State { padding: const EdgeInsets.all(2.0), //border size child: ProfileImage( badgeCount: 0, - badgeColor: Provider.of(context).theme.portraitProfileBadgeColor(), - badgeTextColor: Provider.of(context).theme.portraitProfileBadgeTextColor(), + badgeColor: Provider + .of(context) + .theme + .portraitProfileBadgeColor(), + badgeTextColor: Provider + .of(context) + .theme + .portraitProfileBadgeTextColor(), diameter: 64.0, imagePath: profile.imagePath, - border: profile.isOnline ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor())), + border: profile.isOnline ? Provider + .of(context) + .theme + .portraitOnlineBorderColor() : Provider + .of(context) + .theme + .portraitOfflineBorderColor())), Expanded( child: Column( - children: [ - Text( - profile.nickname, - semanticsLabel: profile.nickname, - style: Provider.of(context).biggerFont, - softWrap: true, - overflow: TextOverflow.ellipsis, - ), - ExcludeSemantics( - child: Text( - profile.onion, - softWrap: true, - overflow: TextOverflow.ellipsis, - )) - ], - )), + children: [ + Text( + profile.nickname, + semanticsLabel: profile.nickname, + style: Provider + .of(context) + .biggerFont, + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ExcludeSemantics( + child: Text( + profile.onion, + softWrap: true, + overflow: TextOverflow.ellipsis, + )) + ], + )), IconButton( enableFeedback: true, tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname, @@ -65,37 +79,35 @@ class _ProfileRowState extends State { ), onTap: () { setState(() { - var flwtch = Provider.of(context, listen: false); - flwtch.setState(() { - flwtch.selectedProfile = profile; - flwtch.selectedConversation = ""; - }); + var appState = Provider.of(context, listen: false); + appState.selectedProfile = profile.onion; + appState.selectedConversation = null; - switch (flwtch.columns.length) { - case 1: - _pushContactList(profile, false); - break; - case 2: - _pushContactList(profile, true); - break; - } // case 3: handled by TripleColumnView + _pushContactList(profile, appState.isLandscape(context));//orientation == Orientation.landscape); }); }, )); } - void _pushContactList(ProfileInfoState profile, bool includeDoublePane) { + void _pushContactList(ProfileInfoState profile, bool isLandscape) { Navigator.of(context).push( MaterialPageRoute( settings: RouteSettings(name: "conversations"), builder: (BuildContext buildcontext) { - return MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: profile), - ChangeNotifierProvider.value(value: profile.contactList), - ], - builder: (context, widget) => includeDoublePane ? DoubleColumnView() : ContactsView(), - ); + return OrientationBuilder( + builder: (orientationBuilderContext, orientation) { + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: profile), + ChangeNotifierProvider.value(value: profile.contactList), + ], + builder: (innercontext, widget) { + var appState = Provider.of(context); + var settings = Provider.of(context); + return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView(); + } + ); + }); }, ), );