diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dc313a..04dc7a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Clicking on "Comments" stats loads the post comment view * Moved the utility buttons from the bottom of the post/comment to a popup menu on the upper right * Show visibility of images in the gallery + * Notifications now show truncated post bodies not the full body * Sort the notifications by unread by category and then unread chronological so: * Unread Direct Messages (sorted newest first) * Unresolved Follow/Connection Requests (sorted newest first) @@ -21,6 +22,7 @@ * Eliminated excessive cropping of top of Contacts Screen * Can go to account profiles of accounts never seen before from the Interactions Screen (list of which accounts, liked, reshared, etc) + * Notifications refreshing more stable * New Features * Post/Comment Editing * Being able to set the posts as private and a single group it is visible to (or "Followers", the default) diff --git a/README.md b/README.md index 8d10009..0525711 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ A Flutter application for interfacing with the Friendica social network. -Relatica v0.2.0 on Linux Home Screen Screenshot +Relatica v0.3.0 on Linux Home Screen Screenshot -Relatica v0.2.0 on Linux Home Expanded Drawer Screenshot +Relatica v0.3.0 on Linux Home Expanded Drawer Screenshot ## Project Objectives diff --git a/beta-program.md b/beta-program.md index f3d7a89..93f42f4 100644 --- a/beta-program.md +++ b/beta-program.md @@ -57,15 +57,18 @@ wanted to lay out some expectations before getting into the small details * Logging in with username/password and OAuth. These are stored using the OS specific key vaults * Having multiple accounts logged in at the same time and switching between them -* Writing **public** posts +* Writing public and private posts + * For private posts have to select one of the Groups you have users setup for or all followers. * Typing @ brings up a list of all known fediverse accounts that the app has ever seen as you type (but not all that your server has seen) * Typing # brings up a list of all known hashtags that the app has ever seen as you type (but not all that your server has seen) - * Very basic markdown (wrapping in stars for bold/italics) + * Very basic markdown (wrapping in stars for bold/italics, bulletted list) + * Explicit link preview builder * Adding new images to posts * Image posting with easy means of adding ALT text * Attaching existing images to posts +* Editing posts and comments * Resharing posts * Direct (private) messaging * Browsing timelines including from groups you've created. In this app "Home" is the same as the " @@ -87,25 +90,21 @@ wanted to lay out some expectations before getting into the small details change in the near future) * Show list of who liked and reshared posts/comments * Detailed information about users, such as description fields +* Notifications browsing and dismissing +* Search through server using freeform text or "%" wild card for: accounts, statuses, hashtags +* Direct Load links to Fediverse posts and accounts within the app ### Big things I want to have working in the near future: * More timeline types like Comments, "By Activity", etc. * Better timeline UX to allow for "in-fill" -* Fine grain details about users (last post, last update) * Better data display on larger format displays by doing things like: * Allowing images/thumbnails to be larger * Limiting maximum width of timeline columns * Nicer aesthetics * Fix below identified issues -* Notifications broken into: - * Messages (from message system) - * Friend requests - * Real notifications -* More greedy loading of notifications when not in low bandwidth mode * More intuitive timeline controls * Real paging across the board - * Notifications * Comments * Offline caching of older content * Dynamic search of locally known posts and hashtags. @@ -115,14 +114,9 @@ wanted to lay out some expectations before getting into the small details ### Further out things I want to have working: -* Private posts -* Post editing * Profile editor * Opening accounts, tags, etc within posts within the app itself through user's home server -* Equivalent of account/post link paste and processing from website version * Group creation, deletion, renaming -* Being able to load accounts and/or posts from links from other users within app -* Open fediverse accounts/files within app * Nitter replacement of Twitter links * User configurable Server blocking * Server-side searching tied into profiles, posts, hashtags @@ -136,7 +130,6 @@ wanted to lay out some expectations before getting into the small details * Creating new forums through the application * Site administration * Adding videos or files to posts -* Multi-account logins ## General behaviors/problems to be aware of: @@ -147,7 +140,7 @@ wanted to lay out some expectations before getting into the small details * Content Warnings/Spoiler Text on *posts* aren't federating over to Mastodon well so only use it on Comments for now (fixed on servers running Friendica 2023.03) * ALT text on images should not have any quotation marks as it breaks when federating over to - Diaspora for the time being + Diaspora for the time being (fixed on servers running Friendica 2023.03) * Portrait videos overflow their boxes in the timeline * Blocked/ignored user's content is still returned by the API (Fixed in Friendica 2023.03) * Paging for some of the endpoints either isn't wired in yet or is not working as needed server @@ -156,14 +149,10 @@ wanted to lay out some expectations before getting into the small details * Gallery Contents * Comments on posts * Tags - * Notifications * Blocked user list ### Cumbersome and hopefully improved in the very near future: -* The only post type that is supported right now are public posts therefore all posts you write - through the app will have this privacy level. -* Notifications need to be manually refreshed. * Sometimes timelines get confused so swapping between the different groups/timelines creates a muddled display. Restarting the app fixes this. * Some images within posts, usually graphical emojis, are rendered drastically larger than they diff --git a/install.md b/install.md index 3f224cd..c18da14 100644 --- a/install.md +++ b/install.md @@ -6,11 +6,11 @@ For more information about the current beta testing program # Latest Binaries: -* [Android v0.2.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.2.0%2Frelatica_v0.2.0.apk.zip) -* iPhone/iPad v0.2.0: This is only available through TestFlight. Please contact me for access. -* [Windows (Intel) v0.2.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.2.0%2FRelatica_v0.2.0_x64_win.zip) -* macOS v0.2.0 This is only available through TestFlight. Please contact me for access. -* [Linux (Intel Ubuntu 22) v0.2.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.2.0%2Frelatica_v0.2.0_linux_x64_ubuntu22.zip) +* [Android v0.3.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.3.0%2Frelatica_v0.3.0.apk.zip) +* iPhone/iPad v0.3.0: This is only available through TestFlight. Please contact me for access. +* [Windows (Intel) v0.3.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.3.0%2FRelatica_v0.3.0_win_x64.zip) +* macOS v0.3.0 This is only available through TestFlight. Please contact me for access. +* [Linux (Intel Ubuntu 22) v0.3.0](https://mysocialportal-relatica.nyc3.cdn.digitaloceanspaces.com/v0.3.0%2Frelatica_v0.3.0_linux_x64_ubuntu22.zip) ## Mobile diff --git a/lib/data/objectbox/objectbox_cache.dart b/lib/data/objectbox/objectbox_cache.dart index c24c3b4..c847d6b 100644 --- a/lib/data/objectbox/objectbox_cache.dart +++ b/lib/data/objectbox/objectbox_cache.dart @@ -4,27 +4,45 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; +import '../../globals.dart'; import '../../objectbox.g.dart'; class ObjectBoxCache { static final _logger = Logger('ObjectBoxCache'); + static final _openCaches = {}; late final Store store; ObjectBoxCache._create(this.store); - static Future create( - {String baseDir = 'objectboxcache', String? subDir}) async { + static Future create({ + String baseDir = 'objectboxcache', + String? subDir, + }) async { final docsDir = await getApplicationSupportDirectory(); final path = p.join(docsDir.path, baseDir, subDir); + _logger.info('ObjectBoxCache path: $path'); + if (_openCaches.containsKey(path)) { + _logger.fine('Cache already exists for path, returning it: $path'); + return _openCaches[path]!; + } + try { - Directory(path).createSync(recursive: true); + final directory = Directory(path); + if (!directory.existsSync()) { + Directory(path).createSync(recursive: true); + } } catch (e) { _logger.severe('Error creating ObjectCachePathDirectory: $e'); } - _logger.info('ObjectBoxCache path: $path'); + final store = await openStore( - directory: path, macosApplicationGroup: 'T69YZGT58U.relatica'); - return ObjectBoxCache._create(store); + directory: path, + macosApplicationGroup: macOsGroupId, + ); + final cache = ObjectBoxCache._create(store); + _openCaches[path] = cache; + _logger.fine('New cache created for path: $path'); + return cache; } } diff --git a/lib/globals.dart b/lib/globals.dart index dbacb83..8e722d0 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:uuid/uuid.dart'; +const macOsGroupId = 'T69YZGT58U.relatica'; + final getIt = GetIt.instance; String randomId() => const Uuid().v4().toString(); diff --git a/lib/models/auth/oauth_credentials.dart b/lib/models/auth/oauth_credentials.dart index ca51148..5103490 100644 --- a/lib/models/auth/oauth_credentials.dart +++ b/lib/models/auth/oauth_credentials.dart @@ -95,7 +95,7 @@ class OAuthCredentials implements ICredentials { final response = await http.post(idEndpoint, body: { 'client_name': 'Relatica', 'redirect_uris': '$redirectUrl', - 'scopes': 'read write push', + 'scopes': 'read write follow push', 'website': 'https://myportal.social', }); @@ -123,7 +123,7 @@ class OAuthCredentials implements ICredentials { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUrl, - 'scope': 'read write push', + 'scope': 'read write follow push', }); try { diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 620fadd..bd15ee3 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -182,8 +182,8 @@ class _SearchScreenState extends State { ), ), ), - bottomNavigationBar: AppBottomNavBar( - currentButton: NavBarButtons.contacts, + bottomNavigationBar: const AppBottomNavBar( + currentButton: NavBarButtons.search, ), ); } diff --git a/lib/services/notifications_manager.dart b/lib/services/notifications_manager.dart index 818cafc..5877028 100644 --- a/lib/services/notifications_manager.dart +++ b/lib/services/notifications_manager.dart @@ -80,32 +80,52 @@ class NotificationsManager extends ChangeNotifier { continue; } final page = _pm.pages[i]; - PagingData? pd; if (i == 0) { - if (page.next == null) { - _logger.severe( - "Expected first page to have a next page so can query on this page of data but doesn't exist."); - continue; - } - final response = await _clientGetNotificationsRequest(page.next!); - response.match( - onSuccess: (response) => pd = response.previous, - onError: (error) => - _logger.severe('Error getting previous page: $error')); - if (pd == null && page.previous == null) { - _logger.severe( - 'Next page returned no results and no previous page so need to re-initalize'); - } else { - final response = await _clientGetNotificationsRequest(pd!); + PagingData? pd; + bool initializedFirstPage = false; + if (page.next != null) { + final response = await _clientGetNotificationsRequest(page.next!); response.match( - onSuccess: (response) => pd = response.next, + onSuccess: (response) => pd = response.previous, onError: (error) => _logger.severe('Error getting previous page: $error')); + if (pd != null) { + final response = await _clientGetNotificationsRequest(pd!); + response.match( + onSuccess: (response) { + initializedFirstPage = true; + notificationsFromRefresh.addAll(response.data); + }, + onError: (error) => + _logger.severe('Error getting previous page: $error')); + } else if (pd == null && page.previous != null) { + final response = await _clientGetNotificationsRequest( + page.previous!) + .andThenAsync((previousData) async => previousData.next != + null + ? await _clientGetNotificationsRequest(previousData.next!) + : buildErrorResult( + type: ErrorType.rangeError, + message: 'No "next" page from previous data either')); + response.match( + onSuccess: (response) { + initializedFirstPage = true; + notificationsFromRefresh.addAll(response.data); + }, + onError: (error) => + _logger.severe('Error getting previous page: $error')); + } else if (pd == null && page.previous == null) { + _logger.severe( + 'Next page returned no results and no previous page so will need to re-initalize'); + } + } else { + _logger.severe( + 'There is no next page to query so will be forced to reset'); } - if (pd == null) { + if (!initializedFirstPage) { _logger.severe( - 'Previous and next page both returned nulls so need to reinitialize'); + 'Unable to determine call to rebuild initial page so resetting'); _pm.clear(); final result = await _pm.initialize(initialPull); result.andThenSuccess( @@ -126,7 +146,7 @@ class NotificationsManager extends ChangeNotifier { onSuccess: (response) => notificationsFromRefresh.addAll(response.data), onError: (error) => - _logger.severe('Error getting previous page: $error')); + _logger.severe('Error getting next page: $error')); } } diff --git a/lib/services/secrets_service.dart b/lib/services/secrets_service.dart index 75789f1..8fc05ae 100644 --- a/lib/services/secrets_service.dart +++ b/lib/services/secrets_service.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:result_monad/result_monad.dart'; +import '../globals.dart'; import '../models/auth/basic_credentials.dart'; import '../models/auth/credentials_intf.dart'; import '../models/auth/oauth_credentials.dart'; @@ -28,6 +29,7 @@ class SecretsService { ), mOptions: MacOsOptions( accountName: _storageAccountName, + groupId: macOsGroupId, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index a2ff5fa..b0b8960 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: relatica description: A mobile and desktop client for interacting with the Friendica social network publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.2.0+1 +version: 0.3.0+1 environment: sdk: '>=2.18.2 <3.0.0' diff --git a/screenshots/v0.3.0/linux/relatica_v0.3.0_drawer.png b/screenshots/v0.3.0/linux/relatica_v0.3.0_drawer.png new file mode 100755 index 0000000..2cfb665 Binary files /dev/null and b/screenshots/v0.3.0/linux/relatica_v0.3.0_drawer.png differ diff --git a/screenshots/v0.3.0/linux/relatica_v0.3.0_home.png b/screenshots/v0.3.0/linux/relatica_v0.3.0_home.png new file mode 100755 index 0000000..966dc7b Binary files /dev/null and b/screenshots/v0.3.0/linux/relatica_v0.3.0_home.png differ