Skip to content

Commit

Permalink
Merge pull request #1925 from matthiasn/feat/habit_notification
Browse files Browse the repository at this point in the history
feat: add (very basic) habit notification
  • Loading branch information
matthiasn authored Feb 4, 2025
2 parents c5af080 + b5d50ec commit 4339f53
Show file tree
Hide file tree
Showing 17 changed files with 357 additions and 190 deletions.
24 changes: 24 additions & 0 deletions lib/blocs/settings/habits/habit_settings_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:lotti/classes/tag_type_definitions.dart';
import 'package:lotti/get_it.dart';
import 'package:lotti/logic/habits/autocomplete_update.dart';
import 'package:lotti/logic/persistence_logic.dart';
import 'package:lotti/services/notification_service.dart';
import 'package:lotti/services/tags_service.dart';
import 'package:lotti/widgets/settings/habits/habit_autocomplete_widget.dart';

Expand Down Expand Up @@ -89,6 +90,27 @@ class HabitSettingsCubit extends Cubit<HabitSettingsState> {
emitState();
}

void setAlertAtTime(DateTime? alertAtTime) {
_dirty = true;
_habitDefinition = _habitDefinition.copyWith(
habitSchedule: HabitSchedule.daily(
requiredCompletions: 1,
alertAtTime: alertAtTime,
),
);
emitState();
}

void clearAlertAtTime() {
_dirty = true;
_habitDefinition = _habitDefinition.copyWith(
habitSchedule: const HabitSchedule.daily(
requiredCompletions: 1,
),
);
emitState();
}

Future<void> onSavePressed() async {
state.formKey.currentState!.save();
if (state.formKey.currentState!.validate()) {
Expand All @@ -111,6 +133,8 @@ class HabitSettingsCubit extends Cubit<HabitSettingsState> {
_dirty = false;
emitState();

await getIt<NotificationService>().scheduleHabitNotification(dataType);

_maybePop();
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/classes/entity_definitions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class HabitSchedule with _$HabitSchedule {
const factory HabitSchedule.daily({
required int requiredCompletions,
DateTime? showFrom,
DateTime? alertAtTime,
}) = DailyHabitSchedule;

const factory HabitSchedule.weekly({
Expand Down
78 changes: 57 additions & 21 deletions lib/classes/entity_definitions.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,27 @@ mixin _$HabitSchedule {
int get requiredCompletions => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int requiredCompletions, DateTime? showFrom)
required TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)
daily,
required TResult Function(int requiredCompletions) weekly,
required TResult Function(int requiredCompletions) monthly,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult? Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult? Function(int requiredCompletions)? weekly,
TResult? Function(int requiredCompletions)? monthly,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult Function(int requiredCompletions)? weekly,
TResult Function(int requiredCompletions)? monthly,
required TResult orElse(),
Expand Down Expand Up @@ -131,7 +136,8 @@ abstract class _$$DailyHabitScheduleImplCopyWith<$Res>
__$$DailyHabitScheduleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int requiredCompletions, DateTime? showFrom});
$Res call(
{int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime});
}

/// @nodoc
Expand All @@ -149,6 +155,7 @@ class __$$DailyHabitScheduleImplCopyWithImpl<$Res>
$Res call({
Object? requiredCompletions = null,
Object? showFrom = freezed,
Object? alertAtTime = freezed,
}) {
return _then(_$DailyHabitScheduleImpl(
requiredCompletions: null == requiredCompletions
Expand All @@ -159,6 +166,10 @@ class __$$DailyHabitScheduleImplCopyWithImpl<$Res>
? _value.showFrom
: showFrom // ignore: cast_nullable_to_non_nullable
as DateTime?,
alertAtTime: freezed == alertAtTime
? _value.alertAtTime
: alertAtTime // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
Expand All @@ -167,7 +178,10 @@ class __$$DailyHabitScheduleImplCopyWithImpl<$Res>
@JsonSerializable()
class _$DailyHabitScheduleImpl implements DailyHabitSchedule {
const _$DailyHabitScheduleImpl(
{required this.requiredCompletions, this.showFrom, final String? $type})
{required this.requiredCompletions,
this.showFrom,
this.alertAtTime,
final String? $type})
: $type = $type ?? 'daily';

factory _$DailyHabitScheduleImpl.fromJson(Map<String, dynamic> json) =>
Expand All @@ -177,13 +191,15 @@ class _$DailyHabitScheduleImpl implements DailyHabitSchedule {
final int requiredCompletions;
@override
final DateTime? showFrom;
@override
final DateTime? alertAtTime;

@JsonKey(name: 'runtimeType')
final String $type;

@override
String toString() {
return 'HabitSchedule.daily(requiredCompletions: $requiredCompletions, showFrom: $showFrom)';
return 'HabitSchedule.daily(requiredCompletions: $requiredCompletions, showFrom: $showFrom, alertAtTime: $alertAtTime)';
}

@override
Expand All @@ -194,12 +210,15 @@ class _$DailyHabitScheduleImpl implements DailyHabitSchedule {
(identical(other.requiredCompletions, requiredCompletions) ||
other.requiredCompletions == requiredCompletions) &&
(identical(other.showFrom, showFrom) ||
other.showFrom == showFrom));
other.showFrom == showFrom) &&
(identical(other.alertAtTime, alertAtTime) ||
other.alertAtTime == alertAtTime));
}

@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, requiredCompletions, showFrom);
int get hashCode =>
Object.hash(runtimeType, requiredCompletions, showFrom, alertAtTime);

/// Create a copy of HabitSchedule
/// with the given fields replaced by the non-null parameter values.
Expand All @@ -213,34 +232,39 @@ class _$DailyHabitScheduleImpl implements DailyHabitSchedule {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int requiredCompletions, DateTime? showFrom)
required TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)
daily,
required TResult Function(int requiredCompletions) weekly,
required TResult Function(int requiredCompletions) monthly,
}) {
return daily(requiredCompletions, showFrom);
return daily(requiredCompletions, showFrom, alertAtTime);
}

@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult? Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult? Function(int requiredCompletions)? weekly,
TResult? Function(int requiredCompletions)? monthly,
}) {
return daily?.call(requiredCompletions, showFrom);
return daily?.call(requiredCompletions, showFrom, alertAtTime);
}

@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult Function(int requiredCompletions)? weekly,
TResult Function(int requiredCompletions)? monthly,
required TResult orElse(),
}) {
if (daily != null) {
return daily(requiredCompletions, showFrom);
return daily(requiredCompletions, showFrom, alertAtTime);
}
return orElse();
}
Expand Down Expand Up @@ -290,14 +314,16 @@ class _$DailyHabitScheduleImpl implements DailyHabitSchedule {
abstract class DailyHabitSchedule implements HabitSchedule {
const factory DailyHabitSchedule(
{required final int requiredCompletions,
final DateTime? showFrom}) = _$DailyHabitScheduleImpl;
final DateTime? showFrom,
final DateTime? alertAtTime}) = _$DailyHabitScheduleImpl;

factory DailyHabitSchedule.fromJson(Map<String, dynamic> json) =
_$DailyHabitScheduleImpl.fromJson;

@override
int get requiredCompletions;
DateTime? get showFrom;
DateTime? get alertAtTime;

/// Create a copy of HabitSchedule
/// with the given fields replaced by the non-null parameter values.
Expand Down Expand Up @@ -388,7 +414,8 @@ class _$WeeklyHabitScheduleImpl implements WeeklyHabitSchedule {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int requiredCompletions, DateTime? showFrom)
required TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)
daily,
required TResult Function(int requiredCompletions) weekly,
required TResult Function(int requiredCompletions) monthly,
Expand All @@ -399,7 +426,9 @@ class _$WeeklyHabitScheduleImpl implements WeeklyHabitSchedule {
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult? Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult? Function(int requiredCompletions)? weekly,
TResult? Function(int requiredCompletions)? monthly,
}) {
Expand All @@ -409,7 +438,9 @@ class _$WeeklyHabitScheduleImpl implements WeeklyHabitSchedule {
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult Function(int requiredCompletions)? weekly,
TResult Function(int requiredCompletions)? monthly,
required TResult orElse(),
Expand Down Expand Up @@ -562,7 +593,8 @@ class _$MonthlyHabitScheduleImpl implements MonthlyHabitSchedule {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int requiredCompletions, DateTime? showFrom)
required TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)
daily,
required TResult Function(int requiredCompletions) weekly,
required TResult Function(int requiredCompletions) monthly,
Expand All @@ -573,7 +605,9 @@ class _$MonthlyHabitScheduleImpl implements MonthlyHabitSchedule {
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult? Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult? Function(int requiredCompletions)? weekly,
TResult? Function(int requiredCompletions)? monthly,
}) {
Expand All @@ -583,7 +617,9 @@ class _$MonthlyHabitScheduleImpl implements MonthlyHabitSchedule {
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int requiredCompletions, DateTime? showFrom)? daily,
TResult Function(
int requiredCompletions, DateTime? showFrom, DateTime? alertAtTime)?
daily,
TResult Function(int requiredCompletions)? weekly,
TResult Function(int requiredCompletions)? monthly,
required TResult orElse(),
Expand Down
4 changes: 4 additions & 0 deletions lib/classes/entity_definitions.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions lib/database/journal_db/config_flags.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:io';

import 'package:lotti/database/database.dart';
import 'package:lotti/utils/consts.dart';

Expand Down Expand Up @@ -84,13 +82,11 @@ Future<void> initConfigFlags(
status: false,
),
);
if (Platform.isMacOS) {
await db.insertFlagIfNotExists(
const ConfigFlag(
name: enableNotificationsFlag,
description: 'Enable desktop notifications?',
status: false,
),
);
}
await db.insertFlagIfNotExists(
const ConfigFlag(
name: enableNotificationsFlag,
description: 'Enable notifications?',
status: false,
),
);
}
2 changes: 1 addition & 1 deletion lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"cancelButton": "Abbrechen",
"categoryDeleteConfirm": "JA, KATEGORIE LÖSCHEN",
"categoryDeleteQuestion": "Kategorie wirklich löschen?",
"configFlagEnableNotifications": "Desktop Notifications erlauben?",
"configFlagEnableNotifications": "Notifications erlauben?",
"configFlagPrivate": "Private Einträge anzeigen?",
"conflictsResolved": "gelöst",
"conflictsUnresolved": "offen",
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"completeHabitFailButton": "Fail",
"completeHabitSkipButton": "Skip",
"completeHabitSuccessButton": "Success",
"configFlagEnableNotifications": "Enable desktop notifications?",
"configFlagEnableNotifications": "Enable notifications?",
"configFlagPrivate": "Show private entries?",
"configInvalidCert": "Allow invalid SSL certificate?",
"conflictsResolved": "resolved",
Expand Down Expand Up @@ -109,6 +109,7 @@
"habitsFilterOpenNow": "due",
"habitsFilterPendingLater": "later",
"habitShowFromLabel": "Show from",
"habitShowAlertAtLabel": "Show alert at",
"habitsLongerStreaksEmptyHeader": "No week-long streaks at the moment",
"habitsLongerStreaksHeader": "Week-long streaks (or longer)",
"habitsOpenHeader": "Due now",
Expand Down
17 changes: 7 additions & 10 deletions lib/logic/persistence_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ class PersistenceLogic {
shouldAddGeolocation: shouldAddGeolocation,
);

if (habitDefinition != null) {
await getIt<NotificationService>().scheduleHabitNotification(
habitDefinition,
daysToAdd: 1,
);
}

return habitCompletionEntry;
} catch (exception, stackTrace) {
_loggingService.captureException(
Expand Down Expand Up @@ -766,16 +773,6 @@ class PersistenceLogic {
);
}

if (dashboard.reviewAt != null && dashboard.deletedAt == null) {
await getIt<NotificationService>().scheduleNotification(
title: 'Time for a Dashboard Review!',
body: dashboard.name,
notifyAt: dashboard.reviewAt!,
notificationId: dashboard.id.hashCode,
deepLink: '/dashboards/${dashboard.id}',
);
}

return linesAffected;
}

Expand Down
Loading

0 comments on commit 4339f53

Please sign in to comment.