From 4c86f979b73476f670567822a23ee5648727e1e1 Mon Sep 17 00:00:00 2001 From: Sarah Reichelt Date: Tue, 24 Sep 2024 13:53:31 +1000 Subject: [PATCH] add sort option and delete all with option key bypasses confirmation --- Localizable.xcstrings | 48 +++++++++-- README.md | 4 +- Today.xcodeproj/project.pbxproj | 128 +++-------------------------- Today/AboutView.swift | 2 +- Today/Edit Views/EditButtons.swift | 54 ++++++++++-- Today/Models/AppState.swift | 6 +- Today/Models/Debouncer.swift | 38 --------- 7 files changed, 111 insertions(+), 169 deletions(-) delete mode 100644 Today/Models/Debouncer.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 04b52ca..6ff1f05 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -88,10 +88,26 @@ } }, "By Date & Time" : { - + "comment" : "Using Translate app", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach Datum und Uhrzeit" + } + } + } }, "By Title" : { - + "comment" : "Using Translate app", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach Titel" + } + } + } }, "Check for Updates…" : { "localizations" : { @@ -233,6 +249,17 @@ } } }, + "Hold down Option to delete all without confirmation." : { + "comment" : "Using Translate app", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Halten Sie die Option gedrückt, um alle ohne Bestätigung zu löschen." + } + } + } + }, "I wanted a very simple menubar app that showed a list of items that I could check off over the day. Nothing long term, just day-by-day." : { "localizations" : { "de" : { @@ -334,7 +361,15 @@ } }, "Sort Todos" : { - + "comment" : "Using Translate app", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einträge sortieren" + } + } + } }, "Strike through" : { "localizations" : { @@ -346,12 +381,13 @@ } } }, - "There are only two settings: **Show Completed** lets you choose how to display, hide or delete the completed todos and **Launch on Login** sets whether you want the app to start automatically when you log in." : { + "There are only three settings: **Show Completed** lets you choose how to display, hide or delete the completed todos, **Sort Todos** allows you to sort the todos by the creation date & time or by title, and **Launch on Login** sets whether you want the app to start automatically." : { + "comment" : "Using Translate app", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Es gibt nur zwei Einstellungen: Mit **Abgeschlossene anzeigen** kannst du auswählen, wie die abgeschlossenen Aufgaben angezeigt, ausgeblendet oder gelöscht werden sollen, und mit **Bei Anmeldung starten** legst du fest, ob die App automatisch gestartet werden soll, wenn du dich anmeldest." + "value" : "Es gibt nur drei Einstellungen: Mit **Abgeschlossene anzeigen** kannst du auswählen, wie die abgeschlossenen Aufgaben angezeigt, ausgeblendet oder gelöscht werden sollen, **Einträge sortieren** ermöglicht es Ihnen, die Aufgaben nach Erstellungsdatum und -zeit oder nach Titel zu sortieren, uund mit **Bei Anmeldung starten** legst du fest, ob die App automatisch gestartet werden soll, wenn du dich anmeldest." } } } @@ -434,4 +470,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/README.md b/README.md index 60cc4c6..7f3ca64 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Every app I found had a terrific list of features, most of which I didn't want, Use the **Edit Todos…** menu item to add, delete, edit and move your todos. Select them in the menu to mark them as complete or incomplete. -There are only two settings: **Show Completed** lets you choose how to display, hide or delete the completed todos and **Launch on Login** sets whether you want the app to start automatically when you log in. +There are only three settings: **Show Completed** lets you choose how to display, hide or delete the completed todos, **Sort Todos** allows you to sort the todos by the creation date & time or by title, and **Launch on Login** sets whether you want the app to start automatically when you log in. This app is free, but if you'd like to support my work, please [Buy Me a Coffee](https://ko-fi.com/H2H3BU7SI). @@ -23,7 +23,9 @@ If you'd like to contact me, I'm [@troz@mastodon.social](https://mastodon.social - [ ] VoiceOver - not yet possible to set VocieOver text in menus - [x] More completion options - [x] German localisation +- [x] Allow sorting by title - [ ] More Localisations +- [x] Option-click on Delete All to delete without confirmation ## Sparkle process diff --git a/Today.xcodeproj/project.pbxproj b/Today.xcodeproj/project.pbxproj index 3f1d822..8af67de 100644 --- a/Today.xcodeproj/project.pbxproj +++ b/Today.xcodeproj/project.pbxproj @@ -3,57 +3,26 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ - 261C2CEA29B4746000049616 /* EditButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261C2CE929B4746000049616 /* EditButtons.swift */; }; - 262051C729A0B8F2008BAFD0 /* EditGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262051C629A0B8F2008BAFD0 /* EditGroup.swift */; }; - 262051C929A0B950008BAFD0 /* SettingsGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262051C829A0B950008BAFD0 /* SettingsGroup.swift */; }; - 262051CB29A0B9A2008BAFD0 /* AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262051CA29A0B9A2008BAFD0 /* AppGroup.swift */; }; - 262623E52977A8B6001F9610 /* EditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262623E42977A8B6001F9610 /* EditView.swift */; }; - 262623E72977C371001F9610 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262623E62977C371001F9610 /* AboutView.swift */; }; - 266E9C6A299DF224003CC545 /* EditTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266E9C69299DF224003CC545 /* EditTodoView.swift */; }; - 266E9C6C299DF279003CC545 /* NewTodoField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266E9C6B299DF279003CC545 /* NewTodoField.swift */; }; - 267402D72C9BA1E6005FEC8B /* Sorts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267402D62C9BA1E5005FEC8B /* Sorts.swift */; }; - 2675DBE82974BA6500492B03 /* TodayApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2675DBE72974BA6500492B03 /* TodayApp.swift */; }; - 2675DBEC2974BA6500492B03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2675DBEB2974BA6500492B03 /* Assets.xcassets */; }; - 2675DBEF2974BA6500492B03 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2675DBEE2974BA6500492B03 /* Preview Assets.xcassets */; }; - 26B83F582B4B6FCF0002381F /* Completes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B83F572B4B6FCF0002381F /* Completes.swift */; }; 26B83F5A2B4B824D0002381F /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 26B83F592B4B824D0002381F /* Localizable.xcstrings */; }; - 26BC9AF1297A100700E19AA3 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BC9AF0297A100700E19AA3 /* AppState.swift */; }; 26D823992978CA960051859E /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 26D823982978CA960051859E /* Sparkle */; }; - 26D8239B2978CAE70051859E /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26D8239A2978CAE70051859E /* Updater.swift */; }; - 26F2268C29762D2A00EDBEF3 /* Todo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F2268B29762D2A00EDBEF3 /* Todo.swift */; }; - 26F2268E2976347000EDBEF3 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F2268D2976347000EDBEF3 /* DataStore.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 2604F4E42978D9F40036A5BA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 2604F4E52978DFBF0036A5BA /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; - 261C2CE929B4746000049616 /* EditButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditButtons.swift; sourceTree = ""; }; - 262051C629A0B8F2008BAFD0 /* EditGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditGroup.swift; sourceTree = ""; }; - 262051C829A0B950008BAFD0 /* SettingsGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsGroup.swift; sourceTree = ""; }; - 262051CA29A0B9A2008BAFD0 /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = ""; }; - 262623E42977A8B6001F9610 /* EditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditView.swift; sourceTree = ""; }; - 262623E62977C371001F9610 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; - 266E9C69299DF224003CC545 /* EditTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTodoView.swift; sourceTree = ""; }; - 266E9C6B299DF279003CC545 /* NewTodoField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTodoField.swift; sourceTree = ""; }; - 267402D62C9BA1E5005FEC8B /* Sorts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sorts.swift; sourceTree = ""; }; 2675DBE42974BA6500492B03 /* To-Day.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "To-Day.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 2675DBE72974BA6500492B03 /* TodayApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayApp.swift; sourceTree = ""; }; - 2675DBEB2974BA6500492B03 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 2675DBEE2974BA6500492B03 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 2675DBF02974BA6500492B03 /* Today.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Today.entitlements; sourceTree = ""; }; - 26B83F572B4B6FCF0002381F /* Completes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Completes.swift; sourceTree = ""; }; 26B83F592B4B824D0002381F /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; - 26BC9AF0297A100700E19AA3 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; - 26D8239A2978CAE70051859E /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = ""; }; 26D8239C2978CD120051859E /* To-Day-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "To-Day-Info.plist"; sourceTree = ""; }; - 26F2268B29762D2A00EDBEF3 /* Todo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Todo.swift; sourceTree = ""; }; - 26F2268D2976347000EDBEF3 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 2658C9452C9BF508008A0A7D /* Today */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Today; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 2675DBE12974BA6500492B03 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -66,32 +35,11 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 262051C529A0B8D8008BAFD0 /* Menu Views */ = { - isa = PBXGroup; - children = ( - 262051C629A0B8F2008BAFD0 /* EditGroup.swift */, - 262051C829A0B950008BAFD0 /* SettingsGroup.swift */, - 262051CA29A0B9A2008BAFD0 /* AppGroup.swift */, - ); - path = "Menu Views"; - sourceTree = ""; - }; - 266E9C68299DF213003CC545 /* Edit Views */ = { - isa = PBXGroup; - children = ( - 262623E42977A8B6001F9610 /* EditView.swift */, - 266E9C69299DF224003CC545 /* EditTodoView.swift */, - 266E9C6B299DF279003CC545 /* NewTodoField.swift */, - 261C2CE929B4746000049616 /* EditButtons.swift */, - ); - path = "Edit Views"; - sourceTree = ""; - }; 2675DBDB2974BA6500492B03 = { isa = PBXGroup; children = ( 26B83F592B4B824D0002381F /* Localizable.xcstrings */, - 2675DBE62974BA6500492B03 /* Today */, + 2658C9452C9BF508008A0A7D /* Today */, 2604F4E42978D9F40036A5BA /* README.md */, 2604F4E52978DFBF0036A5BA /* .gitignore */, 26D8239C2978CD120051859E /* To-Day-Info.plist */, @@ -107,42 +55,6 @@ name = Products; sourceTree = ""; }; - 2675DBE62974BA6500492B03 /* Today */ = { - isa = PBXGroup; - children = ( - 2675DBE72974BA6500492B03 /* TodayApp.swift */, - 262051C529A0B8D8008BAFD0 /* Menu Views */, - 266E9C68299DF213003CC545 /* Edit Views */, - 26BC9AF2297A100E00E19AA3 /* Models */, - 262623E62977C371001F9610 /* AboutView.swift */, - 26D8239A2978CAE70051859E /* Updater.swift */, - 2675DBEB2974BA6500492B03 /* Assets.xcassets */, - 2675DBF02974BA6500492B03 /* Today.entitlements */, - 2675DBED2974BA6500492B03 /* Preview Content */, - ); - path = Today; - sourceTree = ""; - }; - 2675DBED2974BA6500492B03 /* Preview Content */ = { - isa = PBXGroup; - children = ( - 2675DBEE2974BA6500492B03 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - 26BC9AF2297A100E00E19AA3 /* Models */ = { - isa = PBXGroup; - children = ( - 26BC9AF0297A100700E19AA3 /* AppState.swift */, - 26F2268B29762D2A00EDBEF3 /* Todo.swift */, - 26F2268D2976347000EDBEF3 /* DataStore.swift */, - 26B83F572B4B6FCF0002381F /* Completes.swift */, - 267402D62C9BA1E5005FEC8B /* Sorts.swift */, - ); - path = Models; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -159,6 +71,9 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 2658C9452C9BF508008A0A7D /* Today */, + ); name = "To-Day"; packageProductDependencies = ( 26D823982978CA960051859E /* Sparkle */, @@ -209,8 +124,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2675DBEF2974BA6500492B03 /* Preview Assets.xcassets in Resources */, - 2675DBEC2974BA6500492B03 /* Assets.xcassets in Resources */, 26B83F5A2B4B824D0002381F /* Localizable.xcstrings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -243,21 +156,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 26B83F582B4B6FCF0002381F /* Completes.swift in Sources */, - 2675DBE82974BA6500492B03 /* TodayApp.swift in Sources */, - 26F2268E2976347000EDBEF3 /* DataStore.swift in Sources */, - 262051CB29A0B9A2008BAFD0 /* AppGroup.swift in Sources */, - 26BC9AF1297A100700E19AA3 /* AppState.swift in Sources */, - 26F2268C29762D2A00EDBEF3 /* Todo.swift in Sources */, - 267402D72C9BA1E6005FEC8B /* Sorts.swift in Sources */, - 266E9C6A299DF224003CC545 /* EditTodoView.swift in Sources */, - 262623E52977A8B6001F9610 /* EditView.swift in Sources */, - 266E9C6C299DF279003CC545 /* NewTodoField.swift in Sources */, - 261C2CEA29B4746000049616 /* EditButtons.swift in Sources */, - 262623E72977C371001F9610 /* AboutView.swift in Sources */, - 262051C929A0B950008BAFD0 /* SettingsGroup.swift in Sources */, - 262051C729A0B8F2008BAFD0 /* EditGroup.swift in Sources */, - 26D8239B2978CAE70051859E /* Updater.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -393,7 +291,7 @@ CODE_SIGN_ENTITLEMENTS = Today/Today.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 20; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Today/Preview Content\""; DEVELOPMENT_TEAM = TC28MCLCFA; @@ -410,7 +308,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.9.4; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = net.troz.Today; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -427,7 +325,7 @@ CODE_SIGN_ENTITLEMENTS = Today/Today.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 20; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Today/Preview Content\""; DEVELOPMENT_TEAM = TC28MCLCFA; @@ -444,7 +342,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.9.4; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = net.troz.Today; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Today/AboutView.swift b/Today/AboutView.swift index c4db360..b14054b 100644 --- a/Today/AboutView.swift +++ b/Today/AboutView.swift @@ -23,7 +23,7 @@ struct AboutView: View { Text("Use the **Edit Todos…** menu item to add, delete and edit your todos. Select them in the menu to mark them as complete or incomplete.") - Text("There are only two settings: **Show Completed** lets you choose how to display, hide or delete the completed todos and **Launch on Login** sets whether you want the app to start automatically when you log in.") + Text("There are only three settings: **Show Completed** lets you choose how to display, hide or delete the completed todos, **Sort Todos** allows you to sort the todos by the creation date & time or by title, and **Launch on Login** sets whether you want the app to start automatically.") Text("Translation and VoiceOver help by [Sebastian Dellit](www.blindzeln.org).") diff --git a/Today/Edit Views/EditButtons.swift b/Today/Edit Views/EditButtons.swift index aa973a5..145b1dd 100644 --- a/Today/Edit Views/EditButtons.swift +++ b/Today/Edit Views/EditButtons.swift @@ -9,17 +9,16 @@ import SwiftUI struct EditButtons: View { @EnvironmentObject var appState: AppState + @State private var deleteButtonLabel = "Delete All…" + @State private var askForDeleteConfirmation = true var body: some View { HStack { - Button(role: .destructive) { - appState.deleteAll() - } label: { - Text("Delete All") - .foregroundColor(.red) - .opacity(appState.todos.isEmpty ? 0.5 : 1) + if #available(macOS 15.0, *) { + deleteAllButtonWithOption + } else { + deleteAllConfirmButton } - .disabled(appState.todos.isEmpty) Spacer() @@ -30,6 +29,47 @@ struct EditButtons: View { } .padding([.horizontal, .bottom], 12) } + + var deleteAllConfirmButton: some View { + Button(role: .destructive) { + deleteAllTodos() + } label: { + Text("Delete All") + .foregroundColor(.red) + .opacity(appState.todos.isEmpty ? 0.5 : 1) + } + .disabled(appState.todos.isEmpty) + } + + @available(macOS 15.0, *) + var deleteAllButtonWithOption: some View { + Button(role: .destructive) { + deleteAllTodos() + } label: { + Text(deleteButtonLabel) + .foregroundColor(.red) + .opacity(appState.todos.isEmpty ? 0.5 : 1) + } + .help("Hold down Option to delete all without confirmation.") + .onModifierKeysChanged(mask: .option, initial: false) { _, new in + if new .isEmpty { + deleteButtonLabel = "Delete All…" + askForDeleteConfirmation = true + } else { + deleteButtonLabel = "Delete All" + askForDeleteConfirmation = false + } + } + .disabled(appState.todos.isEmpty) + } + + func deleteAllTodos() { + if askForDeleteConfirmation { + appState.deleteAllWithConfirmation() + } else { + appState.deleteAll() + } + } } struct EditButtons_Previews: PreviewProvider { diff --git a/Today/Models/AppState.swift b/Today/Models/AppState.swift index dec1826..52f4011 100644 --- a/Today/Models/AppState.swift +++ b/Today/Models/AppState.swift @@ -200,7 +200,7 @@ extension AppState { } } - func deleteAll() { + func deleteAllWithConfirmation() { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = "Really delete all the todos?" @@ -215,6 +215,10 @@ extension AppState { } } + func deleteAll() { + todos = [] + } + func toggleComplete(_ id: UUID) { let todoIndex = todos.firstIndex { $0.id == id diff --git a/Today/Models/Debouncer.swift b/Today/Models/Debouncer.swift deleted file mode 100644 index 96bb7f6..0000000 --- a/Today/Models/Debouncer.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Debouncer.swift -// To-Day -// -// Created by Sarah Reichelt on 5/3/2023. -// - -import Foundation - -public class Debouncer { - private let timeInterval: TimeInterval - private var timer: Timer? - - typealias Handler = () -> Void - var handler: Handler? - - init(timeInterval: TimeInterval) { - self.timeInterval = timeInterval - } - - public func renewInterval() { - timer?.invalidate() - timer = Timer.scheduledTimer( - withTimeInterval: timeInterval, - repeats: false) { [weak self] (timer) in - self?.timeIntervalDidFinish(for: timer) - } - } - - @objc private func timeIntervalDidFinish(for timer: Timer) { - guard timer.isValid else { - return - } - - handler?() - handler = nil - } -}