Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow date selection for Entry, Minor adjustments #32

Merged
merged 13 commits into from
Jun 3, 2024
Merged
24 changes: 12 additions & 12 deletions Diary/Diary/AppShell.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,51 +62,51 @@
</DataTemplate>
</Shell.ItemTemplate>

<FlyoutItem Title="Entries">
<FlyoutItem Title="{x:Static t:AppShellTexts.Entries_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.Clipboard}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Entries"
Title="{x:Static t:AppShellTexts.Entries_FlyoutItem_Title}"
ContentTemplate="{DataTemplate entryviews:EntryListView}"
Route="entries" />
</FlyoutItem>
<FlyoutItem Title="Time machine">
<FlyoutItem Title="{x:Static t:AppShellTexts.TimeMachine_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.Clock}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Time machine"
Title="{x:Static t:AppShellTexts.TimeMachine_FlyoutItem_Title}"
ContentTemplate="{DataTemplate entryviews:TimeMachineView}"
Route="TimeMachine" />
</FlyoutItem>
<FlyoutItem Title="Templates">
<FlyoutItem Title="{x:Static t:AppShellTexts.Templates_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.Ruler}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Templates"
Title="{x:Static t:AppShellTexts.Templates_FlyoutItem_Title}"
ContentTemplate="{DataTemplate templateviews:TemplateListView}"
Route="templates" />
</FlyoutItem>
<FlyoutItem Title="Labels">
<FlyoutItem Title="{x:Static t:AppShellTexts.Labels_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.Tag}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Labels"
Title="{x:Static t:AppShellTexts.Labels_FlyoutItem_Title}"
ContentTemplate="{DataTemplate labelviews:LabelListView}"
Route="labels" />
</FlyoutItem>
Expand Down Expand Up @@ -135,28 +135,28 @@
Route="mood" />
</FlyoutItem>

<FlyoutItem Title="Import &amp; Export">
<FlyoutItem Title="{x:Static t:AppShellTexts.ImportExport_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.FileImport}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Import &amp; Export"
Title="{x:Static t:AppShellTexts.ImportExport_FlyoutItem_Title}"
ContentTemplate="{DataTemplate importexportviews:ImportExportView}"
Route="importexport" />
</FlyoutItem>

<FlyoutItem Title="Map">
<FlyoutItem Title="{x:Static t:AppShellTexts.Map_FlyoutItem_Title}">
<FlyoutItem.Icon>
<FontImageSource
FontFamily="{Static f:Fonts.FontAwesome}"
Glyph="{Static f:FontAwesomeIcons.Map}"
Color="{StaticResource PrimaryLightText}" />
</FlyoutItem.Icon>
<ShellContent
Title="Map"
Title="{x:Static t:AppShellTexts.Map_FlyoutItem_Title}"
ContentTemplate="{DataTemplate mapviews:MapView}"
Route="map" />
</FlyoutItem>
Expand Down
76 changes: 55 additions & 21 deletions Diary/Diary/Clients/EntryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ public async Task<ICollection<EntryListModel>> GetAllAsync(EntryFilterModel? ent

if (entryFilter?.DateFrom != null)
{
entities = entities.Where(e => e.CreatedAt >= entryFilter.DateFrom).ToList();
entities = entities.Where(e => e.DateTime >= entryFilter.DateFrom).ToList();
}

if (entryFilter?.DateTo != null)
{
entities = entities.Where(e => e.CreatedAt < entryFilter.DateTo.Value.AddDays(1)).ToList();
entities = entities.Where(e => e.DateTime < entryFilter.DateTo.Value.AddDays(1)).ToList();
}

if (entryFilter?.OrderByProperty != null)
Expand All @@ -76,7 +76,7 @@ public async Task<ICollection<EntryListModel>> GetAllAsync(EntryFilterModel? ent
public async Task<ICollection<EntryListModel>> GetByDayFromPreviousYearsAsync(DateTime date)
{
var entities = await _entryRepository.GetByDayFromPreviousYearsAsync(date);
entities = entities.OrderByDescending(e => e.CreatedAt).ToList();
entities = entities.OrderByDescending(e => e.DateTime).ToList();

return entities.MapToListModels();
}
Expand All @@ -94,13 +94,20 @@ public async Task<ICollection<EntryListModel>> GetByDayFromPreviousYearsAsync(Da
/// </summary>
public async Task<EntryDetailModel> SetAsync(EntryDetailModel model)
{
DateTime? oldDateTime = null;
if (model.Id != Guid.Empty)
{
var oldEntity = await _entryRepository.GetByIdAsync(model.Id);
oldDateTime = oldEntity?.DateTime;
}

var entity = model.MapToEntity();
var savedEntity = await _entryRepository.SetAsync(entity);
await _mediaRepository.DeleteIfUnusedAsync(entity.Media);
await MediaFileService.DeleteUnusedFilesAsync(_mediaRepository);

#if ANDROID
await ScheduleTimeMachineNotificationAsync(savedEntity);
await ScheduleTimeMachineNotificationAsync(savedEntity, oldDateTime);
#endif
return savedEntity.MapToDetailModel();
}
Expand Down Expand Up @@ -144,46 +151,73 @@ public async Task<ICollection<PinModel>> GetAllLocationPinsAsync()
return entitiesWithLocation.MapToPinModels();
}

private async Task ScheduleTimeMachineNotificationAsync(EntryEntity entity)
private async Task ScheduleTimeMachineNotificationAsync(EntryEntity entity, DateTime? oldDateTime)
{
if (await LocalNotificationCenter.Current.AreNotificationsEnabled() == false)
{
await LocalNotificationCenter.Current.RequestNotificationPermission();
}

if (oldDateTime != null) // If the entry is being updated, the old notification must be updated or removed
{
var oldNotificationId = Helpers.NotificationHelper.GetNotificationIdFromCreationDate(oldDateTime.Value);
var entriesWithOldNotificationId = await _entryRepository.GetByNotificationIdAsync(oldNotificationId);

LocalNotificationCenter.Current.Cancel(oldNotificationId);

if (entriesWithOldNotificationId.Count > 0)
{
var previousNotification = CreateNotification(oldNotificationId, oldDateTime.Value, entriesWithOldNotificationId.Count);
await LocalNotificationCenter.Current.Show(previousNotification);
}
}

var entriesWithTheSameNotificationId = await _entryRepository.GetByNotificationIdAsync(entity.NotificationId);

TimeSpan repeatInterval;
DateTime notificationDate = entity.CreatedAt.AddYears(1);
LocalNotificationCenter.Current.Cancel(entity.NotificationId);

if (entity.CreatedAt.Month == 2 && entity.CreatedAt.Day == 29)
var request = CreateNotification(entity.NotificationId, entity.DateTime, entriesWithTheSameNotificationId.Count);

await LocalNotificationCenter.Current.Show(request);
}

private NotificationRequest CreateNotification(int notificationId, DateTime date, int numOfEntries)
{
TimeSpan interval;

if (date.Month == 2 && date.Day == 29)
{
notificationDate = entity.CreatedAt.AddYears(4);
repeatInterval = TimeSpan.FromDays(365 * 3 + 366);
while (date < DateTime.Now) // Notification date must be in the future
{
date = date.AddYears(4);
}
interval = TimeSpan.FromDays(365 * 3 + 366);
}
else
{
repeatInterval = notificationDate - entity.CreatedAt;
while (date < DateTime.Now) // Notification date must be in the future
{
date = date.AddYears(1);
}
interval = TimeSpan.FromDays(365);
}

var request = new NotificationRequest

var entryText = numOfEntries > 1 ? "entries" : "entry";

return new NotificationRequest
{
NotificationId = entity.NotificationId,
NotificationId = notificationId,
Title = "Diary Time Machine",
Description = $"You have already written {entriesWithTheSameNotificationId.Count} entries on this day in the past.",
Description = $"You have already written {numOfEntries} {entryText} on this day in the past.",

Schedule = new NotificationRequestSchedule
{
NotifyTime = notificationDate,
NotifyRepeatInterval = repeatInterval,
NotifyTime = date,
NotifyRepeatInterval = interval,
RepeatType = NotificationRepeat.TimeInterval
},
Android = { Priority = AndroidPriority.High }
};

// Remove scheduled notification with out-of-date number of diary entries written on the same day
LocalNotificationCenter.Current.Cancel([entity.NotificationId]);

await LocalNotificationCenter.Current.Show(request);
}
}
2 changes: 1 addition & 1 deletion Diary/Diary/Converters/IntToMoodEmojiConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Diary.Converters
{
public class IntToMoodEmojiConverter : BaseConverterOneWay<int, string>
{
private static readonly string _defaultMoodEmoji = "😢";
private static readonly string _defaultMoodEmoji = ":(";

public override string ConvertFrom(int value, CultureInfo? culture = null)
{
Expand Down
2 changes: 1 addition & 1 deletion Diary/Diary/Converters/IntToMoodIconConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class IntToMoodIconConverter : BaseConverterOneWay<int, string>
{
private static readonly string _defaultMoodIconName = "";

public override string ConvertFrom(int value, CultureInfo? culture)
public override string ConvertFrom(int value, CultureInfo? culture = null)
{
return value switch
{
Expand Down
4 changes: 1 addition & 3 deletions Diary/Diary/Entities/EntryEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ public class EntryEntity : EntityBase

public string Content { get; set; } = string.Empty;

public DateTime CreatedAt { get; set; }

public DateTime EditedAt { get; set; }
public DateTime DateTime { get; set; }

public bool IsFavorite { get; set; }

Expand Down
4 changes: 2 additions & 2 deletions Diary/Diary/Enums/EntryFilterEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public enum OrderByProperty
[Display(Name = nameof(EntryEntity.Title))]
Title,

[Display(Name = nameof(EntryEntity.CreatedAt))]
CreatedAt,
[Display(Name = nameof(EntryEntity.DateTime))]
DateTime,

[Display(Name = nameof(EntryEntity.Mood))]
Mood,
Expand Down
4 changes: 2 additions & 2 deletions Diary/Diary/Mappers/EntryMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static partial class EntryMapper

public static partial ICollection<EntryListModel> MapToListModels(this ICollection<EntryEntity> entities);

[MapProperty(nameof(EntryEntity.CreatedAt), nameof(MoodListModel.DateTime))]
[MapProperty(nameof(EntryEntity.DateTime), nameof(MoodListModel.DateTime))]
public static partial MoodListModel MapToMoodListModel(this EntryEntity entities);

public static partial ICollection<MoodListModel> MapToMoodListModels(this ICollection<EntryEntity> entities);
Expand All @@ -31,7 +31,7 @@ public static PinModel MapToPinModel(this EntryEntity entity)
{
EntryId = entity.Id,
Title = entity.Title,
Description = $"Created: {entity.CreatedAt.ToString(Constants.MapDateTimeFormat)}",
Description = $"Created: {entity.DateTime.ToString(Constants.MapDateTimeFormat)}",
Location = new Location()
{
Latitude = entity.Latitude ?? 0,
Expand Down
3 changes: 3 additions & 0 deletions Diary/Diary/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ private static void RegisterRoutes()
Routing.RegisterRoute("//templates/create", typeof(TemplateCreateView));

Routing.RegisterRoute("//importexport", typeof(ImportExportView));

Routing.RegisterRoute("//map/detail", typeof(EntryDetailView));
Routing.RegisterRoute("//map/edit", typeof(EntryEditView));
}

private static void SetupDirectories()
Expand Down
3 changes: 1 addition & 2 deletions Diary/Diary/Models/Entry/EntryDetailModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ public record EntryDetailModel : ModelBase
public required Guid Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime EditedAt { get; set; }
public DateTime DateTime { get; set; }
public bool IsFavorite { get; set; }

[Range(1, 5)]
Expand Down
2 changes: 1 addition & 1 deletion Diary/Diary/Models/Entry/EntryFilterModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Diary.Models.Entry
{
public record EntryFilterModel : ModelBase
{
public OrderByProperty? OrderByProperty { get; set; } = EntryFilterEnums.OrderByProperty.CreatedAt;
public OrderByProperty? OrderByProperty { get; set; } = EntryFilterEnums.OrderByProperty.DateTime;

public OrderByDirection? OrderByDirection { get; set; } = EntryFilterEnums.OrderByDirection.Descending;

Expand Down
3 changes: 1 addition & 2 deletions Diary/Diary/Models/Entry/EntryListModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ public record EntryListModel : ModelBase
public required Guid Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime EditedAt { get; set; }
public DateTime DateTime { get; set; }
public bool IsFavorite { get; set; }
public int MediaCount { get; init; }
}
9 changes: 4 additions & 5 deletions Diary/Diary/Repositories/EntryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task<ICollection<EntryEntity>> GetByDayFromPreviousYearsAsync(DateT
var entities = await GetAllAsync();

entities = entities
.Where(e => e.CreatedAt.Month == date.Month && e.CreatedAt.Day == date.Day && e.CreatedAt.Year < date.Year)
.Where(e => e.DateTime.Month == date.Month && e.DateTime.Day == date.Day && e.DateTime.Year < date.Year)
.ToList();

return entities;
Expand Down Expand Up @@ -61,10 +61,9 @@ public override async Task<EntryEntity> SetAsync(EntryEntity entity)
if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
entity.CreatedAt = DateTime.Now;
entity.NotificationId = NotificationHelper.GetNotificationIdFromCreationDate(entity.CreatedAt);
}
entity.EditedAt = DateTime.Now;

entity.NotificationId = NotificationHelper.GetNotificationIdFromCreationDate(entity.DateTime);

// Get existing labels and media that are connected to the entry
var existingLabels = await GetLabelEntriesByEntryIdAsync(entity.Id);
Expand Down Expand Up @@ -133,7 +132,7 @@ public async Task<ICollection<EntryEntity>> GetEntriesByDateRangeAsync(DateTime
dateTo = new DateTime(dateTo.Year, dateTo.Month, dateTo.Day, 23, 59, 59);

return await connection.Table<EntryEntity>()
.Where(e => e.CreatedAt >= dateFrom && e.CreatedAt <= dateTo)
.Where(e => e.DateTime >= dateFrom && e.DateTime <= dateTo)
.ToListAsync();
}

Expand Down
11 changes: 10 additions & 1 deletion Diary/Diary/Resources/Styles/Styles.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,16 @@
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Medium" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="Margin" Value="0,5,0,15" />
<Setter Property="Margin" Value="0,5,0,5" />
</Style>

<Style x:Key="ChartSubHeaderLabel" TargetType="Label">
<Setter Property="Padding" Value="5" />
<Setter Property="TextColor" Value="{StaticResource PrimaryDarkText}" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Small" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="Margin" Value="0,0,0,15" />
</Style>

<Style x:Key="ShadowBorder" TargetType="Border">
Expand Down
Loading
Loading