feat: toggle notifications on/off (#3672)

This commit is contained in:
Mathias Mogensen 2023-10-12 06:54:08 +02:00 committed by GitHub
parent bc8f35d7db
commit ebe112581d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 317 additions and 78 deletions

View File

@ -1,4 +1,4 @@
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

View File

@ -1,5 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';

View File

@ -7,7 +7,7 @@ import 'package:appflowy/plugins/inline_actions/handlers/inline_page_reference.d
import 'package:appflowy/plugins/inline_actions/handlers/reminder_reference.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_command.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_service.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart';

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option_action.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart';
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_picker_dialog.dart';
import 'package:appflowy_editor/appflowy_editor.dart';

View File

@ -24,6 +24,7 @@ import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/workspace/application/edit_panel/edit_panel_bloc.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_action_bloc.dart';
import 'package:appflowy/workspace/application/settings/notifications/notification_settings_cubit.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/user/prelude.dart';
@ -168,14 +169,22 @@ void _resolveHomeDeps(GetIt getIt) {
getIt.registerLazySingleton<TabsBloc>(() => TabsBloc());
getIt.registerSingleton<ReminderBloc>(ReminderBloc());
getIt.registerSingleton<NotificationSettingsCubit>(
NotificationSettingsCubit(),
);
getIt.registerSingleton<ReminderBloc>(
ReminderBloc(notificationSettings: getIt<NotificationSettingsCubit>()),
);
}
void _resolveFolderDeps(GetIt getIt) {
//workspace
getIt.registerFactoryParam<WorkspaceListener, UserProfilePB, String>(
(user, workspaceId) =>
WorkspaceListener(user: user, workspaceId: workspaceId),
(user, workspaceId) => WorkspaceListener(
user: user,
workspaceId: workspaceId,
),
);
getIt.registerFactoryParam<ViewBloc, ViewPB, void>(

View File

@ -1,19 +1,23 @@
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_service.dart';
import 'package:appflowy/startup/tasks/prelude.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:appflowy/workspace/application/settings/notifications/notification_settings_cubit.dart';
import 'prelude.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../../user/application/user_settings_service.dart';
import '../../workspace/application/appearance.dart';
import '../startup.dart';
import 'package:go_router/go_router.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_service.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/user/application/user_settings_service.dart';
import 'package:appflowy/startup/startup.dart';
class InitAppWidgetTask extends LaunchTask {
const InitAppWidgetTask();
@ -95,8 +99,8 @@ class ApplicationWidget extends StatefulWidget {
});
final Widget child;
final AppearanceSettingsPB appearanceSetting;
final AppTheme appTheme;
final AppearanceSettingsPB appearanceSetting;
final DateTimeSettingsPB dateTimeSettings;
@override
@ -125,6 +129,9 @@ class _ApplicationWidgetState extends State<ApplicationWidget> {
widget.appTheme,
)..readLocaleWhenAppLaunch(context),
),
BlocProvider<NotificationSettingsCubit>(
create: (_) => getIt<NotificationSettingsCubit>(),
),
BlocProvider<DocumentAppearanceCubit>(
create: (_) => DocumentAppearanceCubit()..fetch(),
),

View File

@ -6,6 +6,7 @@ import 'package:appflowy/user/application/reminder/reminder_service.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_action.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_action_bloc.dart';
import 'package:appflowy/workspace/application/local_notifications/notification_service.dart';
import 'package:appflowy/workspace/application/settings/notifications/notification_settings_cubit.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:bloc/bloc.dart';
@ -18,11 +19,16 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'reminder_bloc.freezed.dart';
class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
final NotificationSettingsCubit _notificationSettings;
late final NotificationActionBloc actionBloc;
late final ReminderService reminderService;
late final Timer timer;
ReminderBloc() : super(ReminderState()) {
ReminderBloc({
required NotificationSettingsCubit notificationSettings,
}) : _notificationSettings = notificationSettings,
super(ReminderState()) {
actionBloc = getIt<NotificationActionBloc>();
reminderService = const ReminderService();
timer = _periodicCheck();
@ -124,16 +130,18 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
);
if (scheduledAt.isBefore(now)) {
NotificationMessage(
identifier: reminder.id,
title: LocaleKeys.reminderNotification_title.tr(),
body: LocaleKeys.reminderNotification_message.tr(),
onClick: () => actionBloc.add(
NotificationActionEvent.performAction(
action: NotificationAction(objectId: reminder.objectId),
if (_notificationSettings.state.isNotificationsEnabled) {
NotificationMessage(
identifier: reminder.id,
title: LocaleKeys.reminderNotification_title.tr(),
body: LocaleKeys.reminderNotification_message.tr(),
onClick: () => actionBloc.add(
NotificationActionEvent.performAction(
action: NotificationAction(objectId: reminder.objectId),
),
),
),
);
);
}
add(
ReminderEvent.update(

View File

@ -41,4 +41,20 @@ class UserSettingsBackendService {
) async {
return (await UserEventSetDateTimeSettings(settings).send()).swap();
}
Future<Either<FlowyError, Unit>> setNotificationSettings(
NotificationSettingsPB settings,
) async {
return (await UserEventSetNotificationSettings(settings).send()).swap();
}
Future<NotificationSettingsPB> getNotificationSettings() async {
final result = await UserEventGetNotificationSettings().send();
return result.fold(
(NotificationSettingsPB setting) => setting,
(error) =>
throw FlowySDKException(ExceptionType.AppearanceSettingsIsEmpty),
);
}
}

View File

@ -6,7 +6,7 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/presentation.dart';
import 'package:appflowy/util/platform_extension.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -8,7 +8,7 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/historical_user_bloc.dart';
import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/user/presentation/widgets/widgets.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';

View File

@ -1,5 +1,5 @@
import 'package:appflowy/user/application/user_listener.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/edit_panel/edit_context.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart'
show WorkspaceSettingPB;

View File

@ -16,33 +16,40 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:google_fonts/google_fonts.dart';
part 'appearance.freezed.dart';
part 'appearance_cubit.freezed.dart';
const _white = Color(0xFFFFFFFF);
/// [AppearanceSettingsCubit] is used to modify the appearance of AppFlowy.
/// It includes the [AppTheme], [ThemeMode], [TextStyles] and [Locale].
/// It includes:
/// - [AppTheme]
/// - [ThemeMode]
/// - [TextStyle]'s
/// - [Locale]
/// - [UserDateFormatPB]
/// - [UserTimeFormatPB]
///
class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
final AppearanceSettingsPB _setting;
final AppearanceSettingsPB _appearanceSettings;
final DateTimeSettingsPB _dateTimeSettings;
AppearanceSettingsCubit(
AppearanceSettingsPB setting,
AppearanceSettingsPB appearanceSettings,
DateTimeSettingsPB dateTimeSettings,
AppTheme appTheme,
) : _setting = setting,
) : _appearanceSettings = appearanceSettings,
_dateTimeSettings = dateTimeSettings,
super(
AppearanceSettingsState.initial(
appTheme,
setting.themeMode,
setting.font,
setting.monospaceFont,
setting.layoutDirection,
setting.textDirection,
setting.locale,
setting.isMenuCollapsed,
setting.menuOffset,
appearanceSettings.themeMode,
appearanceSettings.font,
appearanceSettings.monospaceFont,
appearanceSettings.layoutDirection,
appearanceSettings.textDirection,
appearanceSettings.locale,
appearanceSettings.isMenuCollapsed,
appearanceSettings.menuOffset,
dateTimeSettings.dateFormat,
dateTimeSettings.timeFormat,
dateTimeSettings.timezoneId,
@ -52,7 +59,7 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
/// Update selected theme in the user's settings and emit an updated state
/// with the AppTheme named [themeName].
Future<void> setTheme(String themeName) async {
_setting.theme = themeName;
_appearanceSettings.theme = themeName;
_saveAppearanceSettings();
emit(state.copyWith(appTheme: await AppTheme.fromName(themeName)));
}
@ -63,7 +70,7 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
/// Update the theme mode in the user's settings and emit an updated state.
void setThemeMode(ThemeMode themeMode) {
_setting.themeMode = _themeModeToPB(themeMode);
_appearanceSettings.themeMode = _themeModeToPB(themeMode);
_saveAppearanceSettings();
emit(state.copyWith(themeMode: themeMode));
}
@ -81,13 +88,13 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
}
void setLayoutDirection(LayoutDirection layoutDirection) {
_setting.layoutDirection = layoutDirection.toLayoutDirectionPB();
_appearanceSettings.layoutDirection = layoutDirection.toLayoutDirectionPB();
_saveAppearanceSettings();
emit(state.copyWith(layoutDirection: layoutDirection));
}
void setTextDirection(AppFlowyTextDirection? textDirection) {
_setting.textDirection =
_appearanceSettings.textDirection =
textDirection?.toTextDirectionPB() ?? TextDirectionPB.FALLBACK;
_saveAppearanceSettings();
emit(state.copyWith(textDirection: textDirection));
@ -96,7 +103,7 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
/// Update selected font in the user's settings and emit an updated state
/// with the font name.
void setFontFamily(String fontFamilyName) {
_setting.font = fontFamilyName;
_appearanceSettings.font = fontFamilyName;
_saveAppearanceSettings();
emit(state.copyWith(font: fontFamilyName));
}
@ -118,8 +125,8 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
});
if (state.locale != newLocale) {
_setting.locale.languageCode = newLocale.languageCode;
_setting.locale.countryCode = newLocale.countryCode ?? "";
_appearanceSettings.locale.languageCode = newLocale.languageCode;
_appearanceSettings.locale.countryCode = newLocale.countryCode ?? "";
_saveAppearanceSettings();
emit(state.copyWith(locale: newLocale));
}
@ -127,13 +134,13 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
// Saves the menus current visibility
void saveIsMenuCollapsed(bool collapsed) {
_setting.isMenuCollapsed = collapsed;
_appearanceSettings.isMenuCollapsed = collapsed;
_saveAppearanceSettings();
}
// Saves the current resize offset of the menu
void saveMenuOffset(double offset) {
_setting.menuOffset = offset;
_appearanceSettings.menuOffset = offset;
_saveAppearanceSettings();
}
@ -146,14 +153,14 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
}
if (value == null) {
_setting.settingKeyValue.remove(key);
_appearanceSettings.settingKeyValue.remove(key);
}
if (_setting.settingKeyValue[key] != value) {
if (_appearanceSettings.settingKeyValue[key] != value) {
if (value == null) {
_setting.settingKeyValue.remove(key);
_appearanceSettings.settingKeyValue.remove(key);
} else {
_setting.settingKeyValue[key] = value;
_appearanceSettings.settingKeyValue[key] = value;
}
}
_saveAppearanceSettings();
@ -164,14 +171,14 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
Log.warn("The key should not be empty");
return null;
}
return _setting.settingKeyValue[key];
return _appearanceSettings.settingKeyValue[key];
}
/// Called when the application launches.
/// Uses the device locale when the application is opened for the first time.
void readLocaleWhenAppLaunch(BuildContext context) {
if (_setting.resetToDefault) {
_setting.resetToDefault = false;
if (_appearanceSettings.resetToDefault) {
_appearanceSettings.resetToDefault = false;
_saveAppearanceSettings();
setLocale(context, context.deviceLocale);
return;
@ -204,7 +211,9 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
}
Future<void> _saveAppearanceSettings() async {
UserSettingsBackendService().setAppearanceSetting(_setting).then((result) {
UserSettingsBackendService()
.setAppearanceSetting(_appearanceSettings)
.then((result) {
result.fold(
(l) => null,
(error) => Log.error(error),

View File

@ -0,0 +1,62 @@
import 'dart:async';
import 'package:appflowy/user/application/user_settings_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'notification_settings_cubit.freezed.dart';
class NotificationSettingsCubit extends Cubit<NotificationSettingsState> {
final Completer<void> _initCompleter = Completer();
late final NotificationSettingsPB _notificationSettings;
NotificationSettingsCubit() : super(NotificationSettingsState.initial()) {
UserSettingsBackendService()
.getNotificationSettings()
.then((notificationSettings) {
_notificationSettings = notificationSettings;
_initCompleter.complete();
});
}
Future<void> toggleNotificationsEnabled() async {
await _initCompleter.future;
_notificationSettings.notificationsEnabled = !state.isNotificationsEnabled;
_saveNotificationSettings();
emit(
state.copyWith(
isNotificationsEnabled: _notificationSettings.notificationsEnabled,
),
);
}
Future<void> _saveNotificationSettings() async {
await _initCompleter.future;
UserSettingsBackendService()
.setNotificationSettings(_notificationSettings)
.then((result) {
result.fold(
(error) => Log.error(error),
(r) => null,
);
});
}
}
@freezed
class NotificationSettingsState with _$NotificationSettingsState {
const NotificationSettingsState._();
const factory NotificationSettingsState({
required bool isNotificationsEnabled,
}) = _NotificationSettingsState;
factory NotificationSettingsState.initial() =>
const NotificationSettingsState(isNotificationsEnabled: true);
}

View File

@ -13,6 +13,7 @@ enum SettingsPage {
language,
files,
user,
notifications,
syncSetting,
shortcuts,
}

View File

@ -3,7 +3,7 @@ import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/home/home_bloc.dart';
import 'package:appflowy/workspace/application/home/home_service.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';

View File

@ -1,5 +1,5 @@
import 'dart:io';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:flutter/material.dart';

View File

@ -1,5 +1,6 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/sync_setting_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_customize_shortcuts_view.dart';
@ -101,6 +102,8 @@ class SettingsDialog extends StatelessWidget {
didLogout: didLogout,
didOpenUser: didOpenUser,
);
case SettingsPage.notifications:
return const SettingsNotificationsView();
case SettingsPage.syncSetting:
return SyncSettingView(userId: user.id.toString());
case SettingsPage.shortcuts:

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart';

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -1,7 +1,7 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart';

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -1,4 +1,4 @@
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/create_file_setting.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/date_format_setting.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/time_format_setting.dart';

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';

View File

@ -61,6 +61,13 @@ class SettingsMenu extends StatelessWidget {
icon: Icons.account_box_outlined,
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.notifications,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_notifications.tr(),
icon: Icons.notifications_outlined,
changeSelectedPage: changeSelectedPage,
),
if (showSyncSetting)
const SizedBox(
height: 10,

View File

@ -0,0 +1,45 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/notifications/notification_settings_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SettingsNotificationsView extends StatelessWidget {
const SettingsNotificationsView({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<NotificationSettingsCubit, NotificationSettingsState>(
builder: (context, state) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ThemeSettingEntryTemplateWidget(
label: LocaleKeys
.settings_notifications_enableNotifications_label
.tr(),
hint: LocaleKeys.settings_notifications_enableNotifications_hint
.tr(),
trailing: [
Switch(
value: state.isNotificationsEnabled,
splashRadius: 0,
activeColor: Theme.of(context).colorScheme.primary,
onChanged: (value) {
context
.read<NotificationSettingsCubit>()
.toggleNotificationsEnabled();
},
)
],
),
],
),
);
},
);
}
}

View File

@ -1,5 +1,5 @@
import 'package:appflowy/user/application/user_settings_service.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
import 'package:flowy_infra/theme.dart';
@ -18,6 +18,7 @@ void main() {
group('$AppearanceSettingsCubit', () {
late AppearanceSettingsPB appearanceSetting;
late DateTimeSettingsPB dateTimeSettings;
setUp(() async {
appearanceSetting =
await UserSettingsBackendService().getAppearanceSetting();

View File

@ -1,5 +1,5 @@
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';

View File

@ -241,6 +241,7 @@
"language": "Language",
"user": "User",
"files": "Files",
"notifications": "Notifications",
"open": "Open Settings",
"logout": "Logout",
"logoutPrompt": "Are you sure to logout?",
@ -256,6 +257,12 @@
"historicalUserListTooltip": "This list displays your anonymous accounts. You can click on an account to view its details. Anonymous accounts are created by clicking the 'Get Started' button",
"openHistoricalUser": "Click to open the anonymous account"
},
"notifications": {
"enableNotifications": {
"label": "Enable notifications",
"hint": "Turn off to stop local notifications from appearing."
}
},
"appearance": {
"resetSetting": "Reset this setting",
"fontFamily": {

View File

@ -237,3 +237,17 @@ impl std::default::Default for DateTimeSettingsPB {
}
}
}
#[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone)]
pub struct NotificationSettingsPB {
#[pb(index = 1)]
pub notifications_enabled: bool,
}
impl std::default::Default for NotificationSettingsPB {
fn default() -> Self {
NotificationSettingsPB {
notifications_enabled: true,
}
}
}

View File

@ -195,7 +195,7 @@ pub async fn get_date_time_settings(
Ok(setting) => setting,
Err(e) => {
tracing::error!(
"Deserialize AppearanceSettings failed: {:?}, fallback to default",
"Deserialize DateTimeSettings failed: {:?}, fallback to default",
e
);
DateTimeSettingsPB::default()
@ -206,6 +206,42 @@ pub async fn get_date_time_settings(
}
}
const NOTIFICATION_SETTINGS_CACHE_KEY: &str = "notification_settings";
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn set_notification_settings(
store_preferences: AFPluginState<Weak<StorePreferences>>,
data: AFPluginData<NotificationSettingsPB>,
) -> Result<(), FlowyError> {
let store_preferences = upgrade_store_preferences(store_preferences)?;
let setting = data.into_inner();
store_preferences.set_object(NOTIFICATION_SETTINGS_CACHE_KEY, setting)?;
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn get_notification_settings(
store_preferences: AFPluginState<Weak<StorePreferences>>,
) -> DataResult<NotificationSettingsPB, FlowyError> {
let store_preferences = upgrade_store_preferences(store_preferences)?;
match store_preferences.get_str(NOTIFICATION_SETTINGS_CACHE_KEY) {
None => data_result_ok(NotificationSettingsPB::default()),
Some(s) => {
let setting = match serde_json::from_str(&s) {
Ok(setting) => setting,
Err(e) => {
tracing::error!(
"Deserialize NotificationSettings failed: {:?}, fallback to default",
e
);
NotificationSettingsPB::default()
},
};
data_result_ok(setting)
},
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn get_user_setting(
manager: AFPluginState<Weak<UserManager>>,

View File

@ -65,6 +65,14 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
.event(UserEvent::ResetWorkspace, reset_workspace_handler)
.event(UserEvent::SetDateTimeSettings, set_date_time_settings)
.event(UserEvent::GetDateTimeSettings, get_date_time_settings)
.event(
UserEvent::SetNotificationSettings,
set_notification_settings,
)
.event(
UserEvent::GetNotificationSettings,
get_notification_settings,
)
}
pub struct SignUpContext {
@ -317,4 +325,10 @@ pub enum UserEvent {
/// Retrieve the Date/Time formats
#[event(output = "DateTimeSettingsPB")]
GetDateTimeSettings = 34,
#[event(input = "NotificationSettingsPB")]
SetNotificationSettings = 35,
#[event(output = "NotificationSettingsPB")]
GetNotificationSettings = 36,
}