Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
818354e
docs(710): task addendum for the 2026-07-26 amendment (FR-700-23, FR-…
kipp-ing Jul 26, 2026
58aa189
feat(700/710): availability decoupled from in-app UI; frame_status se…
kipp-ing Jul 26, 2026
39efcae
docs: iOS 27 device session findings (FramePhone 27.0)
kipp-ing Jul 27, 2026
25faa70
docs(1000): T005 entitlement gap, KVS facts, iOS 27 hypothesis demoted
kipp-ing Jul 28, 2026
9db2786
docs(testing): confound closed on real 26.5 hardware — it is iOS 27
kipp-ing Jul 28, 2026
47b6630
test(uitests): convergence-based scrolling; fix a reset that ate its …
kipp-ing Jul 28, 2026
986a21c
test(uitests): keep scrollToElement's contract, scroll the container,…
kipp-ing Jul 28, 2026
0f7f100
test(uitests): all six iOS 27 tests green on FramePhone; album drill-…
kipp-ing Jul 28, 2026
39d21ec
fix(1100): product ids ASC will actually accept (ing.kipp.ownframe.*)
kipp-ing Jul 29, 2026
9fbcf56
fix(300): copy no longer names the device it runs on (#53)
kipp-ing Jul 29, 2026
d90b298
fix(1100): the unlock title no longer truncates on narrow phones (#52)
kipp-ing Jul 29, 2026
e872b90
fix(1100): StoreKit suite skips precisely on the iOS 26.x session def…
kipp-ing Jul 29, 2026
7167ccc
test(screenshots): let the sweep run in English for the ASC review shots
kipp-ing Jul 29, 2026
9a094a5
docs(1100): ASC store setup done; two release blockers the file had w…
kipp-ing Jul 29, 2026
fb37ca4
feat(icon): new screen artwork; fix the reference that broke every build
kipp-ing Jul 29, 2026
888f14f
chore(design): track the icon's source artwork
kipp-ing Jul 29, 2026
8c381a8
chore(release): 1.1 (9)
kipp-ing Jul 29, 2026
9fc6654
chore(design): update the icon source artwork; add the 1.1 release ha…
kipp-ing Jul 29, 2026
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
Binary file added Design/AppIcon/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 20 additions & 20 deletions OwnFrame.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = "OwnFrame/OwnFrame.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -691,7 +691,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand All @@ -717,7 +717,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = "OwnFrame/OwnFrame.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -744,7 +744,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand All @@ -767,12 +767,12 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-SlideshowTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -793,12 +793,12 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-SlideshowTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -818,12 +818,12 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-SlideshowUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -843,12 +843,12 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MACOSX_DEPLOYMENT_TARGET = 26.5;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-SlideshowUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -869,7 +869,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "OwnFrameShareExtension/OwnFrameShareExtension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "OwnFrameShareExtension/Info.plist";
Expand All @@ -880,7 +880,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand All @@ -901,7 +901,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "OwnFrameShareExtension/OwnFrameShareExtension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "OwnFrameShareExtension/Info.plist";
Expand All @@ -912,7 +912,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand All @@ -934,13 +934,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "OwnFrame";
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
Expand All @@ -962,13 +962,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 8Z4SNYUKQ2;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "OwnFrame";
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = "ing.kipp.Immich-Slideshow";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
Expand Down
Binary file modified OwnFrame/Assets.xcassets/ImmichLogo.imageset/frame.afdesign
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions OwnFrame/ImmichSlideshowIcon.icon/icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"layers" : [
{
"glass" : false,
"hidden" : true,
"image-name" : "Image 12.png",
"name" : "Image 12",
"position" : {
Expand All @@ -116,6 +117,30 @@
]
}
},
{
"glass" : false,
"image-name" : "Image 13.png",
"name" : "Image 13",
"position" : {
"scale" : 0.78,
"translation-in-points" : [
11.470000000000027,
-4.903823999999986
]
}
},
{
"hidden" : true,
"image-name" : "image.png",
"name" : "image",
"position" : {
"scale" : 0.59,
"translation-in-points" : [
0,
0
]
}
},
{
"glass" : false,
"hidden" : true,
Expand Down
8 changes: 4 additions & 4 deletions OwnFrame/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -801,13 +801,13 @@
}
}
},
"Easiest — play photos from an album on this iPad or in iCloud. No server needed." : {
"Easiest — play photos from an album on this device or in iCloud. No server needed." : {
"extractionState" : "manual",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Am einfachsten — Fotos aus einem Album auf diesem iPad oder in iCloud abspielen. Kein Server nötig."
"value" : "Am einfachsten — Fotos aus einem Album auf diesem Gerät oder in iCloud abspielen. Kein Server nötig."
}
}
}
Expand Down Expand Up @@ -1586,12 +1586,12 @@
}
}
},
"Photos you have viewed are kept on this iPad so the slideshow keeps playing when the network is down." : {
"Photos you have viewed are kept on this device so the slideshow keeps playing when the network is down." : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bereits angezeigte Fotos werden auf diesem iPad behalten, damit die Diashow weiterläuft, wenn das Netzwerk ausfällt."
"value" : "Bereits angezeigte Fotos werden auf diesem Gerät behalten, damit die Diashow weiterläuft, wenn das Netzwerk ausfällt."
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion OwnFrame/Onboarding/OnboardingChoiceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct OnboardingChoiceView: View {
Section {
ChoiceRow(
title: "Use an iCloud album",
description: "Easiest — play photos from an album on this iPad or in iCloud. No server needed.",
description: "Easiest — play photos from an album on this device or in iCloud. No server needed.",
systemImage: "photo.on.rectangle.angled",
identifier: "onboarding.choice.photoLibrary"
) { viewModel.choosePath(.photoLibrary) }
Expand Down
5 changes: 5 additions & 0 deletions OwnFrame/OwnFrameApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,11 @@ private struct RootView: View {
if let slideshow, let powerManager, let remoteAdapter {
SlideshowView(viewModel: slideshow, powerManager: powerManager, api: api,
isPhotoLibrarySource: activeSourceIsPhotoLibrary,
// FR-700-23 "any other sheet/full-screen surface": these two
// sheets are presented by THIS view over the running
// slideshow, so they must count as a modal cover exactly
// like SlideshowView's own sheets.
externallyCovered: incomingSheet != nil || showAlbumReselect,
themeStore: themeStore,
makeCoordinator: { await factories.makeCoordinator(remoteAdapter) },
onReset: {
Expand Down
45 changes: 35 additions & 10 deletions OwnFrame/Slideshow/BrokerSetupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,31 @@ enum BrokerSettingsStoreFactory {
/// `--uitest-reset-publish-options` clears for a deterministic start; production
/// uses the standard defaults (no secrets — just booleans and a byte cap).
enum HAPublishOptionsStoreFactory {
#if DEBUG
static let uitestSuite = "uitest.haPublish"

/// `--uitest-reset-publish-options` means "start this LAUNCH from a clean slate", so the
/// wipe must happen once per process — a `static let` runs exactly once, lazily.
///
/// It used to sit inside `make()`, which is wrong because `make()` is called repeatedly
/// within a single launch: the app entry builds one store, the HA coordinator builds another
/// on every broker start, and `SlideshowSettingsView.init` builds one *every time SwiftUI
/// re-initialises that view*. So a reset-launch wiped the preference moments after the user
/// toggled it, and the next launch read the default back. That is what made
/// `testImagePublishTogglePersistsAcrossRelaunch` flaky — it only passed when no re-render
/// happened to land between the toggle and the relaunch (diagnosed 2026-07-28, issue #50).
private static let resetOncePerLaunch: Void = {
guard ProcessInfo.processInfo.arguments.contains("--uitest-reset-publish-options") else { return }
UserDefaults(suiteName: uitestSuite)?
.removeObject(forKey: UserDefaultsHAPublishOptionsStore.defaultsKey)
}()
#endif

static func make() -> any HAPublishOptionsStore {
#if DEBUG
if ProcessInfo.processInfo.arguments.contains("--uitest") {
let suite = "uitest.haPublish"
let defaults = UserDefaults(suiteName: suite) ?? .standard
if ProcessInfo.processInfo.arguments.contains("--uitest-reset-publish-options") {
defaults.removePersistentDomain(forName: suite)
}
_ = resetOncePerLaunch
let defaults = UserDefaults(suiteName: uitestSuite) ?? .standard
return UserDefaultsHAPublishOptionsStore(defaults: defaults)
}
#endif
Expand All @@ -174,14 +191,22 @@ enum HAPublishOptionsStoreFactory {
/// factory: a hermetic suite under `--uitest` so a rename in one test cannot leak into the next,
/// the standard defaults in production. Not a secret and not an identity — purely cosmetic.
enum FrameNameStoreFactory {
#if DEBUG
static let uitestSuite = "uitest.frameName"

/// Once per launch, not once per store — see the note in `HAPublishOptionsStoreFactory`.
private static let resetOncePerLaunch: Void = {
guard ProcessInfo.processInfo.arguments.contains("--uitest-reset-publish-options") else { return }
UserDefaults(suiteName: uitestSuite)?
.removeObject(forKey: UserDefaultsFrameNameStore.defaultsKey)
}()
#endif

static func make() -> any FrameNameStore {
#if DEBUG
if ProcessInfo.processInfo.arguments.contains("--uitest") {
let suite = "uitest.frameName"
let defaults = UserDefaults(suiteName: suite) ?? .standard
if ProcessInfo.processInfo.arguments.contains("--uitest-reset-publish-options") {
defaults.removePersistentDomain(forName: suite)
}
_ = resetOncePerLaunch
let defaults = UserDefaults(suiteName: uitestSuite) ?? .standard
return UserDefaultsFrameNameStore(defaults: defaults, defaultName: "OwnFrame")
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion OwnFrame/Slideshow/SlideshowSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ struct SlideshowSettingsView: View {
} header: {
Text("Storage")
} footer: {
Text("Photos you have viewed are kept on this iPad so the slideshow keeps playing when the network is down.")
Text("Photos you have viewed are kept on this device so the slideshow keeps playing when the network is down.")
}
.task { cacheUsage = await diskCache.currentUsage() }
}
Expand Down
Loading
Loading