From 48e2f4fc10f02bd3079b463e432f306f1d52f572 Mon Sep 17 00:00:00 2001 From: kutesir Date: Tue, 16 Jun 2026 02:12:52 +0300 Subject: [PATCH] Widgets/notifications: tracked-event auto-advance, lock-screen dots, Snooze labels, period milestones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bars widget: tracked ("Track Countdown") events now also advance — a completed or past (non-recurring) tracked event falls through to the next-nearest, and the refresh boundary covers the tracked event's expiry. Recurring tracked events still roll forward. Completes the KC-26 auto-advance for all paths. - Progress Dots on the lock screen (accessoryRectangular): show 28 larger dots instead of 100 tiny ones, render with .primary for the vibrant tint, and use AccessoryWidgetBackground so it's legible (Home Screen keeps the 100-dot grid). - Notifications: snooze action labels now read "Snooze 15 min/30 min/1 hour/ 2 hours" (lock screen) and "Snooze 15 Minutes…" (in-app Postpone menu). - New period milestone notifications: recurring "One more week/month/year passed" at end of week (Sun 20:00), month (1st 09:00), and year (Jan 1 09:00); gated by a kisani.periodMilestones flag, scheduled via the existing reschedule path. Co-Authored-By: Claude Opus 4.8 --- KisaniCal.xcodeproj/project.pbxproj | 38 ++++++++++++---- .../xcshareddata/xcschemes/KisaniCal.xcscheme | 16 ++----- KisaniCal/ISSUES.md | 16 +++---- KisaniCal/Managers/NotificationManager.swift | 43 +++++++++++++++++-- KisaniCal/Views/TaskContextMenu.swift | 8 ++-- KisaniCalWidgets/EventCountdownWidget.swift | 37 +++++++++++----- KisaniCalWidgets/WidgetViews.swift | 16 +++++-- 7 files changed, 122 insertions(+), 52 deletions(-) diff --git a/KisaniCal.xcodeproj/project.pbxproj b/KisaniCal.xcodeproj/project.pbxproj index a751ba7..bfcfbc4 100644 --- a/KisaniCal.xcodeproj/project.pbxproj +++ b/KisaniCal.xcodeproj/project.pbxproj @@ -81,7 +81,7 @@ 0506183945D16EC443A69651 /* LiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityManager.swift; sourceTree = ""; }; 0513EB8C7F6B6853B9E93E09 /* TaskLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskLiveActivity.swift; sourceTree = ""; }; 0C96EC00F6B021DBA3CC1A79 /* ProfileSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSetupView.swift; sourceTree = ""; }; - 0D6D6740721F01A74E404EB9 /* KisaniCalWidgets.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = KisaniCalWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D6D6740721F01A74E404EB9 /* KisaniCalWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = KisaniCalWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 106EEF572C6F8990408329F0 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 1AA7498EFED7692022F3E7E1 /* KisaniCal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KisaniCal.entitlements; sourceTree = ""; }; 208F82348DEBD9FF7B0DCF17 /* MyTasksWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyTasksWidget.swift; sourceTree = ""; }; @@ -105,7 +105,7 @@ 9100804DB1E61EA882CC54DA /* FloatingTabState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingTabState.swift; sourceTree = ""; }; 92824ED40ECD41EFD4F78BEC /* ISSUES.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ISSUES.md; sourceTree = ""; }; 93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = ""; }; - 9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; }; ADF6CCD95A587E26E30F5712 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; AF905C574F34B4EE51A8D21E /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = ""; }; BC5E179A9189B0A8C3F856F6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -292,7 +292,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1500; + LastUpgradeCheck = 2620; TargetAttributes = { 28B43516AD88946E21D9BFE0 = { DevelopmentTeam = K8BLMMR883; @@ -409,6 +409,7 @@ CODE_SIGN_ENTITLEMENTS = KisaniCal/KisaniCal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = Wenza; @@ -420,13 +421,14 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 2; PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal; SDKROOT = iphoneos; SWIFT_VERSION = 5.9; @@ -473,6 +475,7 @@ DEVELOPMENT_TEAM = K8BLMMR883; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -487,6 +490,7 @@ MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -499,10 +503,16 @@ APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = KisaniCalWidgets/KisaniCalWidgets.entitlements; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 8; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = KisaniCalWidgets/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 2; PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets; SDKROOT = iphoneos; SWIFT_VERSION = 5.9; @@ -549,6 +559,7 @@ DEVELOPMENT_TEAM = K8BLMMR883; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -570,6 +581,7 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -582,10 +594,16 @@ APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = KisaniCalWidgets/KisaniCalWidgets.entitlements; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 8; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = KisaniCalWidgets/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 2; PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.KisaniCalWidgets; SDKROOT = iphoneos; SWIFT_VERSION = 5.9; @@ -601,6 +619,7 @@ CODE_SIGN_ENTITLEMENTS = KisaniCal/KisaniCal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = Wenza; @@ -612,13 +631,14 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 2; PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal; SDKROOT = iphoneos; SWIFT_VERSION = 5.9; diff --git a/KisaniCal.xcodeproj/xcshareddata/xcschemes/KisaniCal.xcscheme b/KisaniCal.xcodeproj/xcshareddata/xcschemes/KisaniCal.xcscheme index 9db359c..675309e 100644 --- a/KisaniCal.xcodeproj/xcshareddata/xcschemes/KisaniCal.xcscheme +++ b/KisaniCal.xcodeproj/xcshareddata/xcschemes/KisaniCal.xcscheme @@ -1,11 +1,10 @@ + LastUpgradeVersion = "2620" + version = "1.3"> + buildImplicitDependencies = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - diff --git a/KisaniCal/ISSUES.md b/KisaniCal/ISSUES.md index 3ae9187..3c68877 100644 --- a/KisaniCal/ISSUES.md +++ b/KisaniCal/ISSUES.md @@ -722,10 +722,10 @@ snooze choices directly from the pressed item and notification actions. ### Fix (app task menus) - Added a shared **Postpone** submenu to `TaskMenuItems`: - - 15 Minutes - - 30 Minutes - - 1 Hour - - 2 Hours + - Snooze 15 Minutes + - Snooze 30 Minutes + - Snooze 1 Hour + - Snooze 2 Hours - Tomorrow - Wired the submenu through: - Today timeline + overdue/upcoming sections @@ -738,10 +738,10 @@ snooze choices directly from the pressed item and notification actions. ### Fix (lock screen notifications) - Task reminder notifications now include actions: - - 15 min - - 30 min - - 1 hour - - 2 hours + - Snooze 15 min + - Snooze 30 min + - Snooze 1 hour + - Snooze 2 hours - Notification response handler updates the stored task reminder and schedules a one-off snoozed notification for the selected delay. diff --git a/KisaniCal/Managers/NotificationManager.swift b/KisaniCal/Managers/NotificationManager.swift index 38d9540..ea1d6d4 100644 --- a/KisaniCal/Managers/NotificationManager.swift +++ b/KisaniCal/Managers/NotificationManager.swift @@ -30,10 +30,10 @@ final class NotificationManager: NSObject, ObservableObject, @unchecked Sendable private func registerCategories() { let markAction = UNNotificationAction(identifier: Self.actionMarkId, title: "Mark Complete", options: []) let taskSnoozeActions = [ - UNNotificationAction(identifier: Self.taskSnoozePrefix + "15", title: "15 min", options: []), - UNNotificationAction(identifier: Self.taskSnoozePrefix + "30", title: "30 min", options: []), - UNNotificationAction(identifier: Self.taskSnoozePrefix + "60", title: "1 hour", options: []), - UNNotificationAction(identifier: Self.taskSnoozePrefix + "120", title: "2 hours", options: []) + UNNotificationAction(identifier: Self.taskSnoozePrefix + "15", title: "Snooze 15 min", options: []), + UNNotificationAction(identifier: Self.taskSnoozePrefix + "30", title: "Snooze 30 min", options: []), + UNNotificationAction(identifier: Self.taskSnoozePrefix + "60", title: "Snooze 1 hour", options: []), + UNNotificationAction(identifier: Self.taskSnoozePrefix + "120", title: "Snooze 2 hours", options: []) ] let taskCat = UNNotificationCategory(identifier: Self.categoryId, actions: [markAction] + taskSnoozeActions, intentIdentifiers: [], options: .customDismissAction) @@ -129,10 +129,45 @@ final class NotificationManager: NSObject, ObservableObject, @unchecked Sendable self.scheduleCompensations(compensations, programs: programs) self.scheduleTasks(snapshot: tasks) self.scheduleCalendarEvents(snapshot: calEvents) + self.schedulePeriodMilestones() self.updateBadge(snapshot: tasks) } } + // MARK: - Period milestones ("one more week / month / year passed") + + private static let periodIds = ["kisani.period.week", "kisani.period.month", "kisani.period.year"] + + /// Recurring nudges at the close of each week, month, and year. Fixed identifiers + /// + remove-then-add keep them de-duplicated across reschedules. Disabled (and + /// cleared) when the user turns off the "Period milestones" setting. + private func schedulePeriodMilestones() { + center.removePendingNotificationRequests(withIdentifiers: Self.periodIds) + guard UserDefaults.kisani.object(forKey: "kisani.periodMilestones") as? Bool ?? true else { return } + + func add(_ id: String, _ title: String, _ body: String, _ comps: DateComponents) { + let content = UNMutableNotificationContent() + content.title = title + content.body = body + content.sound = .default + center.add(UNNotificationRequest( + identifier: id, content: content, + trigger: UNCalendarNotificationTrigger(dateMatching: comps, repeats: true))) + } + + // End of week → Sunday 20:00 + var week = DateComponents(); week.weekday = 1; week.hour = 20; week.minute = 0 + add(Self.periodIds[0], "This Week", "One more week passed.", week) + + // End of month → 1st of the next month, 09:00 + var month = DateComponents(); month.day = 1; month.hour = 9; month.minute = 0 + add(Self.periodIds[1], "This Month", "One more month passed.", month) + + // End of year → Jan 1, 09:00 + var year = DateComponents(); year.month = 1; year.day = 1; year.hour = 9; year.minute = 0 + add(Self.periodIds[2], "This Year", "One more year passed.", year) + } + // MARK: - Compensation workout reminders (one-off, on chosen rest days) private func scheduleCompensations(_ comps: [String: String], programs: [WorkoutProgram]) { diff --git a/KisaniCal/Views/TaskContextMenu.swift b/KisaniCal/Views/TaskContextMenu.swift index 8341f7a..ac47335 100644 --- a/KisaniCal/Views/TaskContextMenu.swift +++ b/KisaniCal/Views/TaskContextMenu.swift @@ -71,16 +71,16 @@ struct TaskMenuItems: View { Menu { Button { onPostponeMinutes(task, 15) } label: { - Label("15 Minutes", systemImage: "clock") + Label("Snooze 15 Minutes", systemImage: "clock") } Button { onPostponeMinutes(task, 30) } label: { - Label("30 Minutes", systemImage: "clock") + Label("Snooze 30 Minutes", systemImage: "clock") } Button { onPostponeMinutes(task, 60) } label: { - Label("1 Hour", systemImage: "clock") + Label("Snooze 1 Hour", systemImage: "clock") } Button { onPostponeMinutes(task, 120) } label: { - Label("2 Hours", systemImage: "clock") + Label("Snooze 2 Hours", systemImage: "clock") } Divider() Button { onSetDate(task, preservingTime(on: dayOffset(1))) } label: { diff --git a/KisaniCalWidgets/EventCountdownWidget.swift b/KisaniCalWidgets/EventCountdownWidget.swift index e115d5e..bbed562 100644 --- a/KisaniCalWidgets/EventCountdownWidget.swift +++ b/KisaniCalWidgets/EventCountdownWidget.swift @@ -238,11 +238,26 @@ struct EventProvider: AppIntentTimelineProvider { isRecurring: $0.isRecurring, recurrenceLabel: $0.recurrenceLabel) } } - /// Due date of the event the auto-select queue is currently showing (nil if not in - /// queue mode, an event is tracked, or there's nothing upcoming). + /// The tracked task ("Track Countdown"), but only while it's still live: not + /// completed, and either recurring or with a deadline still ahead of `now`. + /// Returns nil once it's done/expired so the widget advances to the next event. + private func trackedTask(asOf now: Date) -> WidgetTask? { + guard let trackedId = UserDefaults.kisani.string(forKey: "kisani.widget.trackedEvent"), + let task = loadWidgetTasks().first(where: { $0.id.uuidString == trackedId }), + let due = task.dueDate, + !task.isComplete, + task.isRecurring || due > now + else { return nil } + return task + } + + /// Due date the currently-shown event expires at, so the timeline can refresh + /// the moment it's done. Covers both the tracked event and the auto-select queue. private func currentSelectionExpiry(for config: EventConfigIntent, asOf now: Date) -> Date? { - guard config.upcomingQueue, - UserDefaults.kisani.string(forKey: "kisani.widget.trackedEvent") == nil else { return nil } + if let task = trackedTask(asOf: now) { + return task.isRecurring ? nil : task.dueDate + } + guard config.upcomingQueue else { return nil } let upcoming = upcomingEvents(asOf: now) let idx = max(0, config.autoSelect.rawValue - 1) guard idx < upcoming.count else { return nil } @@ -266,14 +281,14 @@ struct EventProvider: AppIntentTimelineProvider { let mode = UserDefaults.kisani.integer(forKey: countdownUnitKey) // An event tracked from the app ("Track Countdown") bypasses the widget - // configuration entirely — title, date, and progress come straight from - // App Group storage. - if let trackedId = UserDefaults.kisani.string(forKey: "kisani.widget.trackedEvent"), - let task = loadWidgetTasks().first(where: { $0.id.uuidString == trackedId }), - let due = task.dueDate { + // configuration entirely. Once it's completed or its deadline has passed + // (and it isn't recurring), stop pinning it and fall through to the + // next-nearest event below — so the widget always advances. + if let task = trackedTask(asOf: now) { + let trackedId = task.id.uuidString let since = UserDefaults.kisani.object(forKey: "kisani.widget.trackedSince") as? Date - let s = resolveSpan(due: due, isRecurring: task.isRecurring, label: task.recurrenceLabel, - anchorKey: trackedId, explicitStart: since) + let s = resolveSpan(due: task.dueDate ?? now, isRecurring: task.isRecurring, + label: task.recurrenceLabel, anchorKey: trackedId, explicitStart: since) return EventEntry(date: now, eventName: task.title, start: s.start, target: s.target, unitMode: mode) } diff --git a/KisaniCalWidgets/WidgetViews.swift b/KisaniCalWidgets/WidgetViews.swift index 01dce79..867999f 100644 --- a/KisaniCalWidgets/WidgetViews.swift +++ b/KisaniCalWidgets/WidgetViews.swift @@ -77,25 +77,33 @@ struct ProgressDotView: View { } } + private var isAccessory: Bool { family == .accessoryRectangular } + + // 100 tiny dots are illegible in a lock-screen pill — show a coarse grid there. + private var dotCount: Int { isAccessory ? 28 : entry.totalDots } + var body: some View { let percent = Int((entry.progress * 100).rounded(.down)) VStack(alignment: .leading, spacing: spacing) { HStack(alignment: .firstTextBaseline) { Text(entry.title) .font(headerFont) - .foregroundColor(.white) + .foregroundColor(isAccessory ? .primary : .white) .lineLimit(1) .minimumScaleFactor(0.72) Spacer() Text("\(percent)%") .font(headerFont) - .foregroundColor(Color(white: 0.5)) + .foregroundColor(isAccessory ? .primary.opacity(0.7) : Color(white: 0.5)) .lineLimit(1) } - DotGridView(progress: entry.progress, totalDots: entry.totalDots, color: accent) + DotGridView(progress: entry.progress, totalDots: dotCount, + color: isAccessory ? .primary : accent) } .padding(.horizontal, horizontalPadding) - .containerBackground(WidgetTheme.background, for: .widget) + .containerBackground(for: .widget) { + if isAccessory { AccessoryWidgetBackground() } else { WidgetTheme.background } + } } }