Fix month scroll event disappearance by improving visible month detection and cache refresh rules

This commit is contained in:
Scarriffle
2026-05-25 10:33:31 +02:00
parent 8b3cc11e25
commit d1004a9111
3 changed files with 263 additions and 25 deletions

View File

@@ -6,6 +6,10 @@
objectVersion = 77; objectVersion = 77;
objects = { objects = {
/* Begin PBXBuildFile section */
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
3927C7C02FB99D0F00EAD8ED /* PBXContainerItemProxy */ = { 3927C7C02FB99D0F00EAD8ED /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
@@ -14,13 +18,45 @@
remoteGlobalIDString = C0000A01FB4E10100AB5001; remoteGlobalIDString = C0000A01FB4E10100AB5001;
remoteInfo = "Calendarr iOS"; remoteInfo = "Calendarr iOS";
}; };
D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0000301FB4E10100AB5001 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0001A01ABCDEF0100AB5001;
remoteInfo = CalendarrWidgets;
};
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
D0001A0CABCDEF0100AB5001 /* CalendarrWidgets.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Calendarr iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Calendarr iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
C0000B01FC4E10100AB5001 /* Calendarr iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Calendarr iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C0000B01FC4E10100AB5001 /* Calendarr iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Calendarr iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CalendarrWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
D0001A0FABCDEF0100AB5001 /* Exceptions for "CalendarrWidgets" folder in "CalendarrWidgets" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFileSystemSynchronizedRootGroup section */
3927C7BD2FB99D0F00EAD8ED /* Calendarr iOSTests */ = { 3927C7BD2FB99D0F00EAD8ED /* Calendarr iOSTests */ = {
isa = PBXFileSystemSynchronizedRootGroup; isa = PBXFileSystemSynchronizedRootGroup;
@@ -32,6 +68,19 @@
path = "Calendarr iOS"; path = "Calendarr iOS";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D0001A03ABCDEF0100AB5001 /* CalendarrWidgets */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
D0001A0FABCDEF0100AB5001 /* Exceptions for "CalendarrWidgets" folder in "CalendarrWidgets" target */,
);
path = CalendarrWidgets;
sourceTree = "<group>";
};
D0001A04ABCDEF0100AB5001 /* Shared */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = Shared;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */ /* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -49,6 +98,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
D0001A06ABCDEF0100AB5001 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
@@ -56,6 +112,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C0000D01FC4E10100AB5001 /* Calendarr iOS */, C0000D01FC4E10100AB5001 /* Calendarr iOS */,
D0001A03ABCDEF0100AB5001 /* CalendarrWidgets */,
D0001A04ABCDEF0100AB5001 /* Shared */,
3927C7BD2FB99D0F00EAD8ED /* Calendarr iOSTests */, 3927C7BD2FB99D0F00EAD8ED /* Calendarr iOSTests */,
C0000C01FB4E10100AB5001 /* Products */, C0000C01FB4E10100AB5001 /* Products */,
); );
@@ -66,6 +124,7 @@
children = ( children = (
C0000B01FC4E10100AB5001 /* Calendarr iOS.app */, C0000B01FC4E10100AB5001 /* Calendarr iOS.app */,
3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */, 3927C7BC2FB99D0E00EAD8ED /* Calendarr iOSTests.xctest */,
D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -103,13 +162,16 @@
C0000701FB4E10100AB5001 /* Sources */, C0000701FB4E10100AB5001 /* Sources */,
C0000801FB4E10100AB5001 /* Frameworks */, C0000801FB4E10100AB5001 /* Frameworks */,
C0000901FB4E10100AB5001 /* Resources */, C0000901FB4E10100AB5001 /* Resources */,
D0001A0BABCDEF0100AB5001 /* Embed Foundation Extensions */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */,
); );
fileSystemSynchronizedGroups = ( fileSystemSynchronizedGroups = (
C0000D01FC4E10100AB5001 /* Calendarr iOS */, C0000D01FC4E10100AB5001 /* Calendarr iOS */,
D0001A04ABCDEF0100AB5001 /* Shared */,
); );
name = "Calendarr iOS"; name = "Calendarr iOS";
packageProductDependencies = ( packageProductDependencies = (
@@ -118,6 +180,29 @@
productReference = C0000B01FC4E10100AB5001 /* Calendarr iOS.app */; productReference = C0000B01FC4E10100AB5001 /* Calendarr iOS.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0001A08ABCDEF0100AB5001 /* Build configuration list for PBXNativeTarget "CalendarrWidgets" */;
buildPhases = (
D0001A05ABCDEF0100AB5001 /* Sources */,
D0001A06ABCDEF0100AB5001 /* Frameworks */,
D0001A07ABCDEF0100AB5001 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
D0001A03ABCDEF0100AB5001 /* CalendarrWidgets */,
D0001A04ABCDEF0100AB5001 /* Shared */,
);
name = CalendarrWidgets;
packageProductDependencies = (
);
productName = CalendarrWidgets;
productReference = D0001A02ABCDEF0100AB5001 /* CalendarrWidgets.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
@@ -135,6 +220,9 @@
C0000A01FB4E10100AB5001 = { C0000A01FB4E10100AB5001 = {
CreatedOnToolsVersion = 26.4.1; CreatedOnToolsVersion = 26.4.1;
}; };
D0001A01ABCDEF0100AB5001 = {
CreatedOnToolsVersion = 26.5;
};
}; };
}; };
buildConfigurationList = C0000601FB4E10100AB5001 /* Build configuration list for PBXProject "Calendarr iOS" */; buildConfigurationList = C0000601FB4E10100AB5001 /* Build configuration list for PBXProject "Calendarr iOS" */;
@@ -153,6 +241,7 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
C0000A01FB4E10100AB5001 /* Calendarr iOS */, C0000A01FB4E10100AB5001 /* Calendarr iOS */,
D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */,
3927C7BB2FB99D0E00EAD8ED /* Calendarr iOSTests */, 3927C7BB2FB99D0E00EAD8ED /* Calendarr iOSTests */,
); );
}; };
@@ -173,6 +262,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
D0001A07ABCDEF0100AB5001 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@@ -190,6 +286,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
D0001A05ABCDEF0100AB5001 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
@@ -198,6 +301,11 @@
target = C0000A01FB4E10100AB5001 /* Calendarr iOS */; target = C0000A01FB4E10100AB5001 /* Calendarr iOS */;
targetProxy = 3927C7C02FB99D0F00EAD8ED /* PBXContainerItemProxy */; targetProxy = 3927C7C02FB99D0F00EAD8ED /* PBXContainerItemProxy */;
}; };
D0001A0EABCDEF0100AB5001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0001A01ABCDEF0100AB5001 /* CalendarrWidgets */;
targetProxy = D0001A0DABCDEF0100AB5001 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
@@ -206,22 +314,23 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.5; IPHONEOS_DEPLOYMENT_TARGET = 26.5;
MACOSX_DEPLOYMENT_TARGET = 26.5; MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.local.scarriffle.Calendarr-iOSTests"; PRODUCT_BUNDLE_IDENTIFIER = "com.local.scarriffle.Calendarr-iOSTests";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto; SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO; STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7"; TARGETED_DEVICE_FAMILY = 1;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calendarr iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Calendarr iOS"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calendarr iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Calendarr iOS";
XROS_DEPLOYMENT_TARGET = 26.5; XROS_DEPLOYMENT_TARGET = 26.5;
}; };
@@ -232,22 +341,23 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.5; IPHONEOS_DEPLOYMENT_TARGET = 26.5;
MACOSX_DEPLOYMENT_TARGET = 26.5; MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.local.scarriffle.Calendarr-iOSTests"; PRODUCT_BUNDLE_IDENTIFIER = "com.local.scarriffle.Calendarr-iOSTests";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto; SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO; STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7"; TARGETED_DEVICE_FAMILY = 1;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calendarr iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Calendarr iOS"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calendarr iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Calendarr iOS";
XROS_DEPLOYMENT_TARGET = 26.5; XROS_DEPLOYMENT_TARGET = 26.5;
}; };
@@ -379,14 +489,15 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Calendarr iOS/Calendarr iOS.entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Calendarr; INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
INFOPLIST_KEY_CFBundleName = Calendarr; INFOPLIST_KEY_CFBundleName = Calendarr;
"INFOPLIST_KEY_ITSAppUsesNonExemptEncryption" = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSAppTransportSecurity_NSAllowsArbitraryLoads = YES; INFOPLIST_KEY_NSAppTransportSecurity_NSAllowsArbitraryLoads = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices"; INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -398,7 +509,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios; PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios;
PRODUCT_NAME = "Calendarr iOS"; PRODUCT_NAME = "Calendarr iOS";
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -420,14 +531,15 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Calendarr iOS/Calendarr iOS.entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Calendarr; INFOPLIST_KEY_CFBundleDisplayName = Calendarr;
INFOPLIST_KEY_CFBundleName = Calendarr; INFOPLIST_KEY_CFBundleName = Calendarr;
"INFOPLIST_KEY_ITSAppUsesNonExemptEncryption" = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSAppTransportSecurity_NSAllowsArbitraryLoads = YES; INFOPLIST_KEY_NSAppTransportSecurity_NSAllowsArbitraryLoads = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices"; INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -439,7 +551,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios; PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios;
PRODUCT_NAME = "Calendarr iOS"; PRODUCT_NAME = "Calendarr iOS";
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -456,6 +568,75 @@
}; };
name = Release; name = Release;
}; };
D0001A09ABCDEF0100AB5001 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_ENTITLEMENTS = CalendarrWidgets/CalendarrWidgets.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = CalendarrWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Calendarr Widgets";
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.CalendarrWidgets;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
D0001A0AABCDEF0100AB5001 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_ENTITLEMENTS = CalendarrWidgets/CalendarrWidgets.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = PP34X97WS3;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = CalendarrWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Calendarr Widgets";
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2026 Scarriffleservices";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.scarriffleservices.calendarr.ios.CalendarrWidgets;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
@@ -486,6 +667,15 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
D0001A08ABCDEF0100AB5001 /* Build configuration list for PBXNativeTarget "CalendarrWidgets" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0001A09ABCDEF0100AB5001 /* Debug */,
D0001A0AABCDEF0100AB5001 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = C0000301FB4E10100AB5001 /* Project object */; rootObject = C0000301FB4E10100AB5001 /* Project object */;

View File

@@ -15,6 +15,7 @@ struct CalendarHostView: View {
@State private var editingEvent: CalEvent? = nil @State private var editingEvent: CalEvent? = nil
@State private var selectedEvent: CalEvent? = nil @State private var selectedEvent: CalEvent? = nil
@State private var visibleMonth: Date = .now @State private var visibleMonth: Date = .now
@State private var showFilter = false
private var titleString: String { private var titleString: String {
if store.viewType == .month { if store.viewType == .month {
@@ -67,6 +68,9 @@ struct CalendarHostView: View {
.onChange(of: store.viewType) { _, _ in Task { await onNavigate() } } .onChange(of: store.viewType) { _, _ in Task { await onNavigate() } }
.onChange(of: cacheMonths) { _, _ in Task { await recache() } } .onChange(of: cacheMonths) { _, _ in Task { await recache() } }
.onChange(of: visibleMonth) { _, new in Task { await ensureLoaded(around: new) } } .onChange(of: visibleMonth) { _, new in Task { await ensureLoaded(around: new) } }
.onReceive(NotificationCenter.default.publisher(for: .banishedCalendarsChanged)) { _ in
store.syncBanishedFromDefaults()
}
} }
// MARK: Liquid Glass variant // MARK: Liquid Glass variant
@@ -102,6 +106,11 @@ struct CalendarHostView: View {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
HStack(spacing: 8) { HStack(spacing: 8) {
viewPickerMenu viewPickerMenu
Button { showFilter = true } label: {
Image(systemName: "line.3.horizontal.decrease.circle")
.foregroundStyle(store.hiddenCalendarKeys.isEmpty ? .primary : Color.accentColor)
}
.accessibilityLabel(L10n.t("filter.button", appLang))
Button { showMenu = true } label: { Image(systemName: "line.3.horizontal") } Button { showMenu = true } label: { Image(systemName: "line.3.horizontal") }
} }
} }
@@ -114,6 +123,9 @@ struct CalendarHostView: View {
.onChange(of: store.viewType) { _, _ in Task { await onNavigate() } } .onChange(of: store.viewType) { _, _ in Task { await onNavigate() } }
.onChange(of: cacheMonths) { _, _ in Task { await recache() } } .onChange(of: cacheMonths) { _, _ in Task { await recache() } }
.onChange(of: visibleMonth) { _, new in Task { await ensureLoaded(around: new) } } .onChange(of: visibleMonth) { _, new in Task { await ensureLoaded(around: new) } }
.onReceive(NotificationCenter.default.publisher(for: .banishedCalendarsChanged)) { _ in
store.syncBanishedFromDefaults()
}
} }
// MARK: Top bar (flat mode) // MARK: Top bar (flat mode)
@@ -142,6 +154,7 @@ struct CalendarHostView: View {
.minimumScaleFactor(0.7) .minimumScaleFactor(0.7)
Spacer(minLength: 8) Spacer(minLength: 8)
viewPickerMenu viewPickerMenu
filterButton
Button { showMenu = true } label: { Button { showMenu = true } label: {
Image(systemName: "line.3.horizontal") Image(systemName: "line.3.horizontal")
.font(.system(size: 18, weight: .medium)) .font(.system(size: 18, weight: .medium))
@@ -153,6 +166,16 @@ struct CalendarHostView: View {
.background(.bar) .background(.bar)
} }
private var filterButton: some View {
Button { showFilter = true } label: {
Image(systemName: "line.3.horizontal.decrease.circle")
.font(.system(size: 17, weight: .medium))
.foregroundStyle(store.hiddenCalendarKeys.isEmpty ? .primary : Color.accentColor)
.frame(width: 40, height: 40)
}
.accessibilityLabel(L10n.t("filter.button", appLang))
}
private var viewPickerMenu: some View { private var viewPickerMenu: some View {
Menu { Menu {
ForEach(CalViewType.allCases, id: \.self) { vt in ForEach(CalViewType.allCases, id: \.self) { vt in
@@ -298,8 +321,8 @@ struct CalendarHostView: View {
private var calendarSheets: CalendarSheets { private var calendarSheets: CalendarSheets {
CalendarSheets(store: store, showEditor: $showEditor, CalendarSheets(store: store, showEditor: $showEditor,
editorDate: $editorDate, editingEvent: $editingEvent, editorDate: $editorDate, editingEvent: $editingEvent,
selectedEvent: $selectedEvent, api: api, selectedEvent: $selectedEvent, showFilter: $showFilter,
reload: { await onNavigate() }) api: api, reload: { await onNavigate() })
} }
// MARK: Loading logic // MARK: Loading logic
@@ -327,13 +350,21 @@ struct CalendarHostView: View {
await startup() await startup()
} }
/// Called when the user scrolls into a new month fetches a ±1 month window /// Called when the user scrolls into a new month refreshes the visible range
/// around it on demand. `loadEvents` skips the network if cached. /// immediately from cache, then fetches on demand if needed.
private func ensureLoaded(around month: Date) async { private func ensureLoaded(around month: Date) async {
let cal = store.userCalendar let cal = store.userCalendar
let monthStart = cal.date(from: cal.dateComponents([.year, .month], from: month)) ?? month let monthStart = cal.date(from: cal.dateComponents([.year, .month], from: month)) ?? month
let s = cal.date(byAdding: .month, value: -1, to: monthStart) ?? monthStart let s = cal.date(byAdding: .month, value: -1, to: monthStart) ?? monthStart
let e = cal.date(byAdding: .month, value: 2, to: monthStart) ?? monthStart let e = cal.date(byAdding: .month, value: 2, to: monthStart) ?? monthStart
// Only narrow the visible event set if the requested window is already cached.
// Otherwise keep the current events visible until the network fetch finishes,
// so previous/current/next month events don't disappear temporarily.
if store.isCached(start: s, end: e) {
store.refreshFromCache(start: s, end: e)
}
await store.loadEvents(api: api, start: s, end: e) await store.loadEvents(api: api, start: s, end: e)
} }
} }
@@ -346,6 +377,7 @@ private struct CalendarSheets: ViewModifier {
@Binding var editorDate: Date @Binding var editorDate: Date
@Binding var editingEvent: CalEvent? @Binding var editingEvent: CalEvent?
@Binding var selectedEvent: CalEvent? @Binding var selectedEvent: CalEvent?
@Binding var showFilter: Bool
let api: CalendarrAPI let api: CalendarrAPI
let reload: () async -> Void let reload: () async -> Void
@@ -364,5 +396,8 @@ private struct CalendarSheets: ViewModifier {
await reload() await reload()
} }
} }
.sheet(isPresented: $showFilter) {
CalendarFilterSheet(api: api, store: store)
}
} }
} }

View File

@@ -1,7 +1,9 @@
import SwiftUI import SwiftUI
private let weeksBack = 104 // Cover ~10 years back and ~77 years ahead, so the scrollable range goes well
private let weeksAhead = 104 // past 2100 enough room for any vacation that's actually getting planned.
private let weeksBack = 520
private let weeksAhead = 4000
private let weekdayHeaderHeight: CGFloat = 28 private let weekdayHeaderHeight: CGFloat = 28
private let dayNumberRowHeight: CGFloat = 22 private let dayNumberRowHeight: CGFloat = 22
private let laneHeight: CGFloat = 16 private let laneHeight: CGFloat = 16
@@ -106,13 +108,24 @@ struct MonthView: View {
cal.date(from: cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))! cal.date(from: cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))!
} }
/// Treat the visible month as the one that "owns" Thursday of the current week /// Determine the visible month from the currently-scrolled week.
/// matches ISO week-month conventions and avoids flicker on month boundaries. /// Instead of switching as soon as a few days of the next month appear,
/// we count the month affiliation of the visible week rows and keep the
/// month that occupies the majority of the viewport.
private func publishVisibleMonth(from week: Date?) { private func publishVisibleMonth(from week: Date?) {
guard let w = week else { return } guard let w = week else { return }
let thursday = cal.date(byAdding: .day, value: 3, to: w) ?? w
let m = cal.date(from: cal.dateComponents([.year, .month], from: thursday)) ?? thursday let visibleWeeks = (0..<6).compactMap { cal.date(byAdding: .weekOfYear, value: $0, to: w) }
if visibleMonth != m { visibleMonth = m } let monthCounts = visibleWeeks.reduce(into: [Date: Int]()) { acc, weekStart in
guard let midWeek = cal.date(byAdding: .day, value: 3, to: weekStart) else { return }
let month = cal.date(from: cal.dateComponents([.year, .month], from: midWeek)) ?? midWeek
acc[month, default: 0] += 1
}
let selectedMonth = monthCounts.max { a, b in a.value < b.value }?.key
if let m = selectedMonth, visibleMonth != m {
visibleMonth = m
}
} }
} }