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 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(
nasaFeedURL,
folderId: folder!.folders.single.id,
url: nasaFeedURL,
folderId: folder.folders!.single.id,
);
await pumpAppPage(
@ -449,8 +449,8 @@ Future main() async {
testWidgets('notifications', (final tester) async {
await account.client.notifications.sendAdminNotification(
account.username,
'Notifications demo',
userId: account.username,
shortMessage: 'Notifications demo',
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:neon/l10n/localizations.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/capabilities.dart';
import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -32,7 +32,7 @@ class NeonApp extends StatefulWidget {
// ignore: prefer_mixin
class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver {
final _navigatorKey = GlobalKey<NavigatorState>();
NextcloudTheme? _userTheme;
CoreServerCapabilitiesOcsDataCapabilitiesTheming? _userTheme;
final _platformBrightness = BehaviorSubject<Brightness>.seeded(
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(
account.client.id,
'files-preview-${details.etag}-$width-$height',
() async => (await account.client.core.getPreviewBytes(
details.path.join('/'),
width: width,
height: height,
))!,
() async => account.client.core.getPreview(
file: details.path.join('/'),
x: width,
y: height,
),
preferCache: true,
);

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

@ -70,29 +70,39 @@ class NewsArticlesBloc extends $NewsArticlesBloc {
_$markArticleAsReadEvent.listen((final article) {
_wrapArticleAction((final client) async {
await client.news.markArticleAsRead(article.id!);
_articleUpdateController.add(article..unread = false);
await client.news.markArticleAsRead(itemId: article.id!);
// TODO
//_articleUpdateController.add(article..unread = false);
});
});
_$markArticleAsUnreadEvent.listen((final article) {
_wrapArticleAction((final client) async {
await client.news.markArticleAsUnread(article.id!);
_articleUpdateController.add(article..unread = true);
await client.news.markArticleAsUnread(itemId: article.id!);
// TODO
//_articleUpdateController.add(article..unread = true);
});
});
_$starArticleEvent.listen((final article) {
_wrapArticleAction((final client) async {
await client.news.starArticle(article.feedId!, article.guidHash!);
_articleUpdateController.add(article..starred = true);
await client.news.starArticle(
feedId: article.feedId!,
guidHash: article.guidHash!,
);
// TODO
//_articleUpdateController.add(article..starred = true);
});
});
_$unstarArticleEvent.listen((final article) {
_wrapArticleAction((final client) async {
await client.news.unstarArticle(article.feedId!, article.guidHash!);
_articleUpdateController.add(article..starred = false);
await client.news.unstarArticle(
feedId: article.feedId!,
guidHash: article.guidHash!,
);
// TODO
//_articleUpdateController.add(article..starred = false);
});
});
@ -149,16 +159,15 @@ class NewsArticlesBloc extends $NewsArticlesBloc {
}
newsBloc.requestManager
.wrapNextcloud<List<NewsArticle>, NewsListArticles, void, NextcloudNewsClient>(
.wrapNextcloud<List<NewsArticle>, NewsListArticles>(
newsBloc.client.id,
newsBloc.client.news,
'news-articles-$type-$id-$getRead',
() async => (await newsBloc.client.news.listArticles(
() async => newsBloc.client.news.listArticles(
type: type,
id: id,
getRead: getRead,
))!,
(final response) => response.items,
id: id ?? 0,
getRead: getRead ?? true ? 1 : 0,
),
(final response) => response.items!,
previousData: _articlesSubject.valueOrNull?.data,
)
.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 {
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 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 {
@ -54,21 +54,21 @@ class NewsBloc extends $NewsBloc {
_$addFeedEvent.listen((final event) {
_wrapFeedAction(
(final client) async => client.news.addFeed(
event.url,
folderId: event.folderID,
url: event.url,
folderId: event.folderId,
),
);
});
_$removeFeedEvent.listen((final feedID) {
_wrapFeedAction((final client) async => client.news.deleteFeed(feedID));
_$removeFeedEvent.listen((final feedId) {
_wrapFeedAction((final client) async => client.news.deleteFeed(feedId: feedId));
});
_$renameFeedEvent.listen((final event) {
_wrapFeedAction(
(final client) async => client.news.renameFeed(
event.feedID,
event.name,
feedId: event.feedId,
feedTitle: event.feedTitle,
),
);
});
@ -77,8 +77,8 @@ class NewsBloc extends $NewsBloc {
final stream = requestManager
.wrapWithoutCache(
() async => client.news.moveFeed(
event.feedID,
folderId: event.folderID,
feedId: event.feedId,
folderId: event.folderId,
),
)
.asBroadcastStream();
@ -89,12 +89,12 @@ class NewsBloc extends $NewsBloc {
});
});
_$markFeedAsReadEvent.listen((final feedID) {
_$markFeedAsReadEvent.listen((final feedId) {
final stream = requestManager
.wrapWithoutCache(
() async => client.news.markFeedAsRead(
feedID,
_newestItemId,
feedId: feedId,
newestItemId: _newestItemId,
),
)
.asBroadcastStream();
@ -105,28 +105,28 @@ class NewsBloc extends $NewsBloc {
});
_$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) {
_wrapFolderAction((final client) async => client.news.deleteFolder(folderID));
_$deleteFolderEvent.listen((final folderId) {
_wrapFolderAction((final client) async => client.news.deleteFolder(folderId: folderId));
});
_$renameFolderEvent.listen((final event) {
_wrapFolderAction(
(final client) async => client.news.renameFolder(
event.folderID,
event.name,
folderId: event.folderId,
name: event.name,
),
);
});
_$markFolderAsReadEvent.listen((final folderID) {
_$markFolderAsReadEvent.listen((final folderId) {
final stream = requestManager
.wrapWithoutCache(
() async => client.news.markFolderAsRead(
folderID,
_newestItemId,
folderId: folderId,
newestItemId: _newestItemId,
),
)
.asBroadcastStream();
@ -182,29 +182,27 @@ class NewsBloc extends $NewsBloc {
void _loadFolders() {
requestManager
.wrapNextcloud<List<NewsFolder>, NewsListFolders, void, NextcloudNewsClient>(
.wrapNextcloud<List<NewsFolder>, NewsListFolders>(
client.id,
client.news,
'news-folders',
() async => (await client.news.listFolders())!,
(final response) => response.folders,
() async => client.news.listFolders(),
(final response) => response.folders!,
previousData: _foldersSubject.valueOrNull?.data,
)
.listen(_foldersSubject.add);
}
void _loadFeeds() {
requestManager.wrapNextcloud<List<NewsFeed>, NewsListFeeds, void, NextcloudNewsClient>(
requestManager.wrapNextcloud<List<NewsFeed>, NewsListFeeds>(
client.id,
client.news,
'news-feeds',
() async => (await client.news.listFeeds())!,
() async => client.news.listFeeds(),
(final response) {
// This is a bit ugly, but IDGAF right now
if (response.newestItemId != null) {
_newestItemId = response.newestItemId!;
}
return response.feeds;
return response.feeds!;
},
previousData: _feedsSubject.valueOrNull?.data,
).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);
@override
void addFeed(String url, int? folderID) => _$addFeedEvent.add(_AddFeedEventArgs(url, folderID));
void addFeed(String url, int? folderId) => _$addFeedEvent.add(_AddFeedEventArgs(url, folderId));
@override
void removeFeed(int feedID) => _$removeFeedEvent.add(feedID);
void removeFeed(int feedId) => _$removeFeedEvent.add(feedId);
@override
void renameFeed(int feedID, String name) => _$renameFeedEvent.add(_RenameFeedEventArgs(feedID, name));
void renameFeed(int feedId, String feedTitle) => _$renameFeedEvent.add(_RenameFeedEventArgs(feedId, feedTitle));
@override
void moveFeed(int feedID, int? folderID) => _$moveFeedEvent.add(_MoveFeedEventArgs(feedID, folderID));
void moveFeed(int feedId, int? folderId) => _$moveFeedEvent.add(_MoveFeedEventArgs(feedId, folderId));
@override
void markFeedAsRead(int feedID) => _$markFeedAsReadEvent.add(feedID);
void markFeedAsRead(int feedId) => _$markFeedAsReadEvent.add(feedId);
@override
void createFolder(String name) => _$createFolderEvent.add(name);
@override
void deleteFolder(int folderID) => _$deleteFolderEvent.add(folderID);
void deleteFolder(int folderId) => _$deleteFolderEvent.add(folderId);
@override
void renameFolder(int folderID, String name) => _$renameFolderEvent.add(_RenameFolderEventArgs(folderID, name));
void renameFolder(int folderId, String name) => _$renameFolderEvent.add(_RenameFolderEventArgs(folderId, name));
@override
void markFolderAsRead(int folderID) => _$markFolderAsReadEvent.add(folderID);
void markFolderAsRead(int folderId) => _$markFolderAsReadEvent.add(folderId);
@override
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
/// [NewsBlocEvents.addFeed] event
class _AddFeedEventArgs {
const _AddFeedEventArgs(this.url, this.folderID);
const _AddFeedEventArgs(this.url, this.folderId);
final String url;
final int? folderID;
final int? folderId;
}
/// Helps providing the arguments in the [Subject.add] for
/// [NewsBlocEvents.renameFeed] event
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
/// [NewsBlocEvents.moveFeed] event
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
/// [NewsBlocEvents.renameFolder] event
class _RenameFolderEventArgs {
const _RenameFolderEventArgs(this.folderID, this.name);
const _RenameFolderEventArgs(this.folderId, this.name);
final int folderID;
final int folderId;
final String name;
}

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

@ -13,8 +13,8 @@ abstract class NotesBlocEvents {
void refresh();
void createNote({
final String? title,
final String? category,
final String title = '',
final String category = '',
});
void updateNote(
@ -58,20 +58,20 @@ class NotesBloc extends $NotesBloc {
_$updateNoteEvent.listen((final event) {
_wrapAction(
() async => _noteUpdateController.add(
(await client.notes.updateNote(
event.id,
await client.notes.updateNote(
id: event.id,
title: event.title,
category: event.category,
content: event.content,
favorite: event.favorite,
favorite: event.favorite ?? false ? 1 : 0,
ifMatch: '"${event.etag}"',
))!,
),
),
);
});
_$deleteNoteEvent.listen((final id) {
_wrapAction(() async => client.notes.deleteNote(id));
_wrapAction(() async => client.notes.deleteNote(id: id));
});
_loadNotes();
@ -87,11 +87,10 @@ class NotesBloc extends $NotesBloc {
void _loadNotes() {
requestManager
.wrapNextcloud<List<NotesNote>, List<NotesNote>, NotesNote, NextcloudNotesClient>(
.wrapNextcloud<List<NotesNote>, List<NotesNote>>(
client.id,
client.notes,
'notes-notes',
() async => (await client.notes.getNotes())!,
() async => client.notes.getNotes(),
(final response) => response,
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);
@override
void createNote({String? title, String? category}) =>
void createNote({String title = '', String category = ''}) =>
_$createNoteEvent.add(_CreateNoteEventArgs(title: title, category: category));
@override
@ -89,11 +89,11 @@ abstract class $NotesBloc extends RxBlocBase implements NotesBlocEvents, NotesBl
/// Helps providing the arguments in the [Subject.add] for
/// [NotesBlocEvents.createNote] event
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

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

@ -1,7 +1,7 @@
part of '../app.dart';
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);
} else {
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) {
bloc.createNote(
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 {
void refresh();
void deleteNotification(final NotificationsNotification notification);
void deleteNotification(final int id);
void deleteAllNotifications();
}
@ -34,8 +34,8 @@ class NotificationsBloc extends $NotificationsBloc {
) {
_$refreshEvent.listen((final _) => _loadNotifications());
_$deleteNotificationEvent.listen((final notification) {
_wrapAction(() async => client.notifications.deleteNotification(notification.notificationId!));
_$deleteNotificationEvent.listen((final id) {
_wrapAction(() async => client.notifications.deleteNotification(id: id));
});
_$deleteAllNotificationsEvent.listen((final notification) {
@ -61,13 +61,11 @@ class NotificationsBloc extends $NotificationsBloc {
void _loadNotifications() {
requestManager
.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications, NotificationsNotification,
NextcloudNotificationsClient>(
.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>(
client.id,
client.notifications,
'notifications-notifications',
() async => (await client.notifications.listNotifications())!,
(final response) => response.ocs!.data,
() async => client.notifications.listNotifications(),
(final response) => response.ocs!.data!,
previousData: _notificationsSubject.valueOrNull?.data,
)
.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>();
/// Т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]
final _$deleteAllNotificationsEvent = PublishSubject<void>();
@ -41,7 +41,7 @@ abstract class $NotificationsBloc extends RxBlocBase
void refresh() => _$refreshEvent.add(null);
@override
void deleteNotification(NotificationsNotification notification) => _$deleteNotificationEvent.add(notification);
void deleteNotification(int id) => _$deleteNotificationEvent.add(id);
@override
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: () {
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';
typedef NextcloudApp = CoreNavigationAppsOcsDataInner;
typedef NextcloudApp = CoreNavigationAppsOcsData;
abstract class AppsBlocEvents {
void refresh();
@ -99,12 +99,11 @@ class AppsBloc extends $AppsBloc {
void _loadApps() {
_requestManager
.wrapNextcloud<List<NextcloudApp>, CoreNavigationApps, void, NextcloudCoreClient>(
.wrapNextcloud<List<NextcloudApp>, CoreNavigationApps>(
_account.client.id,
_account.client.core,
'apps-apps',
() async => (await _account.client.core.getNavigationApps())!,
(final response) => response.ocs!.data,
() async => _account.client.core.getNavigationApps(),
(final response) => response.ocs!.data!,
preloadCache: true,
)
.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?>();
/// 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
/// [_mapToAppImplementationsState]
@ -42,7 +42,7 @@ abstract class $AppsBloc extends RxBlocBase implements AppsBlocEvents, AppsBlocS
void setActiveApp(String? appID) => _$setActiveAppEvent.add(appID);
@override
BehaviorSubject<Result<List<CoreNavigationAppsOcsDataInner>>> get apps => _appsState;
BehaviorSubject<Result<List<CoreNavigationAppsOcsData>>> get apps => _appsState;
@override
BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>> get appImplementations =>
@ -51,7 +51,7 @@ abstract class $AppsBloc extends RxBlocBase implements AppsBlocEvents, AppsBlocS
@override
BehaviorSubject<String?> get activeAppID => _activeAppIDState;
BehaviorSubject<Result<List<CoreNavigationAppsOcsDataInner>>> _mapToAppsState();
BehaviorSubject<Result<List<CoreNavigationAppsOcsData>>> _mapToAppsState();
BehaviorSubject<Result<List<AppImplementation<RxBlocBase, NextcloudAppSpecificOptions>>>>
_mapToAppImplementationsState();

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

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

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

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

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

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

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

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

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

@ -38,13 +38,13 @@ class UserStatusBloc extends $UserStatusBloc {
}
void _loadUserStatus() {
// TODO: Fix for no user status
_requestManager
.wrapNextcloud<UserStatus?, UserStatusGetUserStatus, void, NextcloudUserStatusClient>(
.wrapNextcloud<UserStatus?, UserStatusGetUserStatus>(
_account.client.id,
_account.client.userStatus,
'user-status',
() async => (await _account.client.userStatus.getStatus())!,
(final response) => response.ocs?.data,
() async => _account.client.userStatus.getStatus(),
(final response) => response.ocs?.data?.userStatus,
preloadCache: true,
)
.listen(_userStatusSubject.add);
@ -56,7 +56,7 @@ class UserStatusBloc extends $UserStatusBloc {
// TODO: https://github.com/jld3103/nextcloud-neon/issues/10
// ignore: dead_code
try {
await _account.client.userStatus.heartbeat(UserStatusTypeEnum.online);
await _account.client.userStatus.heartbeat(status: UserStatusType.online);
} catch (e) {
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_status.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/nextcloud_notification.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:package_info_plus/package_info_plus.dart';
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 {
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}');
final allAppImplementations = Provider.of<List<AppImplementation>>(context, listen: false);
final matchingAppImplementations =
allAppImplementations.where((final a) => a.id == notification.subject.app);
allAppImplementations.where((final a) => a.id == notification.subject!.app);
late AppImplementation appImplementation;
if (matchingAppImplementations.isNotEmpty) {
@ -181,11 +181,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
}
if (appImplementation.id != 'notifications') {
_appsBloc.getAppBloc<NotificationsBloc>(appImplementation).deleteNotification(
NotificationsNotification(
notificationId: notification.subject.nid,
),
);
_appsBloc.getAppBloc<NotificationsBloc>(appImplementation).deleteNotification(notification.subject!.nid!);
}
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 {
try {
final status = (await widget.account.client.core.getStatus())!;
final status = await widget.account.client.core.getStatus();
if (status.maintenance! && mounted) {
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 data = json.decode(utf8.decode(message)) as Map<String, dynamic>;
final notification = NextcloudNotification(
final notification = NotificationsPushNotification(
accountID: instance,
priority: data['priority']! as String,
type: data['type']! 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));
return;
}
if (notification.subject.deleteAll ?? false) {
if (notification.subject!.deleteAll ?? false) {
await localNotificationsPlugin.cancelAll();
return;
}
@ -80,7 +80,7 @@ class PushUtils {
final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform);
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;
if (matchingAppImplementations.isNotEmpty) {
app = matchingAppImplementations.single;
@ -93,7 +93,7 @@ class PushUtils {
await localNotificationsPlugin.show(
_getNotificationID(instance, notification),
appName,
notification.subject.subject!,
notification.subject!.subject!,
NotificationDetails(
android: AndroidNotificationDetails(
app.id,
@ -120,7 +120,7 @@ class PushUtils {
static int _getNotificationID(
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 S apiInstance,
final String k,
final Future<R> Function() call,
final T Function(R) unwrap, {
@ -39,30 +38,11 @@ class RequestManager {
final bool disableTimeout = false,
final T? previousData,
}) 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>(
clientID,
k,
apiInstance.apiClient.serializeAsync,
(final d) async {
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;
},
(final s) => json.encode(serialize<R>(s)),
(final d) => deserialize<R>(json.decode(d)),
call,
unwrap: unwrap,
preloadCache: preloadCache,
@ -84,8 +64,8 @@ class RequestManager {
_wrap<Uint8List, Uint8List>(
clientID,
k,
(final s) async => base64.encode(s),
(final d) async => base64.decode(d),
(final s) => base64.encode(s),
(final d) => base64.decode(d),
call,
preloadCache: preloadCache,
preferCache: preferCache,
@ -105,20 +85,19 @@ class RequestManager {
_wrap<String, String>(
clientID,
k,
(final s) async => s,
(final d) async => d,
(final s) => s,
(final d) => d,
call,
preloadCache: preloadCache,
preferCache: preferCache,
previousData: previousData,
disableTimeout: disableTimeout,
);
Stream<Result<T>> _wrap<T, R>(
final String clientID,
final String k,
final Future<String> Function(R) serialize,
final Future<R> Function(String) deserialize,
final String Function(R) serialize,
final R Function(String) deserialize,
final Future<R> Function() call, {
final bool preloadCache = false,
final bool preferCache = false,
@ -140,7 +119,7 @@ class RequestManager {
if ((preferCache || preloadCache) && cache != null && await cache!.has(key)) {
_print('[Cache]: $k');
final s = unwrap(await deserialize((await cache!.get(key))!));
final s = unwrap(deserialize((await cache!.get(key))!));
if (preloadCache) {
yield ResultCached(s, loading: true);
} else {
@ -152,7 +131,7 @@ class RequestManager {
try {
final response = await _timeout(disableTimeout, call);
final s = await serialize(response);
final s = serialize(response);
_print('[Response]: $k');
await cache?.set(key, s);
@ -161,7 +140,7 @@ class RequestManager {
if (cache != null && await cache!.has(key)) {
_print('[Cache]: $k');
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;
}
_print('[Failure]: $k');
@ -282,7 +261,3 @@ extension ResultDataError<T> on Result<T> {
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);
ThemeData getThemeFromNextcloudTheme(
final NextcloudTheme? nextcloudTheme,
final CoreServerCapabilitiesOcsDataCapabilitiesTheming? nextcloudTheme,
final ThemeMode themeMode,
final Brightness platformBrightness, {
required final bool oledAsDark,

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

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

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

@ -89,43 +89,40 @@ class ExceptionWidget extends StatelessWidget {
}
if (exception is ApiException) {
if (exception.code == 401) {
if (exception.statusCode == 401) {
return _ExceptionDetails(
text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch,
isUnauthorized: true,
);
}
if (exception.code >= 500 && exception.code <= 599) {
if (exception.statusCode >= 500 && exception.statusCode <= 599) {
return _ExceptionDetails(
text: AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest,
);
}
}
final socketException = _getSpecificExceptionForException<SocketException>(exception);
if (socketException != null) {
if (exception is SocketException) {
return _ExceptionDetails(
text: socketException.address != null
? AppLocalizations.of(context).errorUnableToReachServerAt(socketException.address!.host)
text: exception.address != null
? AppLocalizations.of(context).errorUnableToReachServerAt(exception.address!.host)
: AppLocalizations.of(context).errorUnableToReachServer,
);
}
final clientException = _getSpecificExceptionForException<ClientException>(exception);
if (clientException != null) {
if (exception is ClientException) {
return _ExceptionDetails(
text: clientException.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(clientException.uri!.host)
text: exception.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(exception.uri!.host)
: AppLocalizations.of(context).errorUnableToReachServer,
);
}
final httpException = _getSpecificExceptionForException<HttpException>(exception);
if (httpException != null) {
if (exception is HttpException) {
return _ExceptionDetails(
text: httpException.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(httpException.uri!.host)
text: exception.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(exception.uri!.host)
: 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 {
await Navigator.of(context).push(
MaterialPageRoute(

Loading…
Cancel
Save