Skip to content

Commit

Permalink
🚧 Make sure new items stay in order
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Friese <[email protected]>
  • Loading branch information
peterfriese committed Apr 22, 2022
1 parent 6a8ac37 commit 0f97bee
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 18 deletions.
14 changes: 7 additions & 7 deletions code/frontend/MakeItSo/MakeItSo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
88700F76276B4DE10088F006 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 88700F75276B4DE10088F006 /* FirebaseAuth */; };
88700F78276B4DE10088F006 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 88700F77276B4DE10088F006 /* FirebaseFirestore */; };
88700F7A276B4DE10088F006 /* FirebaseFirestoreSwift-Beta in Frameworks */ = {isa = PBXBuildFile; productRef = 88700F79276B4DE10088F006 /* FirebaseFirestoreSwift-Beta */; };
88700F7D276B4FF90088F006 /* ReminderRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F7C276B4FF90088F006 /* ReminderRepository.swift */; };
88700F7E276B4FF90088F006 /* ReminderRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F7C276B4FF90088F006 /* ReminderRepository.swift */; };
88700F7D276B4FF90088F006 /* RemindersRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F7C276B4FF90088F006 /* RemindersRepository.swift */; };
88700F7E276B4FF90088F006 /* RemindersRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F7C276B4FF90088F006 /* RemindersRepository.swift */; };
88700F82276B517F0088F006 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F81276B517F0088F006 /* AuthenticationService.swift */; };
88700F83276B517F0088F006 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88700F81276B517F0088F006 /* AuthenticationService.swift */; };
88700F86276B52150088F006 /* Resolver in Frameworks */ = {isa = PBXBuildFile; productRef = 88700F85276B52150088F006 /* Resolver */; };
Expand Down Expand Up @@ -48,7 +48,7 @@

/* Begin PBXFileReference section */
881EF5BC272DC399004761E5 /* View+Focus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Focus.swift"; sourceTree = "<group>"; };
88700F7C276B4FF90088F006 /* ReminderRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderRepository.swift; sourceTree = "<group>"; };
88700F7C276B4FF90088F006 /* RemindersRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemindersRepository.swift; sourceTree = "<group>"; };
88700F81276B517F0088F006 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; };
88700F87276B54810088F006 /* MakeItSoApp+Injection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MakeItSoApp+Injection.swift"; sourceTree = "<group>"; };
88700F8A276B5E8A0088F006 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -107,7 +107,7 @@
88700F7B276B4F8F0088F006 /* Repositories */ = {
isa = PBXGroup;
children = (
88700F7C276B4FF90088F006 /* ReminderRepository.swift */,
88700F7C276B4FF90088F006 /* RemindersRepository.swift */,
);
path = Repositories;
sourceTree = "<group>";
Expand Down Expand Up @@ -160,9 +160,9 @@
881EF5BB272DC366004761E5 /* Extensions */,
88FEECEB2727FEC100ED368C /* Features */,
88FEECCA27275ABC00ED368C /* MakeItSoApp.swift */,
88700F87276B54810088F006 /* MakeItSoApp+Injection.swift */,
88FEECCC27275ABD00ED368C /* Assets.xcassets */,
88700F8A276B5E8A0088F006 /* GoogleService-Info.plist */,
88700F87276B54810088F006 /* MakeItSoApp+Injection.swift */,
);
path = Shared;
sourceTree = "<group>";
Expand Down Expand Up @@ -356,7 +356,7 @@
88700F82276B517F0088F006 /* AuthenticationService.swift in Sources */,
88FEECDA27275ABD00ED368C /* MakeItSoApp.swift in Sources */,
881EF5BD272DC399004761E5 /* View+Focus.swift in Sources */,
88700F7D276B4FF90088F006 /* ReminderRepository.swift in Sources */,
88700F7D276B4FF90088F006 /* RemindersRepository.swift in Sources */,
88FEECF32728044100ED368C /* RemindersListView.swift in Sources */,
88FEECF72728072D00ED368C /* RemindersListViewModel.swift in Sources */,
);
Expand All @@ -367,7 +367,7 @@
buildActionMask = 2147483647;
files = (
88FEECF12727FEFF00ED368C /* Reminder.swift in Sources */,
88700F7E276B4FF90088F006 /* ReminderRepository.swift in Sources */,
88700F7E276B4FF90088F006 /* RemindersRepository.swift in Sources */,
88700F89276B54810088F006 /* MakeItSoApp+Injection.swift in Sources */,
88C30CD7274D1B4500E6694D /* ReminderDetailsViewModel.swift in Sources */,
88FEECFB27280F3D00ED368C /* ReminderListRowView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ struct Location {
}

struct Reminder {
@DocumentID var id = UUID().uuidString
/// We need the Firestore document ID so we can update / delete the document
@DocumentID var docId: String?

/// The `id` is required to make the `Reminder` identifiable. We also need to persist this, otherwise
/// it would get lost when round-tripping to Firestore, which would result in the item losing focus.
var id: String? = UUID().uuidString
var title: String
var notes: String?
var url: String?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// ReminderRepository.swift
// RemindersRepository.swift
// MakeItSo
//
// Created by Peter Friese on 16.12.21.
Expand All @@ -24,7 +24,7 @@ import FirebaseFirestoreSwift
import Resolver
import os

class ReminderRepository: ObservableObject {
class RemindersRepository: ObservableObject {
// MARK: - Dependencies
@Injected var db: Firestore
@Injected var authenticationService: AuthenticationService
Expand Down Expand Up @@ -132,7 +132,7 @@ class ReminderRepository: ObservableObject {
}

func updateReminder(_ reminder: Reminder) {
if let documentId = reminder.id {
if let documentId = reminder.docId {
do {
try db.collection("reminders").document(documentId).setData(from: reminder)
}
Expand All @@ -143,7 +143,7 @@ class ReminderRepository: ObservableObject {
}

func removeReminder(_ reminder: Reminder) {
if let documentId = reminder.id {
if let documentId = reminder.docId {
db.collection("reminders").document(documentId).delete() { error in
if let error = error {
self.logger.debug("Unable to remove document \(error.localizedDescription)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,39 @@ import Combine
import SwiftUI
import Resolver

enum SortingMode {
case manual
case byDeadline
case byCreationDate
case byPriority
case byTitle

func sorted(lhs: Reminder, rhs: Reminder) -> Bool {
return lhs.order < rhs.order
}
}

extension Array where Element == Reminder {
func computeOrder(for reminder: Reminder) -> Int {
let index = self.endIndex == 0 ? 0 : self.endIndex - 1
return self.computeOrder(for: reminder, after: index)
}

func computeOrder(for reminder: Reminder, after index: Int) -> Int {
guard self.count > 0 else { return 0 }
let currentOrder = self[index].order

let nextIndex = self.index(after: index)
let nextOrder = nextIndex < self.endIndex ? self[nextIndex].order : currentOrder + 1_000

return (currentOrder + nextOrder) / 2
}
}

class RemindersListViewModel: ObservableObject {
@Published var remindersRepository: ReminderRepository
@Published var remindersRepository: RemindersRepository

@Published var sortingMode: SortingMode = .manual

@Published var reminders: [Reminder]
@Published var selectedReminder: Reminder?
Expand All @@ -38,6 +69,10 @@ class RemindersListViewModel: ObservableObject {
remindersRepository = Resolver.resolve()
remindersRepository.subscribe()
remindersRepository.$reminders
.combineLatest($sortingMode)
.map { reminders, sortingMode in
reminders.sorted(by: sortingMode.sorted)
}
.assign(to: &$reminders)

// This is the beginning of some magic Firestore sauce
Expand All @@ -47,7 +82,7 @@ class RemindersListViewModel: ObservableObject {
// }
// .store(in: &cancellables)

// the following pipeline removes empty reminder when the respecive row in the list view loses focus
// the following pipeline removes empty reminder when the respective row in the list view loses focus
$focusedReminder
.removeDuplicates()
.compactMap { focusedReminder -> Int? in
Expand All @@ -62,6 +97,7 @@ class RemindersListViewModel: ObservableObject {
}
.delay(for: 0.01, scheduler: RunLoop.main) // <-- this helps reduce the visual jank
.sink { index in
self.remindersRepository.removeReminder(self.reminders[index])
self.reminders.remove(at: index)
}
.store(in: &cancellables)
Expand All @@ -85,7 +121,7 @@ class RemindersListViewModel: ObservableObject {
}

func createNewReminder() {
let newReminder = Reminder(title: "")
var newReminder = Reminder(title: "")

// if any row is focused, insert the new reminder after the focused row
if case .row(let id) = focusedReminder {
Expand All @@ -99,11 +135,15 @@ class RemindersListViewModel: ObservableObject {
return
}

// compute median of the elements before and after the new one
newReminder.order = reminders.computeOrder(for: newReminder, after: index)

reminders.insert(newReminder, at: index + 1)
}
}
// no row focused: append at the end of the list
else {
newReminder.order = reminders.computeOrder(for: newReminder)
reminders.append(newReminder)
}
remindersRepository.addReminder(newReminder)
Expand Down
5 changes: 2 additions & 3 deletions code/frontend/MakeItSo/Shared/MakeItSoApp+Injection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ import Firebase
extension Resolver: ResolverRegistering {
public static func registerAllServices() {
// register Firebase services
register { Firestore.firestore().enableLogging(on: false) }.scope(.application)
register { Firestore.firestore().enableLogging(on: true) }.scope(.application)

// register application components
register { AuthenticationService() }.scope(.application)

register { ReminderRepository() }.scope(.application)
register { RemindersRepository() }.scope(.application)
}
}

Expand Down
1 change: 1 addition & 0 deletions code/frontend/MakeItSo/Shared/MakeItSoApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct MakeItSoApp: App {

UINavigationBar.appearance().standardAppearance = navBarAppearance

FirebaseConfiguration.shared.setLoggerLevel(.min)
FirebaseApp.configure()
authenticationService.signIn()
}
Expand Down

0 comments on commit 0f97bee

Please sign in to comment.