Browse Source

neon: Adapt to new client

pull/61/head
jld3103 2 years ago
parent
commit
0c2b5a8634
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 10
      packages/neon/integration_test/screenshot_test.dart
  2. 4
      packages/neon/lib/app.dart
  3. 10
      packages/neon/lib/src/apps/files/widgets/file_preview.dart
  4. 39
      packages/neon/lib/src/apps/news/blocs/articles.dart
  5. 68
      packages/neon/lib/src/apps/news/blocs/news.dart
  6. 36
      packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart
  7. 19
      packages/neon/lib/src/apps/notes/blocs/notes.dart
  8. 8
      packages/neon/lib/src/apps/notes/blocs/notes.rxb.g.dart
  9. 2
      packages/neon/lib/src/apps/notes/utils/exception_handler.dart
  10. 2
      packages/neon/lib/src/apps/notes/widgets/notes_view.dart
  11. 14
      packages/neon/lib/src/apps/notifications/blocs/notifications.dart
  12. 4
      packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart
  13. 2
      packages/neon/lib/src/apps/notifications/pages/main.dart
  14. 9
      packages/neon/lib/src/blocs/apps.dart
  15. 6
      packages/neon/lib/src/blocs/apps.rxb.g.dart
  16. 5
      packages/neon/lib/src/blocs/capabilities.dart
  17. 4
      packages/neon/lib/src/blocs/login.dart
  18. 10
      packages/neon/lib/src/blocs/push_notifications.dart
  19. 5
      packages/neon/lib/src/blocs/user_details.dart
  20. 10
      packages/neon/lib/src/blocs/user_status.dart
  21. 32
      packages/neon/lib/src/models/nextcloud_notification.dart
  22. 21
      packages/neon/lib/src/models/nextcloud_notification.g.dart
  23. 1
      packages/neon/lib/src/neon.dart
  24. 10
      packages/neon/lib/src/pages/home/home.dart
  25. 2
      packages/neon/lib/src/pages/home/widgets/server_status.dart
  26. 14
      packages/neon/lib/src/utils/push_utils.dart
  27. 49
      packages/neon/lib/src/utils/request_manager.dart
  28. 2
      packages/neon/lib/src/utils/theme.dart
  29. 26
      packages/neon/lib/src/widgets/account_avatar.dart
  30. 2
      packages/neon/lib/src/widgets/cached_url_image.dart
  31. 36
      packages/neon/lib/src/widgets/exception.dart

10
packages/neon/integration_test/screenshot_test.dart

@ -307,10 +307,10 @@ Future main() async {
const wikipediaFeedURL = 'https://en.wikipedia.org/w/api.php?action=featuredfeed&feed=featured&feedformat=atom'; const wikipediaFeedURL = 'https://en.wikipedia.org/w/api.php?action=featuredfeed&feed=featured&feedformat=atom';
const nasaFeedURL = 'https://www.nasa.gov/rss/dyn/breaking_news.rss'; const nasaFeedURL = 'https://www.nasa.gov/rss/dyn/breaking_news.rss';
final folder = await account.client.news.createFolder('test'); final folder = await account.client.news.createFolder(name: 'test');
await account.client.news.addFeed( await account.client.news.addFeed(
nasaFeedURL, url: nasaFeedURL,
folderId: folder!.folders.single.id, folderId: folder.folders!.single.id,
); );
await pumpAppPage( await pumpAppPage(
@ -449,8 +449,8 @@ Future main() async {
testWidgets('notifications', (final tester) async { testWidgets('notifications', (final tester) async {
await account.client.notifications.sendAdminNotification( await account.client.notifications.sendAdminNotification(
account.username, userId: account.username,
'Notifications demo', shortMessage: 'Notifications demo',
longMessage: 'This is a notifications demo of the Neon app', longMessage: 'This is a notifications demo of the Neon app',
); );

4
packages/neon/lib/app.dart

@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:neon/l10n/localizations.dart'; import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/capabilities.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -32,7 +32,7 @@ class NeonApp extends StatefulWidget {
// ignore: prefer_mixin // ignore: prefer_mixin
class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver { class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver {
final _navigatorKey = GlobalKey<NavigatorState>(); final _navigatorKey = GlobalKey<NavigatorState>();
NextcloudTheme? _userTheme; CoreServerCapabilitiesOcsDataCapabilitiesTheming? _userTheme;
final _platformBrightness = BehaviorSubject<Brightness>.seeded( final _platformBrightness = BehaviorSubject<Brightness>.seeded(
WidgetsBinding.instance.window.platformBrightness, WidgetsBinding.instance.window.platformBrightness,
); );

10
packages/neon/lib/src/apps/files/widgets/file_preview.dart

@ -46,11 +46,11 @@ class FilePreview extends StatelessWidget {
final stream = Provider.of<RequestManager>(context).wrapBytes( final stream = Provider.of<RequestManager>(context).wrapBytes(
account.client.id, account.client.id,
'files-preview-${details.etag}-$width-$height', 'files-preview-${details.etag}-$width-$height',
() async => (await account.client.core.getPreviewBytes( () async => account.client.core.getPreview(
details.path.join('/'), file: details.path.join('/'),
width: width, x: width,
height: height, y: height,
))!, ),
preferCache: true, preferCache: true,
); );

39
packages/neon/lib/src/apps/news/blocs/articles.dart

@ -70,29 +70,39 @@ class NewsArticlesBloc extends $NewsArticlesBloc {
_$markArticleAsReadEvent.listen((final article) { _$markArticleAsReadEvent.listen((final article) {
_wrapArticleAction((final client) async { _wrapArticleAction((final client) async {
await client.news.markArticleAsRead(article.id!); await client.news.markArticleAsRead(itemId: article.id!);
_articleUpdateController.add(article..unread = false); // TODO
//_articleUpdateController.add(article..unread = false);
}); });
}); });
_$markArticleAsUnreadEvent.listen((final article) { _$markArticleAsUnreadEvent.listen((final article) {
_wrapArticleAction((final client) async { _wrapArticleAction((final client) async {
await client.news.markArticleAsUnread(article.id!); await client.news.markArticleAsUnread(itemId: article.id!);
_articleUpdateController.add(article..unread = true); // TODO
//_articleUpdateController.add(article..unread = true);
}); });
}); });
_$starArticleEvent.listen((final article) { _$starArticleEvent.listen((final article) {
_wrapArticleAction((final client) async { _wrapArticleAction((final client) async {
await client.news.starArticle(article.feedId!, article.guidHash!); await client.news.starArticle(
_articleUpdateController.add(article..starred = true); feedId: article.feedId!,
guidHash: article.guidHash!,
);
// TODO
//_articleUpdateController.add(article..starred = true);
}); });
}); });
_$unstarArticleEvent.listen((final article) { _$unstarArticleEvent.listen((final article) {
_wrapArticleAction((final client) async { _wrapArticleAction((final client) async {
await client.news.unstarArticle(article.feedId!, article.guidHash!); await client.news.unstarArticle(
_articleUpdateController.add(article..starred = false); feedId: article.feedId!,
guidHash: article.guidHash!,
);
// TODO
//_articleUpdateController.add(article..starred = false);
}); });
}); });
@ -149,16 +159,15 @@ class NewsArticlesBloc extends $NewsArticlesBloc {
} }
newsBloc.requestManager newsBloc.requestManager
.wrapNextcloud<List<NewsArticle>, NewsListArticles, void, NextcloudNewsClient>( .wrapNextcloud<List<NewsArticle>, NewsListArticles>(
newsBloc.client.id, newsBloc.client.id,
newsBloc.client.news,
'news-articles-$type-$id-$getRead', 'news-articles-$type-$id-$getRead',
() async => (await newsBloc.client.news.listArticles( () async => newsBloc.client.news.listArticles(
type: type, type: type,
id: id, id: id ?? 0,
getRead: getRead, getRead: getRead ?? true ? 1 : 0,
))!, ),
(final response) => response.items, (final response) => response.items!,
previousData: _articlesSubject.valueOrNull?.data, previousData: _articlesSubject.valueOrNull?.data,
) )
.listen(_articlesSubject.add); .listen(_articlesSubject.add);

68
packages/neon/lib/src/apps/news/blocs/news.dart

@ -13,23 +13,23 @@ part 'news.rxb.g.dart';
abstract class NewsBlocEvents { abstract class NewsBlocEvents {
void refresh({required final bool mainArticlesToo}); void refresh({required final bool mainArticlesToo});
void addFeed(final String url, final int? folderID); void addFeed(final String url, final int? folderId);
void removeFeed(final int feedID); void removeFeed(final int feedId);
void renameFeed(final int feedID, final String name); void renameFeed(final int feedId, final String feedTitle);
void moveFeed(final int feedID, final int? folderID); void moveFeed(final int feedId, final int? folderId);
void markFeedAsRead(final int feedID); void markFeedAsRead(final int feedId);
void createFolder(final String name); void createFolder(final String name);
void deleteFolder(final int folderID); void deleteFolder(final int folderId);
void renameFolder(final int folderID, final String name); void renameFolder(final int folderId, final String name);
void markFolderAsRead(final int folderID); void markFolderAsRead(final int folderId);
} }
abstract class NewsBlocStates { abstract class NewsBlocStates {
@ -54,21 +54,21 @@ class NewsBloc extends $NewsBloc {
_$addFeedEvent.listen((final event) { _$addFeedEvent.listen((final event) {
_wrapFeedAction( _wrapFeedAction(
(final client) async => client.news.addFeed( (final client) async => client.news.addFeed(
event.url, url: event.url,
folderId: event.folderID, folderId: event.folderId,
), ),
); );
}); });
_$removeFeedEvent.listen((final feedID) { _$removeFeedEvent.listen((final feedId) {
_wrapFeedAction((final client) async => client.news.deleteFeed(feedID)); _wrapFeedAction((final client) async => client.news.deleteFeed(feedId: feedId));
}); });
_$renameFeedEvent.listen((final event) { _$renameFeedEvent.listen((final event) {
_wrapFeedAction( _wrapFeedAction(
(final client) async => client.news.renameFeed( (final client) async => client.news.renameFeed(
event.feedID, feedId: event.feedId,
event.name, feedTitle: event.feedTitle,
), ),
); );
}); });
@ -77,8 +77,8 @@ class NewsBloc extends $NewsBloc {
final stream = requestManager final stream = requestManager
.wrapWithoutCache( .wrapWithoutCache(
() async => client.news.moveFeed( () async => client.news.moveFeed(
event.feedID, feedId: event.feedId,
folderId: event.folderID, folderId: event.folderId,
), ),
) )
.asBroadcastStream(); .asBroadcastStream();
@ -89,12 +89,12 @@ class NewsBloc extends $NewsBloc {
}); });
}); });
_$markFeedAsReadEvent.listen((final feedID) { _$markFeedAsReadEvent.listen((final feedId) {
final stream = requestManager final stream = requestManager
.wrapWithoutCache( .wrapWithoutCache(
() async => client.news.markFeedAsRead( () async => client.news.markFeedAsRead(
feedID, feedId: feedId,
_newestItemId, newestItemId: _newestItemId,
), ),
) )
.asBroadcastStream(); .asBroadcastStream();
@ -105,28 +105,28 @@ class NewsBloc extends $NewsBloc {
}); });
_$createFolderEvent.listen((final name) { _$createFolderEvent.listen((final name) {
_wrapFolderAction((final client) async => client.news.createFolder(name)); _wrapFolderAction((final client) async => client.news.createFolder(name: name));
}); });
_$deleteFolderEvent.listen((final folderID) { _$deleteFolderEvent.listen((final folderId) {
_wrapFolderAction((final client) async => client.news.deleteFolder(folderID)); _wrapFolderAction((final client) async => client.news.deleteFolder(folderId: folderId));
}); });
_$renameFolderEvent.listen((final event) { _$renameFolderEvent.listen((final event) {
_wrapFolderAction( _wrapFolderAction(
(final client) async => client.news.renameFolder( (final client) async => client.news.renameFolder(
event.folderID, folderId: event.folderId,
event.name, name: event.name,
), ),
); );
}); });
_$markFolderAsReadEvent.listen((final folderID) { _$markFolderAsReadEvent.listen((final folderId) {
final stream = requestManager final stream = requestManager
.wrapWithoutCache( .wrapWithoutCache(
() async => client.news.markFolderAsRead( () async => client.news.markFolderAsRead(
folderID, folderId: folderId,
_newestItemId, newestItemId: _newestItemId,
), ),
) )
.asBroadcastStream(); .asBroadcastStream();
@ -182,29 +182,27 @@ class NewsBloc extends $NewsBloc {
void _loadFolders() { void _loadFolders() {
requestManager requestManager
.wrapNextcloud<List<NewsFolder>, NewsListFolders, void, NextcloudNewsClient>( .wrapNextcloud<List<NewsFolder>, NewsListFolders>(
client.id, client.id,
client.news,
'news-folders', 'news-folders',
() async => (await client.news.listFolders())!, () async => client.news.listFolders(),
(final response) => response.folders, (final response) => response.folders!,
previousData: _foldersSubject.valueOrNull?.data, previousData: _foldersSubject.valueOrNull?.data,
) )
.listen(_foldersSubject.add); .listen(_foldersSubject.add);
} }
void _loadFeeds() { void _loadFeeds() {
requestManager.wrapNextcloud<List<NewsFeed>, NewsListFeeds, void, NextcloudNewsClient>( requestManager.wrapNextcloud<List<NewsFeed>, NewsListFeeds>(
client.id, client.id,
client.news,
'news-feeds', 'news-feeds',
() async => (await client.news.listFeeds())!, () async => client.news.listFeeds(),
(final response) { (final response) {
// This is a bit ugly, but IDGAF right now // This is a bit ugly, but IDGAF right now
if (response.newestItemId != null) { if (response.newestItemId != null) {
_newestItemId = response.newestItemId!; _newestItemId = response.newestItemId!;
} }
return response.feeds; return response.feeds!;
}, },
previousData: _feedsSubject.valueOrNull?.data, previousData: _feedsSubject.valueOrNull?.data,
).listen(_feedsSubject.add); ).listen(_feedsSubject.add);

36
packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart

@ -64,31 +64,31 @@ abstract class $NewsBloc extends RxBlocBase implements NewsBlocEvents, NewsBlocS
void refresh({required bool mainArticlesToo}) => _$refreshEvent.add(mainArticlesToo); void refresh({required bool mainArticlesToo}) => _$refreshEvent.add(mainArticlesToo);
@override @override
void addFeed(String url, int? folderID) => _$addFeedEvent.add(_AddFeedEventArgs(url, folderID)); void addFeed(String url, int? folderId) => _$addFeedEvent.add(_AddFeedEventArgs(url, folderId));
@override @override
void removeFeed(int feedID) => _$removeFeedEvent.add(feedID); void removeFeed(int feedId) => _$removeFeedEvent.add(feedId);
@override @override
void renameFeed(int feedID, String name) => _$renameFeedEvent.add(_RenameFeedEventArgs(feedID, name)); void renameFeed(int feedId, String feedTitle) => _$renameFeedEvent.add(_RenameFeedEventArgs(feedId, feedTitle));
@override @override
void moveFeed(int feedID, int? folderID) => _$moveFeedEvent.add(_MoveFeedEventArgs(feedID, folderID)); void moveFeed(int feedId, int? folderId) => _$moveFeedEvent.add(_MoveFeedEventArgs(feedId, folderId));
@override @override
void markFeedAsRead(int feedID) => _$markFeedAsReadEvent.add(feedID); void markFeedAsRead(int feedId) => _$markFeedAsReadEvent.add(feedId);
@override @override
void createFolder(String name) => _$createFolderEvent.add(name); void createFolder(String name) => _$createFolderEvent.add(name);
@override @override
void deleteFolder(int folderID) => _$deleteFolderEvent.add(folderID); void deleteFolder(int folderId) => _$deleteFolderEvent.add(folderId);
@override @override
void renameFolder(int folderID, String name) => _$renameFolderEvent.add(_RenameFolderEventArgs(folderID, name)); void renameFolder(int folderId, String name) => _$renameFolderEvent.add(_RenameFolderEventArgs(folderId, name));
@override @override
void markFolderAsRead(int folderID) => _$markFolderAsReadEvent.add(folderID); void markFolderAsRead(int folderId) => _$markFolderAsReadEvent.add(folderId);
@override @override
BehaviorSubject<Result<List<NewsFolder>>> get folders => _foldersState; BehaviorSubject<Result<List<NewsFolder>>> get folders => _foldersState;
@ -136,39 +136,39 @@ abstract class $NewsBloc extends RxBlocBase implements NewsBlocEvents, NewsBlocS
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for
/// [NewsBlocEvents.addFeed] event /// [NewsBlocEvents.addFeed] event
class _AddFeedEventArgs { class _AddFeedEventArgs {
const _AddFeedEventArgs(this.url, this.folderID); const _AddFeedEventArgs(this.url, this.folderId);
final String url; final String url;
final int? folderID; final int? folderId;
} }
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for
/// [NewsBlocEvents.renameFeed] event /// [NewsBlocEvents.renameFeed] event
class _RenameFeedEventArgs { class _RenameFeedEventArgs {
const _RenameFeedEventArgs(this.feedID, this.name); const _RenameFeedEventArgs(this.feedId, this.feedTitle);
final int feedID; final int feedId;
final String name; final String feedTitle;
} }
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for
/// [NewsBlocEvents.moveFeed] event /// [NewsBlocEvents.moveFeed] event
class _MoveFeedEventArgs { class _MoveFeedEventArgs {
const _MoveFeedEventArgs(this.feedID, this.folderID); const _MoveFeedEventArgs(this.feedId, this.folderId);
final int feedID; final int feedId;
final int? folderID; final int? folderId;
} }
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for
/// [NewsBlocEvents.renameFolder] event /// [NewsBlocEvents.renameFolder] event
class _RenameFolderEventArgs { class _RenameFolderEventArgs {
const _RenameFolderEventArgs(this.folderID, this.name); const _RenameFolderEventArgs(this.folderId, this.name);
final int folderID; final int folderId;
final String name; final String name;
} }

19
packages/neon/lib/src/apps/notes/blocs/notes.dart

@ -13,8 +13,8 @@ abstract class NotesBlocEvents {
void refresh(); void refresh();
void createNote({ void createNote({
final String? title, final String title = '',
final String? category, final String category = '',
}); });
void updateNote( void updateNote(
@ -58,20 +58,20 @@ class NotesBloc extends $NotesBloc {
_$updateNoteEvent.listen((final event) { _$updateNoteEvent.listen((final event) {
_wrapAction( _wrapAction(
() async => _noteUpdateController.add( () async => _noteUpdateController.add(
(await client.notes.updateNote( await client.notes.updateNote(
event.id, id: event.id,
title: event.title, title: event.title,
category: event.category, category: event.category,
content: event.content, content: event.content,
favorite: event.favorite, favorite: event.favorite ?? false ? 1 : 0,
ifMatch: '"${event.etag}"', ifMatch: '"${event.etag}"',
))!, ),
), ),
); );
}); });
_$deleteNoteEvent.listen((final id) { _$deleteNoteEvent.listen((final id) {
_wrapAction(() async => client.notes.deleteNote(id)); _wrapAction(() async => client.notes.deleteNote(id: id));
}); });
_loadNotes(); _loadNotes();
@ -87,11 +87,10 @@ class NotesBloc extends $NotesBloc {
void _loadNotes() { void _loadNotes() {
requestManager requestManager
.wrapNextcloud<List<NotesNote>, List<NotesNote>, NotesNote, NextcloudNotesClient>( .wrapNextcloud<List<NotesNote>, List<NotesNote>>(
client.id, client.id,
client.notes,
'notes-notes', 'notes-notes',
() async => (await client.notes.getNotes())!, () async => client.notes.getNotes(),
(final response) => response, (final response) => response,
previousData: _notesSubject.valueOrNull?.data, previousData: _notesSubject.valueOrNull?.data,
) )

8
packages/neon/lib/src/apps/notes/blocs/notes.rxb.g.dart

@ -43,7 +43,7 @@ abstract class $NotesBloc extends RxBlocBase implements NotesBlocEvents, NotesBl
void refresh() => _$refreshEvent.add(null); void refresh() => _$refreshEvent.add(null);
@override @override
void createNote({String? title, String? category}) => void createNote({String title = '', String category = ''}) =>
_$createNoteEvent.add(_CreateNoteEventArgs(title: title, category: category)); _$createNoteEvent.add(_CreateNoteEventArgs(title: title, category: category));
@override @override
@ -89,11 +89,11 @@ abstract class $NotesBloc extends RxBlocBase implements NotesBlocEvents, NotesBl
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for
/// [NotesBlocEvents.createNote] event /// [NotesBlocEvents.createNote] event
class _CreateNoteEventArgs { class _CreateNoteEventArgs {
const _CreateNoteEventArgs({this.title, this.category}); const _CreateNoteEventArgs({this.title = '', this.category = ''});
final String? title; final String title;
final String? category; final String category;
} }
/// Helps providing the arguments in the [Subject.add] for /// Helps providing the arguments in the [Subject.add] for

2
packages/neon/lib/src/apps/notes/utils/exception_handler.dart

@ -1,7 +1,7 @@
part of '../app.dart'; part of '../app.dart';
void handleNotesException(final BuildContext context, final Exception error) { void handleNotesException(final BuildContext context, final Exception error) {
if (error is ApiException && error.code == 412) { if (error is ApiException && error.statusCode == 412) {
ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).notesNoteChangedOnServer); ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).notesNoteChangedOnServer);
} else { } else {
ExceptionWidget.showSnackbar(context, error); ExceptionWidget.showSnackbar(context, error);

2
packages/neon/lib/src/apps/notes/widgets/notes_view.dart

@ -35,7 +35,7 @@ class NotesView extends StatelessWidget {
if (result != null) { if (result != null) {
bloc.createNote( bloc.createNote(
title: result[0] as String, title: result[0] as String,
category: result[1] as String?, category: result[1] as String? ?? '',
); );
} }
}, },

14
packages/neon/lib/src/apps/notifications/blocs/notifications.dart

@ -12,7 +12,7 @@ part 'notifications.rxb.g.dart';
abstract class NotificationsBlocEvents { abstract class NotificationsBlocEvents {
void refresh(); void refresh();
void deleteNotification(final NotificationsNotification notification); void deleteNotification(final int id);
void deleteAllNotifications(); void deleteAllNotifications();
} }
@ -34,8 +34,8 @@ class NotificationsBloc extends $NotificationsBloc {
) { ) {
_$refreshEvent.listen((final _) => _loadNotifications()); _$refreshEvent.listen((final _) => _loadNotifications());
_$deleteNotificationEvent.listen((final notification) { _$deleteNotificationEvent.listen((final id) {
_wrapAction(() async => client.notifications.deleteNotification(notification.notificationId!)); _wrapAction(() async => client.notifications.deleteNotification(id: id));
}); });
_$deleteAllNotificationsEvent.listen((final notification) { _$deleteAllNotificationsEvent.listen((final notification) {
@ -61,13 +61,11 @@ class NotificationsBloc extends $NotificationsBloc {
void _loadNotifications() { void _loadNotifications() {
requestManager requestManager
.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications, NotificationsNotification, .wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>(
NextcloudNotificationsClient>(
client.id, client.id,
client.notifications,
'notifications-notifications', 'notifications-notifications',
() async => (await client.notifications.listNotifications())!, () async => client.notifications.listNotifications(),
(final response) => response.ocs!.data, (final response) => response.ocs!.data!,
previousData: _notificationsSubject.valueOrNull?.data, previousData: _notificationsSubject.valueOrNull?.data,
) )
.listen(_notificationsSubject.add); .listen(_notificationsSubject.add);

4
packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart

@ -23,7 +23,7 @@ abstract class $NotificationsBloc extends RxBlocBase
final _$refreshEvent = PublishSubject<void>(); final _$refreshEvent = PublishSubject<void>();
/// Тhe [Subject] where events sink to by calling [deleteNotification] /// Тhe [Subject] where events sink to by calling [deleteNotification]
final _$deleteNotificationEvent = PublishSubject<NotificationsNotification>(); final _$deleteNotificationEvent = PublishSubject<int>();
/// Тhe [Subject] where events sink to by calling [deleteAllNotifications] /// Тhe [Subject] where events sink to by calling [deleteAllNotifications]
final _$deleteAllNotificationsEvent = PublishSubject<void>(); final _$deleteAllNotificationsEvent = PublishSubject<void>();
@ -41,7 +41,7 @@ abstract class $NotificationsBloc extends RxBlocBase
void refresh() => _$refreshEvent.add(null); void refresh() => _$refreshEvent.add(null);
@override @override
void deleteNotification(NotificationsNotification notification) => _$deleteNotificationEvent.add(notification); void deleteNotification(int id) => _$deleteNotificationEvent.add(id);
@override @override
void deleteAllNotifications() => _$deleteAllNotificationsEvent.add(null); void deleteAllNotifications() => _$deleteAllNotificationsEvent.add(null);

2
packages/neon/lib/src/apps/notifications/pages/main.dart

@ -144,7 +144,7 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
} }
}, },
onLongPress: () { onLongPress: () {
widget.bloc.deleteNotification(notification); widget.bloc.deleteNotification(notification.notificationId!);
}, },
); );
} }

9
packages/neon/lib/src/blocs/apps.dart

@ -10,7 +10,7 @@ import 'package:rxdart/rxdart.dart';
part 'apps.rxb.g.dart'; part 'apps.rxb.g.dart';
typedef NextcloudApp = CoreNavigationAppsOcsDataInner; typedef NextcloudApp = CoreNavigationAppsOcsData;
abstract class AppsBlocEvents { abstract class AppsBlocEvents {
void refresh(); void refresh();
@ -99,12 +99,11 @@ class AppsBloc extends $AppsBloc {
void _loadApps() { void _loadApps() {
_requestManager _requestManager
.wrapNextcloud<List<NextcloudApp>, CoreNavigationApps, void, NextcloudCoreClient>( .wrapNextcloud<List<NextcloudApp>, CoreNavigationApps>(
_account.client.id, _account.client.id,
_account.client.core,
'apps-apps', 'apps-apps',
() async => (await _account.client.core.getNavigationApps())!, () async => _account.client.core.getNavigationApps(),
(final response) => response.ocs!.data, (final response) => response.ocs!.data!,
preloadCache: true, preloadCache: true,
) )
.listen(_appsSubject.add); .listen(_appsSubject.add);

6
packages/neon/lib/src/blocs/apps.rxb.g.dart

@ -25,7 +25,7 @@ abstract class $AppsBloc extends RxBlocBase implements AppsBlocEvents, AppsBlocS
final _$setActiveAppEvent = PublishSubject<String?>(); final _$setActiveAppEvent = PublishSubject<String?>();
/// The state of [apps] implemented in [_mapToAppsState] /// The state of [apps] implemented in [_mapToAppsState]
late final BehaviorSubject<Result<List<CoreNavigationAppsOcsDataInner>>> _appsState = _mapToAppsState(); late final BehaviorSubject<Result<List<CoreNavigationAppsOcsData>>> _appsState = _mapToAppsState();
/// The state of [appImplementations] implemented in /// The state of [appImplementations] implemented in
/// [_mapToAppImplementationsState] /// [_mapToAppImplementationsState]
@ -42,7 +42,7 @@ abstract class $AppsBloc extends RxBlocBase implements AppsBlocEvents, AppsBlocS
void setActiveApp(String? appID) => _$setActiveAppEvent.add(appID); void setActiveApp(String? appID) => _$setActiveAppEvent.add(appID);
@override @override
BehaviorSubject<Result<List<CoreNavigationAppsOcsDataInner>>> get apps => _appsState; BehaviorSubject<Result<List<CoreNavigationAppsOcsData>>> get apps => _appsState;
@override @override
BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>> get appImplementations => BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>> get appImplementations =>
@ -51,7 +51,7 @@ abstract class $AppsBloc extends RxBlocBase implements AppsBlocEvents, AppsBlocS
@override @override
BehaviorSubject<String?> get activeAppID => _activeAppIDState; BehaviorSubject<String?> get activeAppID => _activeAppIDState;
BehaviorSubject<Result<List<CoreNavigationAppsOcsDataInner>>> _mapToAppsState(); BehaviorSubject<Result<List<CoreNavigationAppsOcsData>>> _mapToAppsState();
BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>> BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>>
_mapToAppImplementationsState(); _mapToAppImplementationsState();

5
packages/neon/lib/src/blocs/capabilities.dart

@ -32,11 +32,10 @@ class CapabilitiesBloc extends $CapabilitiesBloc {
void _loadCapabilities() { void _loadCapabilities() {
_requestManager _requestManager
.wrapNextcloud<CoreServerCapabilitiesOcsData, CoreServerCapabilities, void, NextcloudCoreClient>( .wrapNextcloud<CoreServerCapabilitiesOcsData, CoreServerCapabilities>(
_client.id, _client.id,
_client.core,
'capabilities', 'capabilities',
() async => (await _client.core.getCapabilities())!, () async => _client.core.getCapabilities(),
(final response) => response.ocs!.data!, (final response) => response.ocs!.data!,
preloadCache: true, preloadCache: true,
) )

4
packages/neon/lib/src/blocs/login.dart

@ -39,7 +39,7 @@ class LoginBloc extends $LoginBloc {
userAgentOverride: userAgent(_packageInfo), userAgentOverride: userAgent(_packageInfo),
); );
final status = (await client.core.getStatus())!; final status = await client.core.getStatus();
if (status.maintenance!) { if (status.maintenance!) {
_serverConnectionStateSubject.add(ServerConnectionState.maintenanceMode); _serverConnectionStateSubject.add(ServerConnectionState.maintenanceMode);
return; return;
@ -53,7 +53,7 @@ class LoginBloc extends $LoginBloc {
_cancelPollTimer(); _cancelPollTimer();
_pollTimer = Timer.periodic(const Duration(seconds: 2), (final _) async { _pollTimer = Timer.periodic(const Duration(seconds: 2), (final _) async {
try { try {
final result = await client.core.getLoginFlowResult(init!.poll!.token!); final result = await client.core.getLoginFlowResult(token: init.poll!.token!);
_cancelPollTimer(); _cancelPollTimer();
_loginFlowResultSubject.add(result); _loginFlowResultSubject.add(result);
} catch (e) { } catch (e) {

10
packages/neon/lib/src/blocs/push_notifications.dart

@ -72,11 +72,11 @@ class PushNotificationsBloc extends $PushNotificationsBloc {
proxyServerForClient = 'http://${_env!.testHost}:8080/'; proxyServerForClient = 'http://${_env!.testHost}:8080/';
} }
final subscription = (await account.client.notifications.registerDeviceAtServer( final subscription = await account.client.notifications.registerDevice(
endpoint, pushTokenHash: account.client.notifications.generatePushTokenHash(endpoint),
_keypair!.publicKey, devicePublicKey: _keypair!.publicKey.toFormattedPEM(),
proxyServerForNextcloud, proxyServer: proxyServerForNextcloud,
))!; );
await account.client.notifications.registerDeviceAtPushProxy( await account.client.notifications.registerDeviceAtPushProxy(
endpoint, endpoint,

5
packages/neon/lib/src/blocs/user_details.dart

@ -29,11 +29,10 @@ class UserDetailsBloc extends $UserDetailsBloc {
void _loadUserDetails() { void _loadUserDetails() {
_requestManager _requestManager
.wrapNextcloud<ProvisioningApiUserDetails, ProvisioningApiUser, void, NextcloudProvisioningApiClient>( .wrapNextcloud<ProvisioningApiUserDetails, ProvisioningApiUser>(
_client.id, _client.id,
_client.provisioningApi,
'user-details', 'user-details',
() async => (await _client.provisioningApi.getCurrentUser())!, () async => _client.provisioningApi.getCurrentUser(),
(final response) => response.ocs!.data!, (final response) => response.ocs!.data!,
preloadCache: true, preloadCache: true,
) )

10
packages/neon/lib/src/blocs/user_status.dart

@ -38,13 +38,13 @@ class UserStatusBloc extends $UserStatusBloc {
} }
void _loadUserStatus() { void _loadUserStatus() {
// TODO: Fix for no user status
_requestManager _requestManager
.wrapNextcloud<UserStatus?, UserStatusGetUserStatus, void, NextcloudUserStatusClient>( .wrapNextcloud<UserStatus?, UserStatusGetUserStatus>(
_account.client.id, _account.client.id,
_account.client.userStatus,
'user-status', 'user-status',
() async => (await _account.client.userStatus.getStatus())!, () async => _account.client.userStatus.getStatus(),
(final response) => response.ocs?.data, (final response) => response.ocs?.data?.userStatus,
preloadCache: true, preloadCache: true,
) )
.listen(_userStatusSubject.add); .listen(_userStatusSubject.add);
@ -56,7 +56,7 @@ class UserStatusBloc extends $UserStatusBloc {
// TODO: https://github.com/jld3103/nextcloud-neon/issues/10 // TODO: https://github.com/jld3103/nextcloud-neon/issues/10
// ignore: dead_code // ignore: dead_code
try { try {
await _account.client.userStatus.heartbeat(UserStatusTypeEnum.online); await _account.client.userStatus.heartbeat(status: UserStatusType.online);
} catch (e) { } catch (e) {
debugPrint(e.toString()); debugPrint(e.toString());
} }

32
packages/neon/lib/src/models/nextcloud_notification.dart

@ -1,32 +0,0 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:nextcloud/nextcloud.dart';
part 'nextcloud_notification.g.dart';
@JsonSerializable()
class NextcloudNotification {
NextcloudNotification({
required this.accountID,
required this.priority,
required this.type,
required this.subject,
});
factory NextcloudNotification.fromJson(final Map<String, dynamic> json) => _$NextcloudNotificationFromJson(json);
Map<String, dynamic> toJson() => _$NextcloudNotificationToJson(this);
final String accountID;
final String priority;
final String type;
@JsonKey(
fromJson: _fromJsonSubject,
toJson: _toJsonSubject,
)
final NotificationsPushNotificationDecryptedSubject subject;
}
NotificationsPushNotificationDecryptedSubject _fromJsonSubject(final Map<String, dynamic> data) =>
NotificationsPushNotificationDecryptedSubject.fromJson(data)!;
Map<String, dynamic>? _toJsonSubject(final NotificationsPushNotificationDecryptedSubject subject) => subject.toJson();

21
packages/neon/lib/src/models/nextcloud_notification.g.dart

@ -1,21 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'nextcloud_notification.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
NextcloudNotification _$NextcloudNotificationFromJson(Map<String, dynamic> json) => NextcloudNotification(
accountID: json['accountID'] as String,
priority: json['priority'] as String,
type: json['type'] as String,
subject: _fromJsonSubject(json['subject'] as Map<String, dynamic>),
);
Map<String, dynamic> _$NextcloudNotificationToJson(NextcloudNotification instance) => <String, dynamic>{
'accountID': instance.accountID,
'priority': instance.priority,
'type': instance.type,
'subject': _toJsonSubject(instance.subject),
};

1
packages/neon/lib/src/neon.dart

@ -32,7 +32,6 @@ import 'package:neon/src/blocs/login.dart';
import 'package:neon/src/blocs/user_details.dart'; import 'package:neon/src/blocs/user_details.dart';
import 'package:neon/src/blocs/user_status.dart'; import 'package:neon/src/blocs/user_status.dart';
import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/nextcloud_notification.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;

10
packages/neon/lib/src/pages/home/home.dart

@ -165,13 +165,13 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
}; };
Global.onPushNotificationClicked = (final payload) async { Global.onPushNotificationClicked = (final payload) async {
if (payload != null) { if (payload != null) {
final notification = NextcloudNotification.fromJson(json.decode(payload) as Map<String, dynamic>); final notification = NotificationsPushNotification.fromJson(json.decode(payload) as Map<String, dynamic>);
debugPrint('onNotificationClicked: ${notification.subject}'); debugPrint('onNotificationClicked: ${notification.subject}');
final allAppImplementations = Provider.of<List<AppImplementation>>(context, listen: false); final allAppImplementations = Provider.of<List<AppImplementation>>(context, listen: false);
final matchingAppImplementations = final matchingAppImplementations =
allAppImplementations.where((final a) => a.id == notification.subject.app); allAppImplementations.where((final a) => a.id == notification.subject!.app);
late AppImplementation appImplementation; late AppImplementation appImplementation;
if (matchingAppImplementations.isNotEmpty) { if (matchingAppImplementations.isNotEmpty) {
@ -181,11 +181,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
} }
if (appImplementation.id != 'notifications') { if (appImplementation.id != 'notifications') {
_appsBloc.getAppBloc<NotificationsBloc>(appImplementation).deleteNotification( _appsBloc.getAppBloc<NotificationsBloc>(appImplementation).deleteNotification(notification.subject!.nid!);
NotificationsNotification(
notificationId: notification.subject.nid,
),
);
} }
await _openAppFromExternal(appImplementation.id); await _openAppFromExternal(appImplementation.id);
} }

2
packages/neon/lib/src/pages/home/widgets/server_status.dart

@ -19,7 +19,7 @@ class _ServerStatusState extends State<ServerStatus> {
WidgetsBinding.instance.addPostFrameCallback((final _) async { WidgetsBinding.instance.addPostFrameCallback((final _) async {
try { try {
final status = (await widget.account.client.core.getStatus())!; final status = await widget.account.client.core.getStatus();
if (status.maintenance! && mounted) { if (status.maintenance! && mounted) {
ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorServerInMaintenanceMode); ExceptionWidget.showSnackbar(context, AppLocalizations.of(context).errorServerInMaintenanceMode);
} }

14
packages/neon/lib/src/utils/push_utils.dart

@ -49,18 +49,18 @@ class PushUtils {
final keypair = await loadRSAKeypair(Storage('notifications', sharedPreferences)); final keypair = await loadRSAKeypair(Storage('notifications', sharedPreferences));
final data = json.decode(utf8.decode(message)) as Map<String, dynamic>; final data = json.decode(utf8.decode(message)) as Map<String, dynamic>;
final notification = NextcloudNotification( final notification = NotificationsPushNotification(
accountID: instance, accountID: instance,
priority: data['priority']! as String, priority: data['priority']! as String,
type: data['type']! as String, type: data['type']! as String,
subject: decryptPushNotificationSubject(keypair.privateKey, data['subject']! as String), subject: decryptPushNotificationSubject(keypair.privateKey, data['subject']! as String),
); );
if (notification.subject.delete ?? false) { if (notification.subject!.delete ?? false) {
await localNotificationsPlugin.cancel(_getNotificationID(instance, notification)); await localNotificationsPlugin.cancel(_getNotificationID(instance, notification));
return; return;
} }
if (notification.subject.deleteAll ?? false) { if (notification.subject!.deleteAll ?? false) {
await localNotificationsPlugin.cancelAll(); await localNotificationsPlugin.cancelAll();
return; return;
} }
@ -80,7 +80,7 @@ class PushUtils {
final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform); final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform);
final matchingAppImplementations = final matchingAppImplementations =
allAppImplementations.where((final a) => a.id == notification.subject.app!).toList(); allAppImplementations.where((final a) => a.id == notification.subject!.app!).toList();
late AppImplementation app; late AppImplementation app;
if (matchingAppImplementations.isNotEmpty) { if (matchingAppImplementations.isNotEmpty) {
app = matchingAppImplementations.single; app = matchingAppImplementations.single;
@ -93,7 +93,7 @@ class PushUtils {
await localNotificationsPlugin.show( await localNotificationsPlugin.show(
_getNotificationID(instance, notification), _getNotificationID(instance, notification),
appName, appName,
notification.subject.subject!, notification.subject!.subject!,
NotificationDetails( NotificationDetails(
android: AndroidNotificationDetails( android: AndroidNotificationDetails(
app.id, app.id,
@ -120,7 +120,7 @@ class PushUtils {
static int _getNotificationID( static int _getNotificationID(
final String instance, final String instance,
final NextcloudNotification notification, final NotificationsPushNotification notification,
) => ) =>
sha256.convert(utf8.encode('$instance${notification.subject.nid!}')).bytes.reduce((final a, final b) => a + b); sha256.convert(utf8.encode('$instance${notification.subject!.nid!}')).bytes.reduce((final a, final b) => a + b);
} }

49
packages/neon/lib/src/utils/request_manager.dart

@ -28,9 +28,8 @@ class RequestManager {
} }
} }
Stream<Result<T>> wrapNextcloud<T, R, U, S extends ApiInstance>( Stream<Result<T>> wrapNextcloud<T, R>(
final String clientID, final String clientID,
final S apiInstance,
final String k, final String k,
final Future<R> Function() call, final Future<R> Function() call,
final T Function(R) unwrap, { final T Function(R) unwrap, {
@ -39,30 +38,11 @@ class RequestManager {
final bool disableTimeout = false, final bool disableTimeout = false,
final T? previousData, final T? previousData,
}) async* { }) async* {
if (R.toString().endsWith('?')) {
final e = Exception('You can not request a nullable type ${R.toString()}');
debugPrint(e.toString());
yield Result.error(e);
return;
}
if (R is List && R != List<U>) {
final e = Exception('$R is not List<$U> as expected');
debugPrint(e.toString());
yield Result.error(e);
return;
}
yield* _wrap<T, R>( yield* _wrap<T, R>(
clientID, clientID,
k, k,
apiInstance.apiClient.serializeAsync, (final s) => json.encode(serialize<R>(s)),
(final d) async { (final d) => deserialize<R>(json.decode(d)),
var a = await apiInstance.apiClient.deserializeAsync(d, R.toString());
if (a is List) {
a = a.map((final b) => b as U).toList();
}
return a as R;
},
call, call,
unwrap: unwrap, unwrap: unwrap,
preloadCache: preloadCache, preloadCache: preloadCache,
@ -84,8 +64,8 @@ class RequestManager {
_wrap<Uint8List, Uint8List>( _wrap<Uint8List, Uint8List>(
clientID, clientID,
k, k,
(final s) async => base64.encode(s), (final s) => base64.encode(s),
(final d) async => base64.decode(d), (final d) => base64.decode(d),
call, call,
preloadCache: preloadCache, preloadCache: preloadCache,
preferCache: preferCache, preferCache: preferCache,
@ -105,20 +85,19 @@ class RequestManager {
_wrap<String, String>( _wrap<String, String>(
clientID, clientID,
k, k,
(final s) async => s, (final s) => s,
(final d) async => d, (final d) => d,
call, call,
preloadCache: preloadCache, preloadCache: preloadCache,
preferCache: preferCache, preferCache: preferCache,
previousData: previousData, previousData: previousData,
disableTimeout: disableTimeout, disableTimeout: disableTimeout,
); );
Stream<Result<T>> _wrap<T, R>( Stream<Result<T>> _wrap<T, R>(
final String clientID, final String clientID,
final String k, final String k,
final Future<String> Function(R) serialize, final String Function(R) serialize,
final Future<R> Function(String) deserialize, final R Function(String) deserialize,
final Future<R> Function() call, { final Future<R> Function() call, {
final bool preloadCache = false, final bool preloadCache = false,
final bool preferCache = false, final bool preferCache = false,
@ -140,7 +119,7 @@ class RequestManager {
if ((preferCache || preloadCache) && cache != null && await cache!.has(key)) { if ((preferCache || preloadCache) && cache != null && await cache!.has(key)) {
_print('[Cache]: $k'); _print('[Cache]: $k');
final s = unwrap(await deserialize((await cache!.get(key))!)); final s = unwrap(deserialize((await cache!.get(key))!));
if (preloadCache) { if (preloadCache) {
yield ResultCached(s, loading: true); yield ResultCached(s, loading: true);
} else { } else {
@ -152,7 +131,7 @@ class RequestManager {
try { try {
final response = await _timeout(disableTimeout, call); final response = await _timeout(disableTimeout, call);
final s = await serialize(response); final s = serialize(response);
_print('[Response]: $k'); _print('[Response]: $k');
await cache?.set(key, s); await cache?.set(key, s);
@ -161,7 +140,7 @@ class RequestManager {
if (cache != null && await cache!.has(key)) { if (cache != null && await cache!.has(key)) {
_print('[Cache]: $k'); _print('[Cache]: $k');
debugPrint(e.toString()); debugPrint(e.toString());
yield ResultCached(unwrap(await deserialize((await cache!.get(key))!)), error: e); yield ResultCached(unwrap(deserialize((await cache!.get(key))!)), error: e);
return; return;
} }
_print('[Failure]: $k'); _print('[Failure]: $k');
@ -282,7 +261,3 @@ extension ResultDataError<T> on Result<T> {
return null; return null;
} }
} }
extension ListAs<T, V> on List<T> {
List as<U extends List<V>>() => map((final e) => e as V).toList();
}

2
packages/neon/lib/src/utils/theme.dart

@ -4,7 +4,7 @@ const themePrimaryColor = Color(0xFFF37736);
const themeOnPrimaryColor = Color(0xFFFFFFFF); const themeOnPrimaryColor = Color(0xFFFFFFFF);
ThemeData getThemeFromNextcloudTheme( ThemeData getThemeFromNextcloudTheme(
final NextcloudTheme? nextcloudTheme, final CoreServerCapabilitiesOcsDataCapabilitiesTheming? nextcloudTheme,
final ThemeMode themeMode, final ThemeMode themeMode,
final Brightness platformBrightness, { final Brightness platformBrightness, {
required final bool oledAsDark, required final bool oledAsDark,

26
packages/neon/lib/src/widgets/account_avatar.dart

@ -20,10 +20,10 @@ class AccountAvatar extends StatelessWidget {
stream: requestManager.wrapBytes( stream: requestManager.wrapBytes(
account.client.id, account.client.id,
'accounts-avatar-${account.id}', 'accounts-avatar-${account.id}',
() async => (await account.client.core.getAvatarBytes( () async => account.client.core.getAvatar(
account.username, userId: account.username,
(kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt(), size: (kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt(),
))!, ),
preferCache: true, preferCache: true,
), ),
builder: ( builder: (
@ -78,8 +78,8 @@ class AccountAvatar extends StatelessWidget {
: BoxDecoration( : BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: _userStatusToColor(userStatusData), color: _userStatusToColor(userStatusData),
border: userStatusData.status != UserStatusTypeEnum.offline && border: userStatusData.status != UserStatusType.offline &&
userStatusData.status != UserStatusTypeEnum.invisible userStatusData.status != UserStatusType.invisible
? Border.all( ? Border.all(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
) )
@ -90,7 +90,8 @@ class AccountAvatar extends StatelessWidget {
strokeWidth: 1.5, strokeWidth: 1.5,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
) )
: userStatusError != null && (userStatusError is! ApiException || userStatusError.code != 404) : userStatusError != null &&
(userStatusError is! ApiException || userStatusError.statusCode != 404)
? const Icon( ? const Icon(
Icons.error_outline, Icons.error_outline,
size: kAvatarSize / 3, size: kAvatarSize / 3,
@ -106,17 +107,14 @@ class AccountAvatar extends StatelessWidget {
Color _userStatusToColor(final UserStatus userStatus) { Color _userStatusToColor(final UserStatus userStatus) {
switch (userStatus.status) { switch (userStatus.status) {
case UserStatusTypeEnum.online: case UserStatusType.online:
return const Color(0xFF49B382); return const Color(0xFF49B382);
case UserStatusTypeEnum.away: case UserStatusType.away:
return const Color(0xFFF4A331); return const Color(0xFFF4A331);
case UserStatusTypeEnum.dnd: case UserStatusType.dnd:
return const Color(0xFFED484C); return const Color(0xFFED484C);
case UserStatusTypeEnum.invisible: default:
case UserStatusTypeEnum.offline:
return Colors.transparent; return Colors.transparent;
} }
return Colors.transparent;
} }
} }

2
packages/neon/lib/src/widgets/cached_url_image.dart

@ -35,7 +35,7 @@ class CachedURLImage extends StatelessWidget {
'image-${base64.encode(url.codeUnits)}', 'image-${base64.encode(url.codeUnits)}',
() async => (await http.get( () async => (await http.get(
Uri.parse(url), Uri.parse(url),
headers: client.commonHeaders, headers: client.baseHeaders,
)) ))
.bodyBytes, .bodyBytes,
preferCache: true, preferCache: true,

36
packages/neon/lib/src/widgets/exception.dart

@ -89,43 +89,40 @@ class ExceptionWidget extends StatelessWidget {
} }
if (exception is ApiException) { if (exception is ApiException) {
if (exception.code == 401) { if (exception.statusCode == 401) {
return _ExceptionDetails( return _ExceptionDetails(
text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch, text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch,
isUnauthorized: true, isUnauthorized: true,
); );
} }
if (exception.code >= 500 && exception.code <= 599) { if (exception.statusCode >= 500 && exception.statusCode <= 599) {
return _ExceptionDetails( return _ExceptionDetails(
text: AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest, text: AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest,
); );
} }
} }
final socketException = _getSpecificExceptionForException<SocketException>(exception); if (exception is SocketException) {
if (socketException != null) {
return _ExceptionDetails( return _ExceptionDetails(
text: socketException.address != null text: exception.address != null
? AppLocalizations.of(context).errorUnableToReachServerAt(socketException.address!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(exception.address!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
final clientException = _getSpecificExceptionForException<ClientException>(exception); if (exception is ClientException) {
if (clientException != null) {
return _ExceptionDetails( return _ExceptionDetails(
text: clientException.uri != null text: exception.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(clientException.uri!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(exception.uri!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
final httpException = _getSpecificExceptionForException<HttpException>(exception); if (exception is HttpException) {
if (httpException != null) {
return _ExceptionDetails( return _ExceptionDetails(
text: httpException.uri != null text: exception.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(httpException.uri!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(exception.uri!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
@ -141,17 +138,6 @@ class ExceptionWidget extends StatelessWidget {
); );
} }
static T? _getSpecificExceptionForException<T extends Exception>(final dynamic exception) {
if (exception is T) {
return exception;
}
if (exception is ApiException && exception.innerException != null && exception.innerException is T) {
return exception.innerException! as T;
}
return null;
}
static Future _openLoginPage(final BuildContext context) async { static Future _openLoginPage(final BuildContext context) async {
await Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(

Loading…
Cancel
Save