From 6d651d139013c0b7d94a5f1b38814774f43e4c09 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 19 May 2023 12:03:13 -0700 Subject: [PATCH 1/4] accounts ui: Pad list of accounts with SafeArea, like the login screens Like we do with the realm-url and username/password screens, which also have this pattern: body: SafeArea( minimum: const EdgeInsets.all(8), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), --- lib/widgets/app.dart | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/widgets/app.dart b/lib/widgets/app.dart index 533d3df8d8..d38d0a2f08 100644 --- a/lib/widgets/app.dart +++ b/lib/widgets/app.dart @@ -58,21 +58,24 @@ class ChooseAccountPage extends StatelessWidget { final globalStore = GlobalStoreWidget.of(context); return Scaffold( appBar: AppBar(title: const Text('Choose account')), - body: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - for (final (:accountId, :account) in globalStore.accountEntries) - _buildAccountItem(context, - accountId: accountId, - title: Text(account.realmUrl.toString()), - subtitle: Text(account.email)), - const SizedBox(height: 12), - ElevatedButton( - onPressed: () => Navigator.push(context, - AddAccountPage.buildRoute()), - child: const Text('Add an account')), - ])))); + body: SafeArea( + minimum: const EdgeInsets.all(8), + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + for (final (:accountId, :account) in globalStore.accountEntries) + _buildAccountItem(context, + accountId: accountId, + title: Text(account.realmUrl.toString()), + subtitle: Text(account.email)), + const SizedBox(height: 12), + ElevatedButton( + onPressed: () => Navigator.push(context, + AddAccountPage.buildRoute()), + child: const Text('Add an account')), + ]))), + )); } } From 1fa51d7e7ad7f013942170c17d5f704d5b71bd21 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 26 May 2023 14:32:33 -0700 Subject: [PATCH 2/4] macos: Update Xcode last-upgrade metadata, and Info.plist These changes happened automatically for me when building for macOS. --- macos/Runner.xcodeproj/project.pbxproj | 2 +- macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | 2 +- macos/Runner/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 2cb009e601..3d51ad192e 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -203,7 +203,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a1238985f1..beb01cfc2d 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ NSMainNibFile MainMenu NSPrincipalClass - FlutterApplication + NSApplication From cc696914e366c3bb3a35f6509e44c15afb44b53e Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 26 May 2023 14:20:56 -0700 Subject: [PATCH 3/4] deps: Add package_info_plus --- ios/Podfile.lock | 6 ++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ macos/Podfile.lock | 6 ++++++ pubspec.lock | 16 ++++++++++++++++ pubspec.yaml | 1 + 5 files changed, 31 insertions(+) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 158d87b032..c9c0c1cae0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -40,6 +40,8 @@ PODS: - Flutter (1.0.0) - image_picker_ios (0.0.1): - Flutter + - package_info_plus (0.4.5): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS @@ -71,6 +73,7 @@ DEPENDENCIES: - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) @@ -94,6 +97,8 @@ EXTERNAL SOURCES: :path: Flutter image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" share_plus: @@ -109,6 +114,7 @@ SPEC CHECKSUMS: file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 + package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 541a0cfaa4..47703f95fa 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import device_info_plus +import package_info_plus import path_provider_foundation import share_plus import sqlite3_flutter_libs func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 84d7c886bc..c6ed50f5c3 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -2,6 +2,8 @@ PODS: - device_info_plus (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) + - package_info_plus (0.0.1): + - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS @@ -26,6 +28,7 @@ PODS: DEPENDENCIES: - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) @@ -39,6 +42,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos FlutterMacOS: :path: Flutter/ephemeral + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin share_plus: @@ -49,6 +54,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 sqlite3: d31b2b69d59bd1b4ab30e5c92eb18fd8e82fa392 diff --git a/pubspec.lock b/pubspec.lock index 2212ef6682..0ea7944a69 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -544,6 +544,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "28386bbe89ab5a7919a47cea99cdd1128e5a6e0bbd7eaafe20440ead84a15de3" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 36485f3395..e2603fb23f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: sqlite3_flutter_libs: ^0.5.13 app_settings: ^4.2.0 image_picker: ^0.8.7+3 + package_info_plus: ^4.0.1 dev_dependencies: flutter_test: From 4d6f561dae488b10f9198b1d3f2549c409875b19 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 25 May 2023 15:58:23 -0700 Subject: [PATCH 4/4] accounts ui: Add "About Zulip" page, reachable from "Choose account" page From this new page, the user can tap a button to show [LicensePage], so resolving #99. Fixes: #99 --- lib/widgets/about_zulip.dart | 56 ++++++++++++++++++++++++++++++++++++ lib/widgets/app.dart | 27 ++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 lib/widgets/about_zulip.dart diff --git a/lib/widgets/about_zulip.dart b/lib/widgets/about_zulip.dart new file mode 100644 index 0000000000..fc15c6b63f --- /dev/null +++ b/lib/widgets/about_zulip.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class AboutZulipPage extends StatefulWidget { + const AboutZulipPage({super.key}); + + static Route buildRoute(BuildContext context) { + return MaterialPageRoute(builder: (context) => const AboutZulipPage()); + } + + @override + State createState() => _AboutZulipPageState(); +} + +class _AboutZulipPageState extends State { + PackageInfo? _packageInfo; + + @override + void initState() { + super.initState(); + (() async { + final result = await PackageInfo.fromPlatform(); + setState(() { + _packageInfo = result; + }); + })(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("About Zulip")), + body: SingleChildScrollView( + child: SafeArea( + minimum: const EdgeInsets.all(8), // ListView pads vertical + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + ListTile( + title: const Text('App version'), + subtitle: Text(_packageInfo?.version ?? '(…)')), + ListTile( + title: const Text('Open-source licenses'), + subtitle: const Text('Tap to view'), + onTap: () { + // TODO(upstream?): This route and its child routes (pushed + // when you tap a package to view its licenses) can't be + // popped on iOS with the swipe-away gesture; you have to + // tap the "Back" button. Debug/fix. + showLicensePage(context: context); + }), + ])))), + )); + } +} diff --git a/lib/widgets/app.dart b/lib/widgets/app.dart index d38d0a2f08..13bb241333 100644 --- a/lib/widgets/app.dart +++ b/lib/widgets/app.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'about_zulip.dart'; import 'compose_box.dart'; import 'login.dart'; import 'message_list.dart'; @@ -57,7 +58,9 @@ class ChooseAccountPage extends StatelessWidget { assert(!PerAccountStoreWidget.debugExistsOf(context)); final globalStore = GlobalStoreWidget.of(context); return Scaffold( - appBar: AppBar(title: const Text('Choose account')), + appBar: AppBar( + title: const Text('Choose account'), + actions: const [ChooseAccountPageOverflowButton()]), body: SafeArea( minimum: const EdgeInsets.all(8), child: Center( @@ -79,6 +82,28 @@ class ChooseAccountPage extends StatelessWidget { } } +enum ChooseAccountPageOverflowMenuItem { aboutZulip } + +class ChooseAccountPageOverflowButton extends StatelessWidget { + const ChooseAccountPageOverflowButton({super.key}); + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + itemBuilder: (BuildContext context) => const [ + PopupMenuItem( + value: ChooseAccountPageOverflowMenuItem.aboutZulip, + child: Text('About Zulip')), + ], + onSelected: (item) { + switch (item) { + case ChooseAccountPageOverflowMenuItem.aboutZulip: + Navigator.push(context, AboutZulipPage.buildRoute(context)); + } + }); + } +} + class HomePage extends StatelessWidget { const HomePage({super.key});