Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions DevLog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
DEVELOPMENT_TEAM = 4CPC6N38WA;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog_Unit;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = NO;
Expand All @@ -421,7 +421,7 @@
DEVELOPMENT_TEAM = 4CPC6N38WA;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog_Unit;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = NO;
Expand Down Expand Up @@ -457,7 +457,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog.DevLogWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -490,7 +490,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog.DevLogWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -536,7 +536,7 @@
"@executable_path/Frameworks",
);
LOCALIZED_STRING_SWIFTUI_SUPPORT = YES;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -583,7 +583,7 @@
"@executable_path/Frameworks",
);
LOCALIZED_STRING_SWIFTUI_SUPPORT = YES;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
3 changes: 2 additions & 1 deletion DevLog/App/Assembler/DataAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ final class DataAssembler: Assembler {
AuthenticationService.self,
name: "GoogleAuthenticationService"
),
userService: container.resolve(UserService.self)
userService: container.resolve(UserService.self),
widgetSnapshotUpdater: container.resolve(WidgetSnapshotUpdater.self)
)
}

Expand Down
9 changes: 8 additions & 1 deletion DevLog/Data/Repository/AuthenticationRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
private let githubAuthService: AuthenticationService
private let googleAuthService: AuthenticationService
private let userService: UserService
private let widgetSnapshotUpdater: WidgetSnapshotUpdater

init(
authService: AuthService,
appleAuthService: AuthenticationService,
githubAuthService: AuthenticationService,
googleAuthService: AuthenticationService,
userService: UserService
userService: UserService,
widgetSnapshotUpdater: WidgetSnapshotUpdater
) {
self.authService = authService
self.appleAuthService = appleAuthService
self.githubAuthService = githubAuthService
self.googleAuthService = googleAuthService
self.userService = userService
self.widgetSnapshotUpdater = widgetSnapshotUpdater
}

func signIn(_ provider: AuthProvider) async throws {
Expand Down Expand Up @@ -58,6 +61,7 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
let provider = AuthProvider(rawValue: providerID)
else {
try await authService.clearCurrentSession()
widgetSnapshotUpdater.clear()
return
}

Expand All @@ -73,6 +77,8 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
} catch AuthError.notAuthenticated {
try await authService.clearCurrentSession()
}

widgetSnapshotUpdater.clear()
}

func restore() -> Bool {
Expand Down Expand Up @@ -100,5 +106,6 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {

try await authService.deleteCurrentUser()
try await authService.clearCurrentSession()
widgetSnapshotUpdater.clear()
}
}
26 changes: 16 additions & 10 deletions DevLog/Storage/Persistence/WidgetSnapshotPreferenceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import Foundation

final class WidgetSnapshotPreferenceStore {
private enum Key {
static let heatmapActivityTypes = "Profile.heatmap.activityTypes"
static let todayDueDateVisibility = "Today.dueDateVisibility"
static let todayFocusVisibility = "Today.focusVisibility"
private enum Key: String, CaseIterable {
case heatmapActivityTypes = "Profile.heatmap.activityTypes"
case todayDueDateVisibility = "Today.dueDateVisibility"
case todayFocusVisibility = "Today.focusVisibility"
}

private let userDefaults: UserDefaults
Expand All @@ -21,11 +21,11 @@ final class WidgetSnapshotPreferenceStore {
}

func heatmapActivityTypes() -> [String] {
userDefaults.stringArray(forKey: Key.heatmapActivityTypes) ?? []
userDefaults.stringArray(forKey: Key.heatmapActivityTypes.rawValue) ?? []
}

func setHeatmapActivityTypes(_ activityTypes: [String]) {
userDefaults.set(activityTypes, forKey: Key.heatmapActivityTypes)
userDefaults.set(activityTypes, forKey: Key.heatmapActivityTypes.rawValue)
}

func selectedActivityKinds() -> Set<ActivityKind> {
Expand All @@ -41,8 +41,8 @@ final class WidgetSnapshotPreferenceStore {
}

func todayDisplayOptions() -> TodayDisplayOptions {
let dueDateVisibilityRawValue = userDefaults.string(forKey: Key.todayDueDateVisibility)
let focusVisibilityRawValue = userDefaults.string(forKey: Key.todayFocusVisibility)
let dueDateVisibilityRawValue = userDefaults.string(forKey: Key.todayDueDateVisibility.rawValue)
let focusVisibilityRawValue = userDefaults.string(forKey: Key.todayFocusVisibility.rawValue)

return TodayDisplayOptions(
dueDateVisibility: TodayDisplayOptions.DueDateVisibility(
Expand All @@ -55,7 +55,13 @@ final class WidgetSnapshotPreferenceStore {
}

func setTodayDisplayOptions(_ options: TodayDisplayOptions) {
userDefaults.set(options.dueDateVisibility.rawValue, forKey: Key.todayDueDateVisibility)
userDefaults.set(options.focusVisibility.rawValue, forKey: Key.todayFocusVisibility)
userDefaults.set(options.dueDateVisibility.rawValue, forKey: Key.todayDueDateVisibility.rawValue)
userDefaults.set(options.focusVisibility.rawValue, forKey: Key.todayFocusVisibility.rawValue)
}

func clear() {
Key.allCases.forEach {
userDefaults.removeObject(forKey: $0.rawValue)
}
}
}
6 changes: 6 additions & 0 deletions DevLog/Storage/Persistence/WidgetSnapshotUpdater.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,10 @@ final class WidgetSnapshotUpdater {
)
}
}

func clear() {
snapshotStore.clearSnapshots()
Comment thread
opficdev marked this conversation as resolved.
preferenceStore.clear()
WidgetCenter.shared.reloadAllTimelines()
}
}
4 changes: 4 additions & 0 deletions DevLog/Widget/Common/WidgetSharedDefaultsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ final class WidgetSharedDefaultsStore {
func setData(_ value: Data?, forKey key: String) {
userDefaults.set(value, forKey: key)
}

func removeObject(forKey key: String) {
userDefaults.removeObject(forKey: key)
}
}
6 changes: 6 additions & 0 deletions DevLog/Widget/Common/WidgetSnapshotStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@ final class WidgetSnapshotStore {
guard let data = store.data(forKey: WidgetSnapshotKey.heatmap) else { return nil }
return try decoder.decode(HeatmapWidgetSnapshot.self, from: data)
}

func clearSnapshots() {
WidgetSnapshotKey.snapshots.forEach {
store.removeObject(forKey: $0)
}
}
}
2 changes: 1 addition & 1 deletion DevLogWidget/Heatmap/HeatmapWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct HeatmapWidget: Widget {
.containerBackground(.fill.tertiary, for: .widget)
.widgetURL(WidgetDeepLink.heatmapURL)
}
.configurationDisplayName("Heatmap")
.configurationDisplayName(LocalizedStringResource("widget_heatmap_title"))
.description("widget_heatmap_description")
.supportedFamilies([.systemSmall, .systemMedium])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import AppIntents
import WidgetKit

struct HeatmapWidgetConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Heatmap"
static var title: LocalizedStringResource = "widget_heatmap_title"
static var description = IntentDescription("widget_heatmap_description")
}
40 changes: 34 additions & 6 deletions DevLogWidget/Resource/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
},
"%lld" : {

},
"Heatmap" : {

},
"Today" : {

},
"widget_heatmap_current_month_title" : {
"extractionState" : "manual",
Expand Down Expand Up @@ -64,6 +58,23 @@
}
}
},
"widget_heatmap_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Heatmap"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "Heatmap"
}
}
}
},
"widget_today_description" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -97,6 +108,23 @@
}
}
}
},
"widget_today_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Today"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "Today"
}
}
}
}
},
"version" : "1.0"
Expand Down
2 changes: 1 addition & 1 deletion DevLogWidget/Today/TodayTodoWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct TodayTodoWidget: Widget {
.widgetURL(WidgetDeepLink.todayTodoURL)
}
.description("widget_today_description")
.configurationDisplayName("Today")
.configurationDisplayName(LocalizedStringResource("widget_today_title"))
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import AppIntents
import WidgetKit

struct TodayTodoWidgetConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Today"
static var title: LocalizedStringResource = "widget_today_title"
static var description = IntentDescription("widget_today_description")
}
2 changes: 1 addition & 1 deletion DevLogWidget/Today/TodayTodoWidgetEntryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct TodayTodoWidgetEntryView: View {

var body: some View {
VStack(alignment: .leading) {
Text("Today")
Text("widget_today_title")
.font(.headline)

Spacer()
Expand Down
50 changes: 48 additions & 2 deletions DevLog_Unit/Widget/WidgetSnapshotUpdaterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,55 @@ struct WidgetSnapshotUpdaterTests {
#expect(snapshot.maxCount == 1)
}

@Test("WidgetSnapshotUpdater는 모든 위젯 스냅샷을 삭제한다")
func widgetSnapshotUpdater는_모든_위젯_스냅샷을_삭제한다() throws {
let calendar = Calendar(identifier: .gregorian)
let quarterStart = try #require(calendar.date(from: DateComponents(year: 2026, month: 4, day: 1)))
let now = try #require(calendar.date(from: DateComponents(year: 2026, month: 4, day: 30)))
let fixture = makeFixture(calendar: calendar)
let todo = try makeTodayTodoItem(now: now)
fixture.preferenceStore.setHeatmapActivityTypes(["created"])
fixture.preferenceStore.setTodayDisplayOptions(
TodayDisplayOptions(
dueDateVisibility: .withDueDateOnly,
focusVisibility: .focusedOnly
)
)

fixture.updater.updateTodaySnapshot(
todos: [todo],
displayOptions: .default,
now: now
)
fixture.updater.updateHeatmapSnapshot(
createdTodos: [
makeTodo(
id: "created",
createdAt: now
)
],
completedTodos: [],
deletedTodos: [],
selectedActivityKinds: [.created],
quarterStart: quarterStart,
now: now
)

fixture.updater.clear()

#expect(try fixture.snapshotStore.loadTodaySnapshot() == nil)
#expect(try fixture.snapshotStore.loadHeatmapSnapshot() == nil)
#expect(fixture.preferenceStore.heatmapActivityTypes().isEmpty)
#expect(fixture.preferenceStore.todayDisplayOptions() == .default)
}

private func makeFixture(
calendar: Calendar = .current
) -> (updater: WidgetSnapshotUpdater, snapshotStore: WidgetSnapshotStore) {
) -> (
updater: WidgetSnapshotUpdater,
snapshotStore: WidgetSnapshotStore,
preferenceStore: WidgetSnapshotPreferenceStore
) {
let suiteName = "WidgetSnapshotUpdaterTests.\(UUID().uuidString)"
let userDefaults = UserDefaults(suiteName: suiteName) ?? .standard
userDefaults.removePersistentDomain(forName: suiteName)
Expand All @@ -83,7 +129,7 @@ struct WidgetSnapshotUpdaterTests {
preferenceStore: preferenceStore,
heatmapFactory: HeatmapWidgetSnapshotFactory(calendar: calendar)
)
return (updater, snapshotStore)
return (updater, snapshotStore, preferenceStore)
}

private func makeTodayTodoItem(now: Date) throws -> TodayTodoItem {
Expand Down
1 change: 1 addition & 0 deletions WidgetShared/WidgetSnapshotKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ import Foundation
enum WidgetSnapshotKey {
static let today = "Widget.today.snapshot"
static let heatmap = "Widget.heatmap.snapshot"
static let snapshots = [today, heatmap]
}
Loading