From ab3ba95c19588d4ae3cc95c8c24304488c54dff5 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sat, 18 Nov 2023 22:42:20 -0500 Subject: [PATCH 01/17] First cut at updated gradle config for codemagic code signing --- android/app/build.gradle | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6c91c85..cdcb0ad 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -18,7 +18,13 @@ if (flutterVersionCode == null) { def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = '0.8.0' +} + +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } apply plugin: 'com.android.application' @@ -53,11 +59,27 @@ android { versionName flutterVersionName } + signingConfigs { + release { + if (System.getenv()["CI"]) { // CI=true is exported by Codemagic + storeFile file(System.getenv()["CM_KEYSTORE_PATH"]) + storePassword System.getenv()["CM_KEYSTORE_PASSWORD"] + keyAlias System.getenv()["CM_KEY_ALIAS"] + keyPassword System.getenv()["CM_KEY_PASSWORD"] + } else { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } } } From 2979f06e1c1cff4ae0a1910fcd4cfd04ad3ea1c2 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Mon, 27 Nov 2023 07:41:57 -0500 Subject: [PATCH 02/17] Bump version number --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index cdcb0ad..799e4fd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,7 +13,7 @@ if (flutterRoot == null) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = '2' } def flutterVersionName = localProperties.getProperty('flutter.versionName') From cde05afdb189659f09409f6cec5b9533f02613c4 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 15:03:34 -0500 Subject: [PATCH 03/17] Bump up versions in gradle --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 799e4fd..53178d0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,12 +13,12 @@ if (flutterRoot == null) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '2' + flutterVersionCode = '3' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '0.8.0' + flutterVersionName = '0.9.0' } def keystoreProperties = new Properties() From f1c0a51ac9adba4deea29f7bc43fd2cd0884e014 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 15:03:48 -0500 Subject: [PATCH 04/17] Bump up versions in gradle --- pubspec.lock | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index c93acf4..a5d46cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" color_blindness: dependency: "direct main" description: @@ -705,10 +705,10 @@ packages: dependency: transitive description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -761,18 +761,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" media_kit: dependency: "direct main" description: @@ -849,10 +849,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -1239,10 +1239,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -1279,18 +1279,18 @@ packages: dependency: "direct main" description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1335,10 +1335,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" time_machine: dependency: "direct main" description: @@ -1555,6 +1555,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1612,5 +1620,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.10.0" From 3bfe635c6ca950bf7a71658fbdc05925ee7e64d3 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:16:50 -0500 Subject: [PATCH 05/17] Initial try of Android codemagic config --- codemagic.yaml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 codemagic.yaml diff --git a/codemagic.yaml b/codemagic.yaml new file mode 100644 index 0000000..909f98a --- /dev/null +++ b/codemagic.yaml @@ -0,0 +1,49 @@ +workflows: + android-workflow: + name: Android Workflow + instance_type: mac_mini_m1 + max_build_duration: 120 + environment: + android_signing: + - codemagickeystore + groups: + - google_play # <-- (Includes GCLOUD_SERVICE_ACCOUNT_CREDENTIALS) + vars: + PACKAGE_NAME: "relatica.myportal.social" # <-- Put your package name here + GOOGLE_PLAY_TRACK: "internal" + flutter: stable + scripts: + - name: Set up local.properties + script: | + echo "flutter.sdk=$HOME/programs/flutter" > "$CM_BUILD_DIR/android/local.properties" + - name: Get Flutter packages + script: | + flutter packages pub get + - name: Flutter analyze + script: | + flutter analyze + - name: Flutter unit tests + script: | + flutter test + ignore_failure: true + - name: Build AAB with Flutter + script: | + BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) + flutter build appbundle --release \ + --build-name=0.9.$BUILD_NUMBER \ + --build-number=$BUILD_NUMBER + artifacts: + - build/**/outputs/**/*.aab + - build/**/outputs/**/mapping.txt + - flutter_drive.log + publishing: + email: + recipients: + - codemagic@myportal.social + notify: + success: true + failure: false + google_play: + credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS + track: $GOOGLE_PLAY_TRACK + submit_as_draft: true From 4644657c1cec77b480a1f867d186489b8bac205f Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:22:51 -0500 Subject: [PATCH 06/17] ignore flutter analyzie failure in codemagic flow --- codemagic.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/codemagic.yaml b/codemagic.yaml index 909f98a..ef0058c 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -22,6 +22,7 @@ workflows: - name: Flutter analyze script: | flutter analyze + ignore_failure: true - name: Flutter unit tests script: | flutter test From f30b577236b9b434448218b0fede7b62c56cca12 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:32:32 -0500 Subject: [PATCH 07/17] Use project build number --- codemagic.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codemagic.yaml b/codemagic.yaml index ef0058c..77e2b06 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -31,8 +31,8 @@ workflows: script: | BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) flutter build appbundle --release \ - --build-name=0.9.$BUILD_NUMBER \ - --build-number=$BUILD_NUMBER + --build-name=0.9.0 \ + --build-number=($PROJECT_BUILD_NUMBER) artifacts: - build/**/outputs/**/*.aab - build/**/outputs/**/mapping.txt From e190157b95587e82a56777d4b490c92a14c5cc2e Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:39:09 -0500 Subject: [PATCH 08/17] Ignore flutter analyze --- codemagic.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/codemagic.yaml b/codemagic.yaml index 77e2b06..9e6ff13 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -19,10 +19,10 @@ workflows: - name: Get Flutter packages script: | flutter packages pub get - - name: Flutter analyze - script: | - flutter analyze - ignore_failure: true + # - name: Flutter analyze + # script: | + # flutter analyze + # ignore_failure: true - name: Flutter unit tests script: | flutter test @@ -32,7 +32,7 @@ workflows: BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) flutter build appbundle --release \ --build-name=0.9.0 \ - --build-number=($PROJECT_BUILD_NUMBER) + --build-number=$PROJECT_BUILD_NUMBER artifacts: - build/**/outputs/**/*.aab - build/**/outputs/**/mapping.txt From b41d3b4fbf9dad381b280573767d50abaf673ef3 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:40:00 -0500 Subject: [PATCH 09/17] Go back to build number --- codemagic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemagic.yaml b/codemagic.yaml index 9e6ff13..f5958b8 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -32,7 +32,7 @@ workflows: BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) flutter build appbundle --release \ --build-name=0.9.0 \ - --build-number=$PROJECT_BUILD_NUMBER + --build-number=$BUILD_NUMBER artifacts: - build/**/outputs/**/*.aab - build/**/outputs/**/mapping.txt From cc72e58ea6993733d0a780e39588389e58730426 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:48:33 -0500 Subject: [PATCH 10/17] Add offset to build number --- codemagic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemagic.yaml b/codemagic.yaml index f5958b8..9b3019c 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -32,7 +32,7 @@ workflows: BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) flutter build appbundle --release \ --build-name=0.9.0 \ - --build-number=$BUILD_NUMBER + --build-number=($BUILD_NUMBER+4) artifacts: - build/**/outputs/**/*.aab - build/**/outputs/**/mapping.txt From d6b0c9813c053c9f289e0be5fede16862a40e3e2 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 16:56:32 -0500 Subject: [PATCH 11/17] Fix order of package name --- codemagic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemagic.yaml b/codemagic.yaml index 9b3019c..0906810 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -9,7 +9,7 @@ workflows: groups: - google_play # <-- (Includes GCLOUD_SERVICE_ACCOUNT_CREDENTIALS) vars: - PACKAGE_NAME: "relatica.myportal.social" # <-- Put your package name here + PACKAGE_NAME: "social.myportal.relatica" # <-- Put your package name here GOOGLE_PLAY_TRACK: "internal" flutter: stable scripts: From 8ddcab02b11f8a97af5fd0249356ded5827f2658 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 30 Nov 2023 17:06:38 -0500 Subject: [PATCH 12/17] Back to project build number --- codemagic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemagic.yaml b/codemagic.yaml index 0906810..6c9b267 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -32,7 +32,7 @@ workflows: BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1)) flutter build appbundle --release \ --build-name=0.9.0 \ - --build-number=($BUILD_NUMBER+4) + --build-number=$PROJECT_BUILD_NUMBER artifacts: - build/**/outputs/**/*.aab - build/**/outputs/**/mapping.txt From 052fa15eb8fae576a2b0e4b58adb533ee0313b27 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 28 Aug 2024 11:23:44 -0400 Subject: [PATCH 13/17] Rename CircleData to TimelineGroupingListData since includes Circles, Groups, and Channels --- lib/data/interfaces/circles_repo_intf.dart | 22 ++++++---- lib/data/memory/memory_circles_repo.dart | 26 ++++++----- lib/friendica_client/friendica_client.dart | 43 ++++++++++-------- lib/models/circle_data.dart | 22 ---------- lib/models/timeline_grouping_list_data.dart | 25 +++++++++++ lib/screens/circle_add_users_screen.dart | 4 +- lib/screens/circle_editor_screen.dart | 4 +- lib/screens/editor.dart | 18 ++++---- lib/screens/user_profile_screen.dart | 4 +- .../circle_data_mastodon_extensions.dart | 8 ---- .../mastodon/timeline_grouping_list_data.dart | 10 +++++ .../visibility_mastodon_extensions.dart | 5 +-- lib/services/connections_manager.dart | 44 +++++++++++-------- lib/services/timeline_manager.dart | 8 ++-- 14 files changed, 136 insertions(+), 107 deletions(-) delete mode 100644 lib/models/circle_data.dart create mode 100644 lib/models/timeline_grouping_list_data.dart delete mode 100644 lib/serializers/mastodon/circle_data_mastodon_extensions.dart create mode 100644 lib/serializers/mastodon/timeline_grouping_list_data.dart diff --git a/lib/data/interfaces/circles_repo_intf.dart b/lib/data/interfaces/circles_repo_intf.dart index b64f618..86330e3 100644 --- a/lib/data/interfaces/circles_repo_intf.dart +++ b/lib/data/interfaces/circles_repo_intf.dart @@ -1,27 +1,31 @@ import 'package:result_monad/result_monad.dart'; -import '../../models/circle_data.dart'; import '../../models/connection.dart'; import '../../models/exec_error.dart'; +import '../../models/timeline_grouping_list_data.dart'; abstract class ICirclesRepo { void clear(); - void addAllCircles(List circles); + void addAllCircles(List circles); - void addConnectionToCircle(CircleData circle, Connection connection); + void addConnectionToCircle( + TimelineGroupingListData circle, Connection connection); void clearMyCircles(); - void upsertCircle(CircleData circle); + void upsertCircle(TimelineGroupingListData circle); - void deleteCircle(CircleData circle); + void deleteCircle(TimelineGroupingListData circle); - List getMyCircles(); + List getMyCircles(); - Result, ExecError> getCircleMembers(CircleData circle); + Result, ExecError> getCircleMembers( + TimelineGroupingListData circle); - Result, ExecError> getCirclesForUser(String id); + Result, ExecError> getCirclesForUser( + String id); - bool updateConnectionCircleData(String id, List currentCircless); + bool updateConnectionCircleData( + String id, List currentCircless); } diff --git a/lib/data/memory/memory_circles_repo.dart b/lib/data/memory/memory_circles_repo.dart index a19133a..1551232 100644 --- a/lib/data/memory/memory_circles_repo.dart +++ b/lib/data/memory/memory_circles_repo.dart @@ -1,14 +1,14 @@ import 'package:result_monad/result_monad.dart'; -import '../../models/circle_data.dart'; import '../../models/connection.dart'; import '../../models/exec_error.dart'; +import '../../models/timeline_grouping_list_data.dart'; import '../interfaces/circles_repo_intf.dart'; class MemoryCirclesRepo implements ICirclesRepo { - final _circlesForConnection = >{}; + final _circlesForConnection = >{}; final _connectionsForCircle = >{}; - final _myCircles = {}; + final _myCircles = {}; @override void clear() { @@ -18,7 +18,8 @@ class MemoryCirclesRepo implements ICirclesRepo { } @override - Result, ExecError> getCirclesForUser(String id) { + Result, ExecError> getCirclesForUser( + String id) { if (!_circlesForConnection.containsKey(id)) { return Result.error(ExecError( type: ErrorType.notFound, @@ -30,12 +31,13 @@ class MemoryCirclesRepo implements ICirclesRepo { } @override - List getMyCircles() { + List getMyCircles() { return _myCircles.toList(); } @override - Result, ExecError> getCircleMembers(CircleData circle) { + Result, ExecError> getCircleMembers( + TimelineGroupingListData circle) { if (_connectionsForCircle.containsKey(circle.id)) { return Result.ok(_connectionsForCircle[circle.id]!.toList()); } @@ -52,31 +54,33 @@ class MemoryCirclesRepo implements ICirclesRepo { } @override - void addConnectionToCircle(CircleData circle, Connection connection) { + void addConnectionToCircle( + TimelineGroupingListData circle, Connection connection) { _connectionsForCircle.putIfAbsent(circle.id, () => {}).add(connection); _circlesForConnection[connection.id]?.add(circle); } @override - void addAllCircles(List circle) { + void addAllCircles(List circle) { _myCircles.addAll(circle); } @override - bool updateConnectionCircleData(String id, List currentCircles) { + bool updateConnectionCircleData( + String id, List currentCircles) { _circlesForConnection[id] = currentCircles; return true; } @override - void upsertCircle(CircleData circle) { + void upsertCircle(TimelineGroupingListData circle) { _connectionsForCircle.putIfAbsent(circle.id, () => {}); _myCircles.remove(circle); _myCircles.add(circle); } @override - void deleteCircle(CircleData circle) { + void deleteCircle(TimelineGroupingListData circle) { for (final conCircles in _circlesForConnection.values) { conCircles.remove(circle); } diff --git a/lib/friendica_client/friendica_client.dart b/lib/friendica_client/friendica_client.dart index 74b6c38..6e1b607 100644 --- a/lib/friendica_client/friendica_client.dart +++ b/lib/friendica_client/friendica_client.dart @@ -9,7 +9,6 @@ import 'package:result_monad/result_monad.dart'; import '../friendica_client/paged_response.dart'; import '../globals.dart'; import '../models/auth/profile.dart'; -import '../models/circle_data.dart'; import '../models/connection.dart'; import '../models/direct_message.dart'; import '../models/exec_error.dart'; @@ -21,6 +20,7 @@ import '../models/media_attachment_uploads/image_types_enum.dart'; import '../models/search_results.dart'; import '../models/search_types.dart'; import '../models/timeline_entry.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../models/timeline_identifiers.dart'; import '../models/user_notification.dart'; import '../models/visibility.dart'; @@ -28,13 +28,13 @@ import '../serializers/friendica/direct_message_friendica_extensions.dart'; import '../serializers/friendica/gallery_data_friendica_extensions.dart'; import '../serializers/friendica/image_entry_friendica_extensions.dart'; import '../serializers/friendica/visibility_friendica_extensions.dart'; -import '../serializers/mastodon/circle_data_mastodon_extensions.dart'; import '../serializers/mastodon/connection_mastodon_extensions.dart'; import '../serializers/mastodon/follow_request_mastodon_extensions.dart'; import '../serializers/mastodon/instance_info_mastodon_extensions.dart'; import '../serializers/mastodon/notification_mastodon_extension.dart'; import '../serializers/mastodon/search_result_mastodon_extensions.dart'; import '../serializers/mastodon/timeline_entry_mastodon_extensions.dart'; +import '../serializers/mastodon/timeline_grouping_list_data.dart'; import '../serializers/mastodon/visibility_mastodon_extensions.dart'; import '../services/fediverse_server_validator.dart'; import '../services/network_status_service.dart'; @@ -168,18 +168,20 @@ class GalleryClient extends FriendicaClient { } } -class CirclesClient extends FriendicaClient { - static final _logger = Logger('$CirclesClient'); +class TimelineGroupingListClient extends FriendicaClient { + static final _logger = Logger('$TimelineGroupingListClient'); - CirclesClient(super.credentials) : super(); + TimelineGroupingListClient(super.credentials) : super(); - FutureResult, ExecError> getCircles() async { - _logger.finest(() => 'Getting circle (Mastodon List) data'); + FutureResult, ExecError> + getTimelineGroupingListData() async { + _logger.finest(() => 'Getting timeline grouping data (Mastodon List) data'); final url = 'https://$serverName/api/v1/lists'; final request = Uri.parse(url); return (await _getApiListRequest(request).andThenSuccessAsync( (listsJson) async => listsJson.data - .map((json) => CircleDataMastodonExtensions.fromJson(json)) + .map((json) => + TimelineGroupingListDataMastodonExtensions.fromJson(json)) .toList())) .mapError((error) => error is ExecError ? error @@ -187,7 +189,7 @@ class CirclesClient extends FriendicaClient { } FutureResult>, ExecError> getCircleMembers( - CircleData circleData, + TimelineGroupingListData circleData, PagingData page, ) async { _networkStatusService.startConnectionUpdateStatus(); @@ -206,7 +208,8 @@ class CirclesClient extends FriendicaClient { .execErrorCast(); } - FutureResult createCircle(String title) async { + FutureResult createCircle( + String title) async { _logger.finest(() => 'Creating circle (Mastodon List) of name $title'); final url = 'https://$serverName/api/v1/lists'; final body = { @@ -217,11 +220,11 @@ class CirclesClient extends FriendicaClient { body, headers: _headers, ).andThenSuccessAsync((data) async => - CircleDataMastodonExtensions.fromJson(jsonDecode(data))); + TimelineGroupingListDataMastodonExtensions.fromJson(jsonDecode(data))); return result.execErrorCast(); } - FutureResult renameCircle( + FutureResult renameCircle( String id, String title) async { _logger.finest(() => 'Reanming circle (Mastodon List) to name $title'); final url = 'https://$serverName/api/v1/lists/$id'; @@ -234,12 +237,13 @@ class CirclesClient extends FriendicaClient { headers: _headers, ).andThenSuccessAsync((data) async { final json = jsonDecode(data); - return CircleDataMastodonExtensions.fromJson(json); + return TimelineGroupingListDataMastodonExtensions.fromJson(json); }); return result.execErrorCast(); } - FutureResult deleteCircle(CircleData circleData) async { + FutureResult deleteCircle( + TimelineGroupingListData circleData) async { _logger.finest( () => 'Reanming circle (Mastodon List) to name ${circleData.name}'); final url = 'https://$serverName/api/v1/lists/${circleData.id}'; @@ -247,21 +251,22 @@ class CirclesClient extends FriendicaClient { return result.mapValue((_) => true).execErrorCast(); } - FutureResult, ExecError> getMemberCirclesForConnection( - String connectionId) async { + FutureResult, ExecError> + getMemberCirclesForConnection(String connectionId) async { _logger.finest(() => 'Getting circles (Mastodon Lists) containing connection: $connectionId'); final url = 'https://$serverName/api/v1/accounts/$connectionId/lists'; final request = Uri.parse(url); return (await _getApiListRequest(request).andThenSuccessAsync( (listsJson) async => listsJson.data - .map((json) => CircleDataMastodonExtensions.fromJson(json)) + .map((json) => + TimelineGroupingListDataMastodonExtensions.fromJson(json)) .toList())) .mapError((error) => error as ExecError); } FutureResult addConnectionToCircle( - CircleData circle, + TimelineGroupingListData circle, Connection connection, ) async { _logger.finest(() => 'Adding connection to circle'); @@ -275,7 +280,7 @@ class CirclesClient extends FriendicaClient { } FutureResult removeConnectionFromCircle( - CircleData circle, + TimelineGroupingListData circle, Connection connection, ) async { _logger.finest(() => 'Adding connection to circle'); diff --git a/lib/models/circle_data.dart b/lib/models/circle_data.dart deleted file mode 100644 index 81cb20c..0000000 --- a/lib/models/circle_data.dart +++ /dev/null @@ -1,22 +0,0 @@ -class CircleData { - static final followersPseudoCircle = CircleData('~', 'Followers'); - - final String id; - - final String name; - - CircleData(this.id, this.name); - - @override - String toString() { - return 'CircleData{id: $id, name: $name}'; - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is CircleData && runtimeType == other.runtimeType && id == other.id; - - @override - int get hashCode => id.hashCode; -} diff --git a/lib/models/timeline_grouping_list_data.dart b/lib/models/timeline_grouping_list_data.dart new file mode 100644 index 0000000..7eba97e --- /dev/null +++ b/lib/models/timeline_grouping_list_data.dart @@ -0,0 +1,25 @@ +class TimelineGroupingListData { + static final followersPseudoCircle = + TimelineGroupingListData('~', 'Followers'); + + final String id; + + final String name; + + TimelineGroupingListData(this.id, this.name); + + @override + String toString() { + return 'CircleData{id: $id, name: $name}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TimelineGroupingListData && + runtimeType == other.runtimeType && + id == other.id; + + @override + int get hashCode => id.hashCode; +} diff --git a/lib/screens/circle_add_users_screen.dart b/lib/screens/circle_add_users_screen.dart index cfbe6e6..e160381 100644 --- a/lib/screens/circle_add_users_screen.dart +++ b/lib/screens/circle_add_users_screen.dart @@ -8,9 +8,9 @@ import '../controls/linear_status_indicator.dart'; import '../controls/responsive_max_width.dart'; import '../controls/status_and_refresh_button.dart'; import '../globals.dart'; -import '../models/circle_data.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../services/network_status_service.dart'; @@ -29,7 +29,7 @@ class CircleAddUsersScreen extends StatefulWidget { class _CircleAddUsersScreenState extends State { static final _logger = Logger('$CircleAddUsersScreen'); var filterText = ''; - late CircleData circleData; + late TimelineGroupingListData circleData; @override void initState() { diff --git a/lib/screens/circle_editor_screen.dart b/lib/screens/circle_editor_screen.dart index 6061050..427ba36 100644 --- a/lib/screens/circle_editor_screen.dart +++ b/lib/screens/circle_editor_screen.dart @@ -9,8 +9,8 @@ import '../controls/responsive_max_width.dart'; import '../controls/standard_appbar.dart'; import '../controls/status_and_refresh_button.dart'; import '../globals.dart'; -import '../models/circle_data.dart'; import '../models/connection.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../services/network_status_service.dart'; @@ -31,7 +31,7 @@ class _CircleEditorScreenState extends State { var processingUpdate = false; var allowNameEditing = false; var filterText = ''; - late CircleData circleData; + late TimelineGroupingListData circleData; Future updateCircleName( BuildContext context, ConnectionsManager manager) async { diff --git a/lib/screens/editor.dart b/lib/screens/editor.dart index d0fa4f9..74f8ec9 100644 --- a/lib/screens/editor.dart +++ b/lib/screens/editor.dart @@ -17,12 +17,12 @@ import '../controls/standard_appbar.dart'; import '../controls/timeline/status_header_control.dart'; import '../controls/visibility_dialog.dart'; import '../globals.dart'; -import '../models/circle_data.dart'; import '../models/exec_error.dart'; import '../models/image_entry.dart'; import '../models/link_preview_data.dart'; import '../models/media_attachment_uploads/new_entry_media_items.dart'; import '../models/timeline_entry.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../models/visibility.dart'; import '../serializers/friendica/link_preview_friendica_extensions.dart'; import '../services/connections_manager.dart'; @@ -58,7 +58,7 @@ class _EditorScreenState extends State { final existingMediaItems = []; final focusNode = FocusNode(); Visibility visibility = Visibility.public(); - CircleData? currentCircle; + TimelineGroupingListData? currentCircle; var isSubmitting = false; @@ -603,17 +603,19 @@ class _EditorScreenState extends State { .getValueOrElse(() => []); circles.sort((g1, g2) => g1.name.compareTo(g2.name)); - final circleMenuItems = >[]; + final circleMenuItems = >[]; circleMenuItems.add(DropdownMenuItem( - value: CircleData.followersPseudoCircle, - child: Text(CircleData.followersPseudoCircle.name))); + value: TimelineGroupingListData.followersPseudoCircle, + child: Text(TimelineGroupingListData.followersPseudoCircle.name))); circleMenuItems.add(DropdownMenuItem( - value: CircleData('', ''), enabled: false, child: const Divider())); + value: TimelineGroupingListData('', ''), + enabled: false, + child: const Divider())); circleMenuItems.addAll(circles.map((g) => DropdownMenuItem( value: g, child: Text(g.name), ))); - if (currentCircle != CircleData.followersPseudoCircle && + if (currentCircle != TimelineGroupingListData.followersPseudoCircle && !circles.contains(currentCircle)) { currentCircle = null; } @@ -660,7 +662,7 @@ class _EditorScreenState extends State { const HorizontalPadding(), if (visibility.type == VisibilityType.private) Expanded( - child: DropdownButton( + child: DropdownButton( value: currentCircle, isExpanded: true, onChanged: widget.forEditing diff --git a/lib/screens/user_profile_screen.dart b/lib/screens/user_profile_screen.dart index 2916e66..2087a28 100644 --- a/lib/screens/user_profile_screen.dart +++ b/lib/screens/user_profile_screen.dart @@ -6,8 +6,8 @@ import '../controls/html_text_viewer_control.dart'; import '../controls/login_aware_cached_network_image.dart'; import '../controls/padding.dart'; import '../globals.dart'; -import '../models/circle_data.dart'; import '../models/connection.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../routes.dart'; import '../services/auth_service.dart'; import '../services/blocks_manager.dart'; @@ -157,7 +157,7 @@ class _UserProfileScreenState extends State { onSuccess: (circles) => circles.toSet(), onError: (error) { buildSnackbar(context, 'Error getting circle data: $error'); - return {}; + return {}; }); myCircles.sort((g1, g2) => g1.name.compareTo(g2.name)); diff --git a/lib/serializers/mastodon/circle_data_mastodon_extensions.dart b/lib/serializers/mastodon/circle_data_mastodon_extensions.dart deleted file mode 100644 index 1429d12..0000000 --- a/lib/serializers/mastodon/circle_data_mastodon_extensions.dart +++ /dev/null @@ -1,8 +0,0 @@ -import '../../models/circle_data.dart'; - -extension CircleDataMastodonExtensions on CircleData { - static CircleData fromJson(Map json) => CircleData( - json['id'], - json['title'], - ); -} diff --git a/lib/serializers/mastodon/timeline_grouping_list_data.dart b/lib/serializers/mastodon/timeline_grouping_list_data.dart new file mode 100644 index 0000000..ae0acdb --- /dev/null +++ b/lib/serializers/mastodon/timeline_grouping_list_data.dart @@ -0,0 +1,10 @@ +import '../../models/timeline_grouping_list_data.dart'; + +extension TimelineGroupingListDataMastodonExtensions + on TimelineGroupingListData { + static TimelineGroupingListData fromJson(Map json) => + TimelineGroupingListData( + json['id'], + json['title'], + ); +} diff --git a/lib/serializers/mastodon/visibility_mastodon_extensions.dart b/lib/serializers/mastodon/visibility_mastodon_extensions.dart index 663b7a5..43748aa 100644 --- a/lib/serializers/mastodon/visibility_mastodon_extensions.dart +++ b/lib/serializers/mastodon/visibility_mastodon_extensions.dart @@ -1,5 +1,4 @@ -import 'package:relatica/models/circle_data.dart'; - +import '../../models/timeline_grouping_list_data.dart'; import '../../models/visibility.dart'; extension VisibilityMastodonExtensions on Visibility { @@ -15,7 +14,7 @@ extension VisibilityMastodonExtensions on Visibility { if (!onComment && hasDetails) { final circleId = allowedCircleIds.firstOrNull ?? allowedUserIds.firstOrNull; - if (circleId == CircleData.followersPseudoCircle.id) { + if (circleId == TimelineGroupingListData.followersPseudoCircle.id) { return 'private'; } diff --git a/lib/services/connections_manager.dart b/lib/services/connections_manager.dart index 46fc351..0adcf1a 100644 --- a/lib/services/connections_manager.dart +++ b/lib/services/connections_manager.dart @@ -11,9 +11,9 @@ import '../friendica_client/friendica_client.dart'; import '../friendica_client/paging_data.dart'; import '../globals.dart'; import '../models/auth/profile.dart'; -import '../models/circle_data.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../utils/active_profile_selector.dart'; import 'persistent_info_service.dart'; @@ -238,7 +238,7 @@ class ConnectionsManager extends ChangeNotifier { notifyListeners(); } - List getMyCircles() { + List getMyCircles() { if (circlesNotInitialized) { circlesNotInitialized = false; _updateMyCircles(true); @@ -247,7 +247,8 @@ class ConnectionsManager extends ChangeNotifier { return circlesRepo.getMyCircles(); } - Result, ExecError> getCircleMembers(CircleData circle) { + Result, ExecError> getCircleMembers( + TimelineGroupingListData circle) { return circlesRepo .getCircleMembers(circle) .transform( @@ -258,8 +259,9 @@ class ConnectionsManager extends ChangeNotifier { .execErrorCast(); } - FutureResult createCircle(String newName) async { - final result = await CirclesClient(profile) + FutureResult createCircle( + String newName) async { + final result = await TimelineGroupingListClient(profile) .createCircle(newName) .withResultAsync((newCircle) async { circlesRepo.upsertCircle(newCircle); @@ -268,9 +270,9 @@ class ConnectionsManager extends ChangeNotifier { return result.execErrorCast(); } - FutureResult renameCircle( + FutureResult renameCircle( String id, String newName) async { - final result = await CirclesClient(profile) + final result = await TimelineGroupingListClient(profile) .renameCircle(id, newName) .withResultAsync((renamedCircle) async { circlesRepo.upsertCircle(renamedCircle); @@ -279,8 +281,9 @@ class ConnectionsManager extends ChangeNotifier { return result.execErrorCast(); } - FutureResult deleteCircle(CircleData circleData) async { - final result = await CirclesClient(profile) + FutureResult deleteCircle( + TimelineGroupingListData circleData) async { + final result = await TimelineGroupingListClient(profile) .deleteCircle(circleData) .withResultAsync((_) async { circlesRepo.deleteCircle(circleData); @@ -293,9 +296,9 @@ class ConnectionsManager extends ChangeNotifier { _updateMyCircles(true); } - Future refreshCircleMemberships(CircleData circle) async { + Future refreshCircleMemberships(TimelineGroupingListData circle) async { var page = PagingData(limit: 50); - final client = CirclesClient(profile); + final client = TimelineGroupingListClient(profile); final allResults = {}; var moreResults = true; while (moreResults) { @@ -319,7 +322,8 @@ class ConnectionsManager extends ChangeNotifier { notifyListeners(); } - Result, ExecError> getCirclesForUser(String id) { + Result, ExecError> getCirclesForUser( + String id) { final result = circlesRepo.getCirclesForUser(id); if (result.isSuccess) { _logger.finer("Circles for user $id: $result"); @@ -335,9 +339,9 @@ class ConnectionsManager extends ChangeNotifier { } FutureResult addUserToCircle( - CircleData circle, Connection connection) async { + TimelineGroupingListData circle, Connection connection) async { _logger.finest('Adding ${connection.name} to circle: ${circle.name}'); - return await CirclesClient(profile) + return await TimelineGroupingListClient(profile) .addConnectionToCircle(circle, connection) .withResultAsync((_) async => await refreshCircleMemberships(circle)) .withResult((_) => notifyListeners()) @@ -349,9 +353,9 @@ class ConnectionsManager extends ChangeNotifier { } FutureResult removeUserFromCircle( - CircleData circle, Connection connection) async { + TimelineGroupingListData circle, Connection connection) async { _logger.finest('Removing ${connection.name} from circle: ${circle.name}'); - return CirclesClient(profile) + return TimelineGroupingListClient(profile) .removeConnectionFromCircle(circle, connection) .withResultAsync((_) async => await refreshCircleMemberships(circle)) .withResult((_) => notifyListeners()) @@ -405,7 +409,9 @@ class ConnectionsManager extends ChangeNotifier { Future _refreshCircleListData(String id, bool withNotification) async { _logger.finest('Refreshing member list data for Connection $id'); - await CirclesClient(profile).getMemberCirclesForConnection(id).match( + await TimelineGroupingListClient(profile) + .getMemberCirclesForConnection(id) + .match( onSuccess: (circles) { circlesRepo.updateConnectionCircleData(id, circles); if (withNotification) { @@ -438,7 +444,9 @@ class ConnectionsManager extends ChangeNotifier { Future _updateMyCircles(bool withNotification) async { _logger.finest('Refreshing my circles list'); - await CirclesClient(profile).getCircles().match( + await TimelineGroupingListClient(profile) + .getTimelineGroupingListData() + .match( onSuccess: (circles) { _logger.finest('Got updated circles:${circles.map((e) => e.name)}'); circlesRepo.clearMyCircles(); diff --git a/lib/services/timeline_manager.dart b/lib/services/timeline_manager.dart index fda1a2d..ec78da0 100644 --- a/lib/services/timeline_manager.dart +++ b/lib/services/timeline_manager.dart @@ -5,13 +5,13 @@ import 'package:result_monad/result_monad.dart'; import '../data/interfaces/circles_repo_intf.dart'; import '../friendica_client/friendica_client.dart'; import '../models/auth/profile.dart'; -import '../models/circle_data.dart'; import '../models/entry_tree_item.dart'; import '../models/exec_error.dart'; import '../models/image_entry.dart'; import '../models/media_attachment_uploads/new_entry_media_items.dart'; import '../models/timeline.dart'; import '../models/timeline_entry.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../models/timeline_identifiers.dart'; import '../models/visibility.dart'; import 'entry_manager_service.dart'; @@ -42,7 +42,7 @@ class TimelineManager extends ChangeNotifier { notifyListeners(); } - Result, ExecError> getCircles() { + Result, ExecError> getCircles() { if (circlesNotInitialized) { _refreshCircleData(); circlesNotInitialized = false; @@ -54,7 +54,9 @@ class TimelineManager extends ChangeNotifier { Future _refreshCircleData() async { _logger.finer('Refreshing member circle data '); - await CirclesClient(profile).getCircles().match( + await TimelineGroupingListClient(profile) + .getTimelineGroupingListData() + .match( onSuccess: (circles) { circlesRepo.addAllCircles(circles); notifyListeners(); From 9c9428a85b2b38f2993144509d95a21b978e75e8 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 28 Aug 2024 11:56:19 -0400 Subject: [PATCH 14/17] Add TimelineGroupingListdata parsing of type --- lib/models/timeline_grouping_list_data.dart | 18 ++++++++++--- .../mastodon/timeline_grouping_list_data.dart | 27 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/models/timeline_grouping_list_data.dart b/lib/models/timeline_grouping_list_data.dart index 7eba97e..3bce48b 100644 --- a/lib/models/timeline_grouping_list_data.dart +++ b/lib/models/timeline_grouping_list_data.dart @@ -1,16 +1,26 @@ +enum GroupingType { + channel, + circle, + group, +} + class TimelineGroupingListData { - static final followersPseudoCircle = - TimelineGroupingListData('~', 'Followers'); + static const followersPseudoCircle = + TimelineGroupingListData('~', 'Followers', GroupingType.circle); + + static const empty = TimelineGroupingListData('', '', GroupingType.circle); final String id; final String name; - TimelineGroupingListData(this.id, this.name); + final GroupingType groupingType; + + const TimelineGroupingListData(this.id, this.name, this.groupingType); @override String toString() { - return 'CircleData{id: $id, name: $name}'; + return 'CircleData{id: $id, name: $name, type: ${groupingType.name}}'; } @override diff --git a/lib/serializers/mastodon/timeline_grouping_list_data.dart b/lib/serializers/mastodon/timeline_grouping_list_data.dart index ae0acdb..11c0784 100644 --- a/lib/serializers/mastodon/timeline_grouping_list_data.dart +++ b/lib/serializers/mastodon/timeline_grouping_list_data.dart @@ -2,9 +2,26 @@ import '../../models/timeline_grouping_list_data.dart'; extension TimelineGroupingListDataMastodonExtensions on TimelineGroupingListData { - static TimelineGroupingListData fromJson(Map json) => - TimelineGroupingListData( - json['id'], - json['title'], - ); + static TimelineGroupingListData fromJson(Map json) { + final id = json['id']?.toString() ?? ''; + final typeString = json['replies_policy']?.toString() ?? ''; + + late final GroupingType type; + if (typeString == 'followed') { + if (id.startsWith('channel')) { + type = GroupingType.channel; + } else if (id.startsWith('group')) { + type = GroupingType.group; + } else { + type = GroupingType.circle; + } + } else { + type = GroupingType.circle; + } + return TimelineGroupingListData( + id, + json['title'], + type, + ); + } } From 37d3b1a125d00182736275d1dba97a929b581741 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 28 Aug 2024 11:57:10 -0400 Subject: [PATCH 15/17] Add awareness of TimelineGroupingListData Type to queries --- lib/controls/visibility_dialog.dart | 5 ++++- lib/screens/circle_add_users_screen.dart | 6 ++++-- lib/screens/circle_editor_screen.dart | 2 +- lib/screens/circle_management_screen.dart | 3 ++- lib/screens/editor.dart | 6 +++--- lib/screens/home.dart | 4 +++- lib/screens/user_profile_screen.dart | 2 +- lib/services/connections_manager.dart | 15 +++++++++------ macos/Runner/AppDelegate.swift | 2 +- 9 files changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/controls/visibility_dialog.dart b/lib/controls/visibility_dialog.dart index 54c4831..561178e 100644 --- a/lib/controls/visibility_dialog.dart +++ b/lib/controls/visibility_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../models/visibility.dart' as v; +import '../models/timeline_grouping_list_data.dart'; import '../services/connections_manager.dart'; Future showVisibilityDialog( @@ -8,7 +9,9 @@ Future showVisibilityDialog( ConnectionsManager cm, v.Visibility visibility, ) async { - final circlesMap = {for (var item in cm.getMyCircles()) item.id: item}; + final circlesMap = { + for (var item in cm.getGroupingListData(GroupingType.circle)) item.id: item + }; final allowedCircles = visibility.allowedCircleIds.map((c) { if (c == '~') { diff --git a/lib/screens/circle_add_users_screen.dart b/lib/screens/circle_add_users_screen.dart index e160381..8db937f 100644 --- a/lib/screens/circle_add_users_screen.dart +++ b/lib/screens/circle_add_users_screen.dart @@ -36,8 +36,10 @@ class _CircleAddUsersScreenState extends State { super.initState(); final manager = getIt>().activeEntry.value; - circleData = - manager.getMyCircles().where((g) => g.id == widget.circleId).first; + circleData = manager + .getGroupingListData(GroupingType.circle) + .where((g) => g.id == widget.circleId) + .first; } Future addUserToCircle( diff --git a/lib/screens/circle_editor_screen.dart b/lib/screens/circle_editor_screen.dart index 427ba36..3cd83ac 100644 --- a/lib/screens/circle_editor_screen.dart +++ b/lib/screens/circle_editor_screen.dart @@ -93,7 +93,7 @@ class _CircleEditorScreenState extends State { final manager = getIt>().activeEntry.value; circleData = manager - .getMyCircles() + .getGroupingListData(GroupingType.circle) .where( (g) => g.id == widget.circleId, ) diff --git a/lib/screens/circle_management_screen.dart b/lib/screens/circle_management_screen.dart index 7badb83..872adaf 100644 --- a/lib/screens/circle_management_screen.dart +++ b/lib/screens/circle_management_screen.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import '../controls/responsive_max_width.dart'; import '../controls/standard_appbar.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../utils/active_profile_selector.dart'; @@ -17,7 +18,7 @@ class CircleManagementScreen extends StatelessWidget { .watch>() .activeEntry .value; - final circles = manager.getMyCircles(); + final circles = manager.getGroupingListData(GroupingType.circle); circles.sort((g1, g2) => g1.name.compareTo(g2.name)); return Scaffold( appBar: StandardAppBar.build( diff --git a/lib/screens/editor.dart b/lib/screens/editor.dart index 74f8ec9..3ebeb29 100644 --- a/lib/screens/editor.dart +++ b/lib/screens/editor.dart @@ -607,10 +607,10 @@ class _EditorScreenState extends State { circleMenuItems.add(DropdownMenuItem( value: TimelineGroupingListData.followersPseudoCircle, child: Text(TimelineGroupingListData.followersPseudoCircle.name))); - circleMenuItems.add(DropdownMenuItem( - value: TimelineGroupingListData('', ''), + circleMenuItems.add(const DropdownMenuItem( + value: TimelineGroupingListData.empty, enabled: false, - child: const Divider())); + child: Divider())); circleMenuItems.addAll(circles.map((g) => DropdownMenuItem( value: g, child: Text(g.name), diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 60abc59..7d6bdd1 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -139,7 +139,9 @@ class _HomeScreenState extends ConsumerState { )), ...circles .map((c) => TimelineIdentifiers( - timeline: TimelineType.circle, auxData: c.id, label: c.name)) + timeline: TimelineType.circle, + auxData: c.id, + label: '${c.name} - ${c.groupingType.name}')) .map((e) => DropdownMenuItem( value: e, child: Text( diff --git a/lib/screens/user_profile_screen.dart b/lib/screens/user_profile_screen.dart index 2087a28..b8102c5 100644 --- a/lib/screens/user_profile_screen.dart +++ b/lib/screens/user_profile_screen.dart @@ -152,7 +152,7 @@ class _UserProfileScreenState extends State { Connection profile, ConnectionsManager manager, ) { - final myCircles = manager.getMyCircles(); + final myCircles = manager.getGroupingListData(GroupingType.circle); final usersCircles = manager.getCirclesForUser(profile.id).fold( onSuccess: (circles) => circles.toSet(), onError: (error) { diff --git a/lib/services/connections_manager.dart b/lib/services/connections_manager.dart index 0adcf1a..c559950 100644 --- a/lib/services/connections_manager.dart +++ b/lib/services/connections_manager.dart @@ -238,13 +238,16 @@ class ConnectionsManager extends ChangeNotifier { notifyListeners(); } - List getMyCircles() { + List getGroupingListData(GroupingType type) { if (circlesNotInitialized) { circlesNotInitialized = false; - _updateMyCircles(true); + _updateMyGroupingListData(true); } - return circlesRepo.getMyCircles(); + return circlesRepo + .getMyCircles() + .where((e) => e.groupingType == type) + .toList(); } Result, ExecError> getCircleMembers( @@ -293,7 +296,7 @@ class ConnectionsManager extends ChangeNotifier { } void refreshCircles() { - _updateMyCircles(true); + _updateMyGroupingListData(true); } Future refreshCircleMemberships(TimelineGroupingListData circle) async { @@ -399,7 +402,7 @@ class ConnectionsManager extends ChangeNotifier { Connection connection, { bool withNotifications = true, }) async { - await _updateMyCircles(false); + await _updateMyGroupingListData(false); await _refreshCircleListData(connection.id, false); await _refreshConnection(connection, false); if (withNotifications) { @@ -442,7 +445,7 @@ class ConnectionsManager extends ChangeNotifier { ); } - Future _updateMyCircles(bool withNotification) async { + Future _updateMyGroupingListData(bool withNotification) async { _logger.finest('Refreshing my circles list'); await TimelineGroupingListClient(profile) .getTimelineGroupingListData() diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef64..8e02df2 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true From c8eea6cdae34b51b5dbb9c1c7f23e9512e2d4255 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 28 Aug 2024 12:18:06 -0400 Subject: [PATCH 16/17] Breakup timeline selection into Circles, Groups, and Channels --- lib/screens/editor.dart | 2 +- lib/screens/home.dart | 82 +++++++++++++++++++++++++----- lib/services/timeline_manager.dart | 8 ++- 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/lib/screens/editor.dart b/lib/screens/editor.dart index 3ebeb29..07e6cb3 100644 --- a/lib/screens/editor.dart +++ b/lib/screens/editor.dart @@ -599,7 +599,7 @@ class _EditorScreenState extends State { final circles = context .watch>() .activeEntry - .andThen((tm) => tm.getCircles()) + .andThen((tm) => tm.getTimelineGroupingListData(GroupingType.circle)) .getValueOrElse(() => []); circles.sort((g1, g2) => g1.name.compareTo(g2.name)); diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 7d6bdd1..796c366 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -3,17 +3,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; -import 'package:relatica/controls/focus_mode_status_headline.dart'; -import 'package:relatica/riverpod_controllers/focus_mode.dart'; import '../controls/app_bottom_nav_bar.dart'; +import '../controls/focus_mode_status_headline.dart'; import '../controls/linear_status_indicator.dart'; import '../controls/login_aware_cached_network_image.dart'; import '../controls/responsive_max_width.dart'; import '../controls/standard_app_drawer.dart'; import '../controls/timeline/timeline_panel.dart'; import '../globals.dart'; +import '../models/timeline_grouping_list_data.dart'; import '../models/timeline_identifiers.dart'; +import '../riverpod_controllers/focus_mode.dart'; import '../services/auth_service.dart'; import '../services/network_status_service.dart'; import '../services/timeline_manager.dart'; @@ -114,9 +115,24 @@ class _HomeScreenState extends ConsumerState { .activeEntry .value; - final circles = manager.getCircles().getValueOrElse(() => []).toList(); + final circles = manager + .getTimelineGroupingListData(GroupingType.circle) + .getValueOrElse(() => []) + .toList(); circles.sort((g1, g2) => g1.name.compareTo(g2.name)); + final groups = manager + .getTimelineGroupingListData(GroupingType.group) + .getValueOrElse(() => []) + .toList(); + groups.sort((g1, g2) => g1.name.compareTo(g2.name)); + + final channels = manager + .getTimelineGroupingListData(GroupingType.channel) + .getValueOrElse(() => []) + .toList(); + channels.sort((g1, g2) => g1.name.compareTo(g2.name)); + final items = [ ...standardTypes .map((t) => TimelineIdentifiers(timeline: t)) @@ -137,17 +153,41 @@ class _HomeScreenState extends ConsumerState { decoration: TextDecoration.underline, ), )), - ...circles - .map((c) => TimelineIdentifiers( - timeline: TimelineType.circle, - auxData: c.id, - label: '${c.name} - ${c.groupingType.name}')) - .map((e) => DropdownMenuItem( - value: e, - child: Text( - e.toLabel(), - overflow: TextOverflow.fade, - ))), + ..._timelineGroupingListDataCollectionToDropdown(circles), + const DropdownMenuItem( + value: null, + enabled: false, + child: Divider(), + ), + const DropdownMenuItem( + value: null, + enabled: false, + child: Text( + 'Groups', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + decoration: TextDecoration.underline, + ), + )), + ..._timelineGroupingListDataCollectionToDropdown(groups), + const DropdownMenuItem( + value: null, + enabled: false, + child: Divider(), + ), + const DropdownMenuItem( + value: null, + enabled: false, + child: Text( + 'Channels', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + decoration: TextDecoration.underline, + ), + )), + ..._timelineGroupingListDataCollectionToDropdown(channels), ]; if (items.where((i) => i.value == currentTimeline).isEmpty) { @@ -169,3 +209,17 @@ class _HomeScreenState extends ConsumerState { }); } } + +List> + _timelineGroupingListDataCollectionToDropdown( + List circles) => + circles + .map((c) => TimelineIdentifiers( + timeline: TimelineType.circle, auxData: c.id, label: c.name)) + .map((e) => DropdownMenuItem( + value: e, + child: Text( + e.toLabel(), + overflow: TextOverflow.fade, + ))) + .toList(); diff --git a/lib/services/timeline_manager.dart b/lib/services/timeline_manager.dart index ec78da0..db9bb63 100644 --- a/lib/services/timeline_manager.dart +++ b/lib/services/timeline_manager.dart @@ -42,14 +42,18 @@ class TimelineManager extends ChangeNotifier { notifyListeners(); } - Result, ExecError> getCircles() { + Result, ExecError> getTimelineGroupingListData( + GroupingType type) { if (circlesNotInitialized) { _refreshCircleData(); circlesNotInitialized = false; return Result.ok([]); } - return Result.ok(circlesRepo.getMyCircles()); + return Result.ok(circlesRepo + .getMyCircles() + .where((e) => e.groupingType == type) + .toList()); } Future _refreshCircleData() async { From a90ad978fcf45f7a76d6e257ae03aa12de3a986c Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 29 Aug 2024 12:45:25 -0400 Subject: [PATCH 17/17] Only add Circle/Group/Channel section if not empty --- lib/screens/home.dart | 96 +++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 796c366..23fd8d6 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -137,57 +137,63 @@ class _HomeScreenState extends ConsumerState { ...standardTypes .map((t) => TimelineIdentifiers(timeline: t)) .map((e) => DropdownMenuItem(value: e, child: Text(e.toLabel()))), - const DropdownMenuItem( - value: null, - enabled: false, - child: Divider(), - ), - const DropdownMenuItem( + if (circles.isNotEmpty) ...[ + const DropdownMenuItem( value: null, enabled: false, - child: Text( - 'Circles', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - decoration: TextDecoration.underline, - ), - )), - ..._timelineGroupingListDataCollectionToDropdown(circles), - const DropdownMenuItem( - value: null, - enabled: false, - child: Divider(), - ), - const DropdownMenuItem( + child: Divider(), + ), + const DropdownMenuItem( + value: null, + enabled: false, + child: Text( + 'Circles', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + decoration: TextDecoration.underline, + ), + )), + ..._timelineGroupingListDataCollectionToDropdown(circles), + ], + if (groups.isNotEmpty) ...[ + const DropdownMenuItem( value: null, enabled: false, - child: Text( - 'Groups', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - decoration: TextDecoration.underline, - ), - )), - ..._timelineGroupingListDataCollectionToDropdown(groups), - const DropdownMenuItem( - value: null, - enabled: false, - child: Divider(), - ), - const DropdownMenuItem( + child: Divider(), + ), + const DropdownMenuItem( + value: null, + enabled: false, + child: Text( + 'Groups', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + decoration: TextDecoration.underline, + ), + )), + ..._timelineGroupingListDataCollectionToDropdown(groups), + ], + if (channels.isNotEmpty) ...[ + const DropdownMenuItem( value: null, enabled: false, - child: Text( - 'Channels', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - decoration: TextDecoration.underline, - ), - )), - ..._timelineGroupingListDataCollectionToDropdown(channels), + child: Divider(), + ), + const DropdownMenuItem( + value: null, + enabled: false, + child: Text( + 'Channels', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + decoration: TextDecoration.underline, + ), + )), + ..._timelineGroupingListDataCollectionToDropdown(channels), + ], ]; if (items.where((i) => i.value == currentTimeline).isEmpty) {