Apple Watch support, new widget, and other improvements
This commit is contained in:
parent
3a8176f931
commit
81793f9842
@ -48,6 +48,23 @@
|
||||
B999DE602B76FB3E00509868 /* ContactRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B999DE5F2B76FB3E00509868 /* ContactRow.swift */; };
|
||||
B9B469B02B9A275F00AD5585 /* FollowGoalWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469AF2B9A275F00AD5585 /* FollowGoalWidget.swift */; };
|
||||
B9B469B22B9A6E8300AD5585 /* PrivacyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469B12B9A6E8300AD5585 /* PrivacyView.swift */; };
|
||||
B9B469BA2B9A7E6800AD5585 /* ThreadedWatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469B92B9A7E6800AD5585 /* ThreadedWatchApp.swift */; };
|
||||
B9B469BC2B9A7E6800AD5585 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469BB2B9A7E6800AD5585 /* ContentView.swift */; };
|
||||
B9B469BE2B9A7E6B00AD5585 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B9B469BD2B9A7E6B00AD5585 /* Assets.xcassets */; };
|
||||
B9B469C12B9A7E6B00AD5585 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B9B469C02B9A7E6B00AD5585 /* Preview Assets.xcassets */; };
|
||||
B9B469C42B9A7E6B00AD5585 /* Threaded.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = B9B469B72B9A7E6800AD5585 /* Threaded.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
B9B469C92B9A804800AD5585 /* Redeclarations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C20D602B949AD7004DC9B3 /* Redeclarations.swift */; };
|
||||
B9B469CA2B9A811200AD5585 /* AppInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FB94A12B2EF24A00D81C07 /* AppInfo.swift */; };
|
||||
B9B469CB2B9A816D00AD5585 /* LoggedAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98627302B86F23500844245 /* LoggedAccounts.swift */; };
|
||||
B9B469CD2B9A823600AD5585 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = B9B469CC2B9A823600AD5585 /* Localizable.xcstrings */; };
|
||||
B9B469CF2B9A82ED00AD5585 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = B9B469CE2B9A82ED00AD5585 /* KeychainSwift */; };
|
||||
B9B469D12B9A82FD00AD5585 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9B469D02B9A82FD00AD5585 /* SwiftUI.framework */; };
|
||||
B9B469D42B9A871C00AD5585 /* WatchConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469D32B9A871C00AD5585 /* WatchConnectivity.swift */; };
|
||||
B9B469D52B9A871C00AD5585 /* WatchConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469D32B9A871C00AD5585 /* WatchConnectivity.swift */; };
|
||||
B9B469D72B9A8B0700AD5585 /* GivenAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469D62B9A8B0700AD5585 /* GivenAccount.swift */; };
|
||||
B9B469D82B9A8B1600AD5585 /* GivenAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469D62B9A8B0700AD5585 /* GivenAccount.swift */; };
|
||||
B9B469D92B9AA4DF00AD5585 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FB949A2B2EF09A00D81C07 /* Client.swift */; };
|
||||
B9B469DB2B9B2EDB00AD5585 /* ComingSoonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B469DA2B9B2EDB00AD5585 /* ComingSoonView.swift */; };
|
||||
B9B63B212B442D1500BBC82D /* DynamicTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B63B202B442D1500BBC82D /* DynamicTextEditor.swift */; };
|
||||
B9B63B232B447B8000BBC82D /* PostCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B63B222B447B8000BBC82D /* PostCardView.swift */; };
|
||||
B9B63B252B44997400BBC82D /* QuotePostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B63B242B44997400BBC82D /* QuotePostView.swift */; };
|
||||
@ -119,6 +136,13 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
B9B469C22B9A7E6B00AD5585 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = B9FB944F2B2DEECE00D81C07 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = B9B469B62B9A7E6800AD5585;
|
||||
remoteInfo = "ThreadedWatch Watch App";
|
||||
};
|
||||
B9C20D182B921C7B004DC9B3 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = B9FB944F2B2DEECE00D81C07 /* Project object */;
|
||||
@ -136,6 +160,17 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
B9B469C82B9A7E6B00AD5585 /* Embed Watch Content */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
B9B469C42B9A7E6B00AD5585 /* Threaded.app in Embed Watch Content */,
|
||||
);
|
||||
name = "Embed Watch Content";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B9FB94B82B2F009F00D81C07 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -185,6 +220,17 @@
|
||||
B999DE5F2B76FB3E00509868 /* ContactRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRow.swift; sourceTree = "<group>"; };
|
||||
B9B469AF2B9A275F00AD5585 /* FollowGoalWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowGoalWidget.swift; sourceTree = "<group>"; };
|
||||
B9B469B12B9A6E8300AD5585 /* PrivacyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyView.swift; sourceTree = "<group>"; };
|
||||
B9B469B72B9A7E6800AD5585 /* Threaded.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Threaded.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B9B469B92B9A7E6800AD5585 /* ThreadedWatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedWatchApp.swift; sourceTree = "<group>"; };
|
||||
B9B469BB2B9A7E6800AD5585 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
B9B469BD2B9A7E6B00AD5585 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
B9B469C02B9A7E6B00AD5585 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
B9B469CC2B9A823600AD5585 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
B9B469D02B9A82FD00AD5585 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS10.4.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B9B469D22B9A838500AD5585 /* ThreadedWatch Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "ThreadedWatch Watch App.entitlements"; sourceTree = "<group>"; };
|
||||
B9B469D32B9A871C00AD5585 /* WatchConnectivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConnectivity.swift; sourceTree = "<group>"; };
|
||||
B9B469D62B9A8B0700AD5585 /* GivenAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GivenAccount.swift; sourceTree = "<group>"; };
|
||||
B9B469DA2B9B2EDB00AD5585 /* ComingSoonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComingSoonView.swift; sourceTree = "<group>"; };
|
||||
B9B63B202B442D1500BBC82D /* DynamicTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicTextEditor.swift; sourceTree = "<group>"; };
|
||||
B9B63B222B447B8000BBC82D /* PostCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCardView.swift; sourceTree = "<group>"; };
|
||||
B9B63B242B44997400BBC82D /* QuotePostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotePostView.swift; sourceTree = "<group>"; };
|
||||
@ -256,6 +302,15 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
B9B469B42B9A7E6800AD5585 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B9B469D12B9A82FD00AD5585 /* SwiftUI.framework in Frameworks */,
|
||||
B9B469CF2B9A82ED00AD5585 /* KeychainSwift in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B9C20D062B921C78004DC9B3 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -310,6 +365,28 @@
|
||||
path = Content;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B9B469B82B9A7E6800AD5585 /* ThreadedWatch */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B9B469B92B9A7E6800AD5585 /* ThreadedWatchApp.swift */,
|
||||
B9B469BB2B9A7E6800AD5585 /* ContentView.swift */,
|
||||
B9B469D32B9A871C00AD5585 /* WatchConnectivity.swift */,
|
||||
B9B469D62B9A8B0700AD5585 /* GivenAccount.swift */,
|
||||
B9B469BD2B9A7E6B00AD5585 /* Assets.xcassets */,
|
||||
B9B469BF2B9A7E6B00AD5585 /* Preview Content */,
|
||||
B9B469CC2B9A823600AD5585 /* Localizable.xcstrings */,
|
||||
);
|
||||
path = ThreadedWatch;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B9B469BF2B9A7E6B00AD5585 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B9B469C02B9A7E6B00AD5585 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B9BED5142B5D5CCD00C9B715 /* Post */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -361,8 +438,10 @@
|
||||
B9FB944E2B2DEECE00D81C07 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B9B469D22B9A838500AD5585 /* ThreadedWatch Watch App.entitlements */,
|
||||
B9CC45B92B40AA1E001E4FA5 /* README.md */,
|
||||
B9FB94592B2DEECE00D81C07 /* Threaded */,
|
||||
B9B469B82B9A7E6800AD5585 /* ThreadedWatch */,
|
||||
B9C20D0E2B921C78004DC9B3 /* ThreadedWidgets */,
|
||||
B9FB94AB2B2F009F00D81C07 /* AuthService */,
|
||||
B9FB94A82B2F009F00D81C07 /* Frameworks */,
|
||||
@ -378,6 +457,7 @@
|
||||
B9FB94572B2DEECE00D81C07 /* Threaded.app */,
|
||||
B9FB94A72B2F009F00D81C07 /* ThreadedAuthService.appex */,
|
||||
B9C20D092B921C78004DC9B3 /* ThreadedWidgetsExtension.appex */,
|
||||
B9B469B72B9A7E6800AD5585 /* Threaded.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -471,6 +551,7 @@
|
||||
B9FA6E762B82788A00D63E30 /* AccountRow.swift */,
|
||||
B97491E22B6E96700098BC48 /* SymbolWidth.swift */,
|
||||
B9DC69282B78D9A500E625B9 /* SearchResultView.swift */,
|
||||
B9B469DA2B9B2EDB00AD5585 /* ComingSoonView.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
@ -492,6 +573,7 @@
|
||||
B9FB94A82B2F009F00D81C07 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B9B469D02B9A82FD00AD5585 /* SwiftUI.framework */,
|
||||
B9DC692C2B79362700E625B9 /* StoreKit.framework */,
|
||||
B9FB94A92B2F009F00D81C07 /* AuthenticationServices.framework */,
|
||||
B9C20D0A2B921C78004DC9B3 /* WidgetKit.framework */,
|
||||
@ -525,6 +607,26 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
B9B469B62B9A7E6800AD5585 /* ThreadedWatch Watch App */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B9B469C52B9A7E6B00AD5585 /* Build configuration list for PBXNativeTarget "ThreadedWatch Watch App" */;
|
||||
buildPhases = (
|
||||
B9B469B32B9A7E6800AD5585 /* Sources */,
|
||||
B9B469B42B9A7E6800AD5585 /* Frameworks */,
|
||||
B9B469B52B9A7E6800AD5585 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "ThreadedWatch Watch App";
|
||||
packageProductDependencies = (
|
||||
B9B469CE2B9A82ED00AD5585 /* KeychainSwift */,
|
||||
);
|
||||
productName = "ThreadedWatch Watch App";
|
||||
productReference = B9B469B72B9A7E6800AD5585 /* Threaded.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
B9C20D082B921C78004DC9B3 /* ThreadedWidgetsExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B9C20D1D2B921C7B004DC9B3 /* Build configuration list for PBXNativeTarget "ThreadedWidgetsExtension" */;
|
||||
@ -553,12 +655,14 @@
|
||||
B9FB94542B2DEECE00D81C07 /* Frameworks */,
|
||||
B9FB94552B2DEECE00D81C07 /* Resources */,
|
||||
B9FB94B82B2F009F00D81C07 /* Embed Foundation Extensions */,
|
||||
B9B469C82B9A7E6B00AD5585 /* Embed Watch Content */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
B9FB94B32B2F009F00D81C07 /* PBXTargetDependency */,
|
||||
B9C20D192B921C7B004DC9B3 /* PBXTargetDependency */,
|
||||
B9B469C32B9A7E6B00AD5585 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Threaded;
|
||||
packageProductDependencies = (
|
||||
@ -600,9 +704,12 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1520;
|
||||
LastSwiftUpdateCheck = 1530;
|
||||
LastUpgradeCheck = 1510;
|
||||
TargetAttributes = {
|
||||
B9B469B62B9A7E6800AD5585 = {
|
||||
CreatedOnToolsVersion = 15.3;
|
||||
};
|
||||
B9C20D082B921C78004DC9B3 = {
|
||||
CreatedOnToolsVersion = 15.2;
|
||||
};
|
||||
@ -636,13 +743,24 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
B9FB94562B2DEECE00D81C07 /* Threaded */,
|
||||
B9FB94A62B2F009F00D81C07 /* ThreadedAuthService */,
|
||||
B9B469B62B9A7E6800AD5585 /* ThreadedWatch Watch App */,
|
||||
B9C20D082B921C78004DC9B3 /* ThreadedWidgetsExtension */,
|
||||
B9FB94A62B2F009F00D81C07 /* ThreadedAuthService */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
B9B469B52B9A7E6800AD5585 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B9B469C12B9A7E6B00AD5585 /* Preview Assets.xcassets in Resources */,
|
||||
B9B469BE2B9A7E6B00AD5585 /* Assets.xcassets in Resources */,
|
||||
B9B469CD2B9A823600AD5585 /* Localizable.xcstrings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B9C20D072B921C78004DC9B3 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -679,6 +797,21 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
B9B469B32B9A7E6800AD5585 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B9B469D72B9A8B0700AD5585 /* GivenAccount.swift in Sources */,
|
||||
B9B469D52B9A871C00AD5585 /* WatchConnectivity.swift in Sources */,
|
||||
B9B469C92B9A804800AD5585 /* Redeclarations.swift in Sources */,
|
||||
B9B469CB2B9A816D00AD5585 /* LoggedAccounts.swift in Sources */,
|
||||
B9B469CA2B9A811200AD5585 /* AppInfo.swift in Sources */,
|
||||
B9B469BC2B9A7E6800AD5585 /* ContentView.swift in Sources */,
|
||||
B9B469D92B9AA4DF00AD5585 /* Client.swift in Sources */,
|
||||
B9B469BA2B9A7E6800AD5585 /* ThreadedWatchApp.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B9C20D052B921C78004DC9B3 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -722,6 +855,7 @@
|
||||
B9FB94972B2EDABF00D81C07 /* SupportView.swift in Sources */,
|
||||
B9F8FA162B5D3AC30044DAB4 /* SafariView.swift in Sources */,
|
||||
B97798892B853E6600DC869F /* UpdateView.swift in Sources */,
|
||||
B9B469D82B9A8B1600AD5585 /* GivenAccount.swift in Sources */,
|
||||
B9842C142B2F310C00D9F3C1 /* FetchTimeline.swift in Sources */,
|
||||
B9842C162B2F363600D9F3C1 /* TimelineFilter.swift in Sources */,
|
||||
B9B63B232B447B8000BBC82D /* PostCardView.swift in Sources */,
|
||||
@ -747,6 +881,7 @@
|
||||
B9FB94812B2E1FEF00D81C07 /* HTMLString.swift in Sources */,
|
||||
B9FB947F2B2E1D5F00D81C07 /* Account.swift in Sources */,
|
||||
B9842C122B2F2A5800D9F3C1 /* TimelineView.swift in Sources */,
|
||||
B9B469D42B9A871C00AD5585 /* WatchConnectivity.swift in Sources */,
|
||||
B9FB948C2B2E232300D81C07 /* OnlineImage.swift in Sources */,
|
||||
B9BED5182B5D649C00C9B715 /* PostMenu.swift in Sources */,
|
||||
B9FB94742B2DF6A100D81C07 /* ButtonStyles.swift in Sources */,
|
||||
@ -761,6 +896,7 @@
|
||||
B9B63B272B449CDC00BBC82D /* SearchResults.swift in Sources */,
|
||||
B9B63B252B44997400BBC82D /* QuotePostView.swift in Sources */,
|
||||
B9BCC3182B90B3BC00211976 /* Tenor.swift in Sources */,
|
||||
B9B469DB2B9B2EDB00AD5585 /* ComingSoonView.swift in Sources */,
|
||||
B97BCE242B3DD8400044756D /* HapticManager.swift in Sources */,
|
||||
B9B63B212B442D1500BBC82D /* DynamicTextEditor.swift in Sources */,
|
||||
B9FB949F2B2EF0F200D81C07 /* MastodonRequest.swift in Sources */,
|
||||
@ -784,6 +920,11 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
B9B469C32B9A7E6B00AD5585 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B9B469B62B9A7E6800AD5585 /* ThreadedWatch Watch App */;
|
||||
targetProxy = B9B469C22B9A7E6B00AD5585 /* PBXContainerItemProxy */;
|
||||
};
|
||||
B9C20D192B921C7B004DC9B3 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B9C20D082B921C78004DC9B3 /* ThreadedWidgetsExtension */;
|
||||
@ -808,6 +949,66 @@
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
B9B469C62B9A7E6B00AD5585 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "ThreadedWatch Watch App.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"ThreadedWatch/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = HB5P3BML86;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = fr.lumaa.Threaded;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = fr.lumaa.Threaded.watchkitapp;
|
||||
PRODUCT_NAME = Threaded;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B9B469C72B9A7E6B00AD5585 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "ThreadedWatch Watch App.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"ThreadedWatch/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = HB5P3BML86;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = fr.lumaa.Threaded;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = fr.lumaa.Threaded.watchkitapp;
|
||||
PRODUCT_NAME = Threaded;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B9C20D1B2B921C7B004DC9B3 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -831,13 +1032,14 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = fr.lumaa.Threaded.ThreadedWidgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,4";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -864,13 +1066,14 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = fr.lumaa.Threaded.ThreadedWidgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,4";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -1001,7 +1204,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Threaded/Threaded.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 500;
|
||||
CURRENT_PROJECT_VERSION = 1482;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Threaded/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = HB5P3BML86;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@ -1041,7 +1244,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Threaded/Threaded.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 500;
|
||||
CURRENT_PROJECT_VERSION = 1482;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Threaded/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = HB5P3BML86;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@ -1136,6 +1339,15 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
B9B469C52B9A7E6B00AD5585 /* Build configuration list for PBXNativeTarget "ThreadedWatch Watch App" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B9B469C62B9A7E6B00AD5585 /* Debug */,
|
||||
B9B469C72B9A7E6B00AD5585 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B9C20D1D2B921C7B004DC9B3 /* Build configuration list for PBXNativeTarget "ThreadedWidgetsExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@ -1253,6 +1465,11 @@
|
||||
package = B95ED2312B8707D60055F5BD /* XCRemoteSwiftPackageReference "purchases-ios" */;
|
||||
productName = RevenueCatUI;
|
||||
};
|
||||
B9B469CE2B9A82ED00AD5585 /* KeychainSwift */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = B9BF54052B6B6823004B24E7 /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
||||
productName = KeychainSwift;
|
||||
};
|
||||
B9BF54062B6B6823004B24E7 /* KeychainSwift */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = B9BF54052B6B6823004B24E7 /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
||||
|
@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1530"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9B469B62B9A7E6800AD5585"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "ThreadedWatch Watch App"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9FB94562B2DEECE00D81C07"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "Threaded"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "NO">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9B469B62B9A7E6800AD5585"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "ThreadedWatch Watch App"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9B469B62B9A7E6800AD5585"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "ThreadedWatch Watch App"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1530"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9C20D082B921C78004DC9B3"
|
||||
BuildableName = "ThreadedWidgetsExtension.appex"
|
||||
BlueprintName = "ThreadedWidgetsExtension"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9FB94562B2DEECE00D81C07"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "Threaded"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.springboard">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9C20D082B921C78004DC9B3"
|
||||
BuildableName = "ThreadedWidgetsExtension.appex"
|
||||
BlueprintName = "ThreadedWidgetsExtension"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9FB94562B2DEECE00D81C07"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "Threaded"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B9FB94562B2DEECE00D81C07"
|
||||
BuildableName = "Threaded.app"
|
||||
BlueprintName = "Threaded"
|
||||
ReferencedContainer = "container:Threaded.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
28
Threaded/Components/ComingSoonView.swift
Normal file
28
Threaded/Components/ComingSoonView.swift
Normal file
@ -0,0 +1,28 @@
|
||||
//Made by Lumaa
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ComingSoonView: View {
|
||||
@State private var spin: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 5) {
|
||||
Text(String("👀"))
|
||||
.font(.system(size: 62))
|
||||
.rotation3DEffect(.degrees(spin), axis: (x: 0, y: 1, z: 0))
|
||||
.onTapGesture {
|
||||
withAnimation(.spring.speed(0.8)) {
|
||||
spin = 360
|
||||
}
|
||||
spin = 0
|
||||
}
|
||||
|
||||
Text("coming-soon")
|
||||
.font(.title.bold())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ComingSoonView()
|
||||
}
|
@ -694,6 +694,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"coming-soon" : {
|
||||
|
||||
},
|
||||
"discovery" : {
|
||||
"localizations" : {
|
||||
@ -1559,6 +1562,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.remove" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Remove"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Supprimer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.send-to-watch" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Send to Apple Watch"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Envoyer vers l'Apple Watch"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.cancel" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -2980,4 +3015,4 @@
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ struct PrivacyView: View {
|
||||
}
|
||||
} label: {
|
||||
Text(clearedCache ? "settings.privacy.cleared" : "settings.privacy.clear")
|
||||
.foregroundStyle(clearedCache ? Color(uiColor: UIColor.label) : Color(uiColor: UIColor.systemBackground))
|
||||
}
|
||||
.buttonStyle(LargeButton(filled: true, filledColor: clearedCache ? Color.green : Color(uiColor: UIColor.label), height: 7.5))
|
||||
.disabled(clearedCache)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
import WatchConnectivity
|
||||
|
||||
//TODO: Bring back "Privacy" with mutelist, blocklist and default visibility
|
||||
|
||||
@ -20,7 +21,7 @@ struct SettingsView: View {
|
||||
Section {
|
||||
ForEach(loggedAccounts) { logged in
|
||||
if let app = logged.app {
|
||||
SwitcherRow(app: app)
|
||||
SwitcherRow(app: app, loggedAccount: logged)
|
||||
.listRowThreaded()
|
||||
}
|
||||
}
|
||||
@ -122,12 +123,16 @@ struct SettingsView: View {
|
||||
|
||||
extension SettingsView {
|
||||
struct SwitcherRow: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@Environment(AccountManager.self) private var accountManager: AccountManager
|
||||
@Environment(UniversalNavigator.self) private var uniNav: UniversalNavigator
|
||||
@EnvironmentObject private var navigator: Navigator
|
||||
|
||||
var logged: LoggedAccount
|
||||
var app: AppAccount
|
||||
|
||||
private let connectivity: SessionDelegator = .init()
|
||||
|
||||
@State private var account: Account? = nil
|
||||
@State private var error: Bool = false
|
||||
|
||||
@ -139,8 +144,9 @@ extension SettingsView {
|
||||
return currentAcct == app.accountName ?? ""
|
||||
}
|
||||
|
||||
init(app: AppAccount) {
|
||||
init(app: AppAccount, loggedAccount: LoggedAccount) {
|
||||
self.app = app
|
||||
self.logged = loggedAccount
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@ -174,8 +180,9 @@ extension SettingsView {
|
||||
} else {
|
||||
AccountManager.shared.setAccount(fetched!)
|
||||
AccountManager.shared.setClient(c)
|
||||
uniNav.selectedTab = .timeline
|
||||
|
||||
navigator.path = []
|
||||
uniNav.selectedTab = .timeline
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
@ -191,6 +198,33 @@ extension SettingsView {
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
.contextMenu {
|
||||
Button(role: .destructive) {
|
||||
modelContext.delete(self.logged)
|
||||
} label: {
|
||||
Label("settings.account-switcher.remove", systemImage: "trash")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
if connectivity.isWorking {
|
||||
Button {
|
||||
// double check in case states change in between
|
||||
if connectivity.isWorking {
|
||||
let message = GivenAccount(acct: app.accountName!, bearerToken: app.oauthToken?.accessToken ?? "")
|
||||
connectivity.session.sendMessageData(message.turnToMessage(), replyHandler: { data in
|
||||
let str = String(data: data, encoding: .utf8)
|
||||
print(str ?? "No data?")
|
||||
HapticManager.playHaptics(haptics: Haptic.success)
|
||||
})
|
||||
} else {
|
||||
print("No Watch?")
|
||||
}
|
||||
} label: {
|
||||
Label("settings.account-switcher.send-to-watch", systemImage: "applewatch.and.arrow.forward")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Circle()
|
||||
.fill(error ? Color.red.opacity(0.45) : Color.gray.opacity(0.45))
|
||||
@ -221,6 +255,8 @@ extension SettingsView {
|
||||
}
|
||||
.task {
|
||||
account = await findAccount(acct: app.accountName!)
|
||||
|
||||
connectivity.initialize()
|
||||
}
|
||||
}
|
||||
|
||||
|
10
ThreadedWatch Watch App.entitlements
Normal file
10
ThreadedWatch Watch App.entitlements
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.lumaa.ThreadedApp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ThreadedApp.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "watchos",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
ThreadedWatch/Assets.xcassets/AppIcon.appiconset/ThreadedApp.png
Normal file
BIN
ThreadedWatch/Assets.xcassets/AppIcon.appiconset/ThreadedApp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
6
ThreadedWatch/Assets.xcassets/Contents.json
Normal file
6
ThreadedWatch/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
152
ThreadedWatch/ContentView.swift
Normal file
152
ThreadedWatch/ContentView.swift
Normal file
@ -0,0 +1,152 @@
|
||||
//Made by Lumaa
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var givenAccounts: [GivenAccount] = []
|
||||
private let connectivity: SessionDelegator = .init()
|
||||
|
||||
@State private var fetching: Bool = false
|
||||
@State private var currentAccount: (UIImage, Int)? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
TabView {
|
||||
if givenAccounts.isEmpty || givenAccounts.count < 1 {
|
||||
ContentUnavailableView("iphone.login", systemImage: "iphone", description: Text("iphone.login.description"))
|
||||
.containerBackground(Color.yellow.gradient, for: .tabView)
|
||||
.scrollDisabled(true)
|
||||
} else {
|
||||
ForEach(givenAccounts, id: \.bearerToken) { acc in
|
||||
ZStack {
|
||||
if currentAccount == nil {
|
||||
ProgressView()
|
||||
.task {
|
||||
self.currentAccount = await getData(givenAccount: acc)
|
||||
}
|
||||
} else {
|
||||
let username: String = String(acc.acct.split(separator: "@")[0])
|
||||
let server: String = String(acc.acct.split(separator: "@")[1])
|
||||
|
||||
VStack {
|
||||
Image(uiImage: currentAccount!.0)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 60, height: 60)
|
||||
.clipShape(Circle())
|
||||
.padding(.top, 7.5)
|
||||
.onDisappear() {
|
||||
guard !fetching else { print("Fetching..."); return }
|
||||
self.currentAccount = nil
|
||||
}
|
||||
|
||||
Text(String("@\(username)"))
|
||||
.font(.title2.bold())
|
||||
Text(server)
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.gray)
|
||||
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
VStack {
|
||||
Text(currentAccount!.1, format: .number.notation(.compactName))
|
||||
.font(.headline)
|
||||
|
||||
Text("account.followers")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.gray)
|
||||
}
|
||||
.safeAreaPadding()
|
||||
}
|
||||
}
|
||||
.padding(.top, 7.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabViewStyle(.verticalPage(transitionStyle: .blur))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .topBarLeading) {
|
||||
Button {
|
||||
refresh()
|
||||
} label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
connectivity.initialize()
|
||||
self.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
private func refresh() {
|
||||
self.givenAccounts = self.getAccounts()
|
||||
|
||||
if connectivity.isWorking {
|
||||
withAnimation {
|
||||
self.givenAccounts.append(contentsOf: connectivity.allMessage.filter({ !self.givenAccounts.contains($0) }))
|
||||
if let _givenAccounts = connectivity.lastMessage, self.givenAccounts.isEmpty {
|
||||
self.givenAccounts = [_givenAccounts] // fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.givenAccounts = self.givenAccounts.uniqued()
|
||||
self.saveCurrentModels()
|
||||
}
|
||||
|
||||
private func getAccounts() -> [GivenAccount] {
|
||||
guard let modelContainer: ModelContainer = try? ModelContainer(for: LoggedAccount.self, configurations: ModelConfiguration(isStoredInMemoryOnly: false)) else { return [] }
|
||||
let modelContext = ModelContext(modelContainer)
|
||||
let loggedAccounts: [LoggedAccount]? = try? modelContext.fetch(FetchDescriptor<LoggedAccount>())
|
||||
let given: [GivenAccount] = loggedAccounts?.map({ $0.toGiven() }) ?? []
|
||||
|
||||
return given
|
||||
}
|
||||
|
||||
private func getData(givenAccount: GivenAccount) async -> (UIImage, Int) {
|
||||
let server = givenAccount.acct.split(separator: "@")[1]
|
||||
let client: Client = Client(server: String(server), oauthToken: OauthToken(accessToken: givenAccount.bearerToken, tokenType: "Bearer", scope: "", createdAt: .nan))
|
||||
|
||||
var pfp: UIImage
|
||||
do {
|
||||
fetching = true
|
||||
let acc = try await client.getString(endpoint: Accounts.verifyCredentials, forceVersion: .v1)
|
||||
fetching = false
|
||||
|
||||
if let serialized: [String : Any] = try JSONSerialization.jsonObject(with: acc.data(using: String.Encoding.utf8) ?? Data()) as? [String : Any] {
|
||||
let avatar: String = serialized["avatar"] as! String
|
||||
let task = try await URLSession.shared.data(from: URL(string: avatar)!)
|
||||
pfp = UIImage(data: task.0) ?? UIImage()
|
||||
|
||||
let followers: Int = serialized["followers_count"] as! Int
|
||||
return (pfp, followers)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
return (UIImage(), 0)
|
||||
}
|
||||
|
||||
private func saveModel(model: LoggedAccount) {
|
||||
guard let modelContainer: ModelContainer = try? ModelContainer(for: LoggedAccount.self, configurations: ModelConfiguration(isStoredInMemoryOnly: false)) else { return }
|
||||
let modelContext = ModelContext(modelContainer)
|
||||
modelContext.insert(model)
|
||||
}
|
||||
|
||||
private func saveCurrentModels() {
|
||||
for given in self.givenAccounts {
|
||||
saveModel(model: given.toLogged())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
59
ThreadedWatch/GivenAccount.swift
Normal file
59
ThreadedWatch/GivenAccount.swift
Normal file
@ -0,0 +1,59 @@
|
||||
//Made by Lumaa
|
||||
|
||||
import Foundation
|
||||
|
||||
struct GivenAccount: Hashable {
|
||||
static let messageSeparator: String = "||"
|
||||
|
||||
let acct: String
|
||||
let bearerToken: String
|
||||
|
||||
init(acct: String, bearerToken: String) {
|
||||
self.acct = acct
|
||||
self.bearerToken = bearerToken
|
||||
}
|
||||
|
||||
private func messageString() -> String {
|
||||
return "\(self.acct)\(Self.messageSeparator)\(self.bearerToken)"
|
||||
}
|
||||
|
||||
func turnToMessage() -> Data {
|
||||
let str = self.messageString()
|
||||
return str.data(using: .utf8) ?? Data()
|
||||
}
|
||||
|
||||
func turnToDictionary() -> [String : Any] {
|
||||
let str = self.messageString()
|
||||
return ["givenAccount" : str]
|
||||
}
|
||||
|
||||
func toLogged() -> LoggedAccount {
|
||||
let oauth = OauthToken(accessToken: self.bearerToken, tokenType: "Bearer", scope: "", createdAt: .nan)
|
||||
return LoggedAccount(token: oauth, acct: self.acct)
|
||||
}
|
||||
|
||||
static func makeFromMessage(_ message: Data) -> GivenAccount {
|
||||
if let string = String(data: message, encoding: .utf8) {
|
||||
let decomposed = string.split(separator: Self.messageSeparator)
|
||||
let acct = decomposed[0]
|
||||
let bearerToken = decomposed[1]
|
||||
|
||||
return GivenAccount(acct: String(acct), bearerToken: String(bearerToken))
|
||||
}
|
||||
fatalError("Message couldn't be stringified")
|
||||
}
|
||||
|
||||
static func makeFromMessage(_ message: String) -> GivenAccount {
|
||||
let decomposed = message.split(separator: Self.messageSeparator)
|
||||
let acct = decomposed[0]
|
||||
let bearerToken = decomposed[1]
|
||||
|
||||
return GivenAccount(acct: String(acct), bearerToken: String(bearerToken))
|
||||
}
|
||||
}
|
||||
|
||||
extension LoggedAccount {
|
||||
func toGiven() -> GivenAccount {
|
||||
return GivenAccount(acct: self.acct, bearerToken: self.token.accessToken)
|
||||
}
|
||||
}
|
54
ThreadedWatch/Localizable.xcstrings
Normal file
54
ThreadedWatch/Localizable.xcstrings
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"account.followers" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "followers"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "followers"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iphone.login" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Login with your iPhone"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Connexion via l'iPhone"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iphone.login.description" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Use your iPhone to see your account on your Apple Watch"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Utilisez votre iPhone pour voir vos comptes sur votre Apple Watch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
13
ThreadedWatch/ThreadedWatchApp.swift
Normal file
13
ThreadedWatch/ThreadedWatchApp.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//Made by Lumaa
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct ThreadedWatch_Watch_AppApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.modelData()
|
||||
}
|
||||
}
|
||||
}
|
122
ThreadedWatch/WatchConnectivity.swift
Normal file
122
ThreadedWatch/WatchConnectivity.swift
Normal file
@ -0,0 +1,122 @@
|
||||
//Made by Lumaa
|
||||
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
|
||||
#if os(watchOS)
|
||||
import ClockKit
|
||||
#endif
|
||||
|
||||
// Implement WCSessionDelegate methods to receive Watch Connectivity data and notify clients.
|
||||
// Handle WCSession status changes.
|
||||
//
|
||||
class SessionDelegator: NSObject, WCSessionDelegate {
|
||||
public var session: WCSession = .default
|
||||
public var lastMessage: GivenAccount? = nil
|
||||
public var allMessage: [GivenAccount] = []
|
||||
public var isWorking: Bool {
|
||||
self.session.isReachable
|
||||
}
|
||||
|
||||
func initialize() {
|
||||
guard WCSession.isSupported() else { fatalError("Doesn't support WCSession") }
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
// Monitor WCSession activation state changes.
|
||||
//
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
||||
return
|
||||
}
|
||||
|
||||
// Monitor WCSession reachability state changes.
|
||||
//
|
||||
func sessionReachabilityDidChange(_ session: WCSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
// Did receive an app context.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
|
||||
if let givenAccount = applicationContext["givenAccount"] as? String {
|
||||
self.lastMessage = GivenAccount.makeFromMessage(givenAccount)
|
||||
guard !self.allMessage.contains(where: { $0.bearerToken == self.lastMessage!.bearerToken }) else { return }
|
||||
self.allMessage.append(self.lastMessage!)
|
||||
}
|
||||
}
|
||||
|
||||
// Did receive a message, and the peer doesn't need a response.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||
if let givenAccount = message["givenAccount"] as? String {
|
||||
self.lastMessage = GivenAccount.makeFromMessage(givenAccount)
|
||||
guard !self.allMessage.contains(where: { $0.bearerToken == self.lastMessage!.bearerToken }) else { return }
|
||||
self.allMessage.append(self.lastMessage!)
|
||||
}
|
||||
}
|
||||
|
||||
// Did receive a message, and the peer needs a response.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
|
||||
self.session(session, didReceiveMessage: message)
|
||||
let response = ["success" : true, "timestamp": Int(Date.now.timeIntervalSince1970)] as [String : Any]
|
||||
replyHandler(response)
|
||||
}
|
||||
|
||||
// Did receive a piece of message data, and the peer doesn't need a response.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
|
||||
self.lastMessage = GivenAccount.makeFromMessage(messageData)
|
||||
guard !self.allMessage.contains(where: { $0.bearerToken == self.lastMessage!.bearerToken }) else { return }
|
||||
self.allMessage.append(self.lastMessage!)
|
||||
}
|
||||
|
||||
// Did receive a piece of message data, and the peer needs a response.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void) {
|
||||
self.session(session, didReceiveMessageData: messageData)
|
||||
replyHandler(messageData) // Echo back the data.
|
||||
}
|
||||
|
||||
// Did receive a piece of userInfo.
|
||||
//
|
||||
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
|
||||
return
|
||||
}
|
||||
|
||||
// Did finish sending a piece of userInfo.
|
||||
//
|
||||
func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
|
||||
return
|
||||
}
|
||||
|
||||
// Did receive a file.
|
||||
//
|
||||
func session(_ session: WCSession, didReceive file: WCSessionFile) {
|
||||
return
|
||||
}
|
||||
|
||||
// Did finish a file transfer.
|
||||
//
|
||||
func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?) {
|
||||
return
|
||||
}
|
||||
|
||||
// WCSessionDelegate methods for iOS only.
|
||||
//
|
||||
#if os(iOS)
|
||||
func sessionDidBecomeInactive(_ session: WCSession) {
|
||||
print("\(#function): activationState = \(session.activationState.rawValue)")
|
||||
}
|
||||
|
||||
func sessionDidDeactivate(_ session: WCSession) {
|
||||
// Activate the new session after having switched to a new watch.
|
||||
session.activate()
|
||||
}
|
||||
|
||||
func sessionWatchStateDidChange(_ session: WCSession) {
|
||||
print("\(#function): activationState = \(session.activationState.rawValue)")
|
||||
}
|
||||
#endif
|
||||
}
|
@ -11,15 +11,16 @@ struct FollowCountWidgetView: View {
|
||||
var body: some View {
|
||||
if let account = entry.configuration.account {
|
||||
ZStack {
|
||||
#if os(iOS)
|
||||
if family == WidgetFamily.systemSmall {
|
||||
small
|
||||
} else if family == WidgetFamily.systemMedium {
|
||||
medium
|
||||
} else if family == WidgetFamily.accessoryRectangular {
|
||||
}
|
||||
#endif
|
||||
|
||||
if family == WidgetFamily.accessoryRectangular {
|
||||
rectangular
|
||||
} else {
|
||||
Text(String("Unsupported WidgetFamily"))
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.modelContainer(for: [LoggedAccount.self])
|
||||
@ -124,13 +125,22 @@ struct FollowCountWidget: Widget {
|
||||
}
|
||||
.configurationDisplayName("widget.follow-count")
|
||||
.description("widget.follow-count.description")
|
||||
#if os(iOS)
|
||||
.supportedFamilies([.systemSmall, .systemMedium, .accessoryRectangular])
|
||||
#else
|
||||
.supportedFamilies([.accessoryRectangular])
|
||||
#endif
|
||||
}
|
||||
|
||||
struct Provider: AppIntentTimelineProvider {
|
||||
func recommendations() -> [AppIntentRecommendation<AccountAppIntent>] {
|
||||
let intent = AccountAppIntent()
|
||||
return [.init(intent: intent, description: intent.account?.username ?? String("Mastodon"))]
|
||||
}
|
||||
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
let placeholder: UIImage = UIImage(systemName: "person.crop.circle") ?? UIImage()
|
||||
placeholder.withTintColor(UIColor.label)
|
||||
placeholder.withTintColor(UIColor.actualLabel)
|
||||
return SimpleEntry(date: Date(), pfp: placeholder, followers: 38, configuration: AccountAppIntent())
|
||||
}
|
||||
|
||||
@ -157,7 +167,7 @@ struct FollowCountWidget: Widget {
|
||||
|
||||
private func getData(configuration: AccountAppIntent) async -> (UIImage, Int) {
|
||||
var pfp: UIImage = UIImage(systemName: "person.crop.circle") ?? UIImage()
|
||||
pfp.withTintColor(UIColor.label)
|
||||
pfp.withTintColor(UIColor.actualLabel)
|
||||
if let account = configuration.account {
|
||||
do {
|
||||
let acc = try await account.client.getString(endpoint: Accounts.verifyCredentials, forceVersion: .v1)
|
||||
@ -185,3 +195,23 @@ struct FollowCountWidget: Widget {
|
||||
let configuration: AccountAppIntent
|
||||
}
|
||||
}
|
||||
|
||||
private extension Color {
|
||||
private static var label: Color {
|
||||
#if os(iOS)
|
||||
return Color(uiColor: UIColor.label)
|
||||
#else
|
||||
return Color.white
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIColor {
|
||||
static var actualLabel: UIColor {
|
||||
#if os(iOS)
|
||||
return UIColor.label
|
||||
#else
|
||||
return UIColor.white
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,16 @@ struct FollowGoalWidgetView: View {
|
||||
var body: some View {
|
||||
if let account = entry.configuration.account {
|
||||
ZStack {
|
||||
#if os(iOS)
|
||||
if family == WidgetFamily.systemMedium {
|
||||
medium
|
||||
} else if family == WidgetFamily.accessoryRectangular {
|
||||
}
|
||||
#endif
|
||||
|
||||
if family == WidgetFamily.accessoryRectangular {
|
||||
rectangular
|
||||
} else if family == WidgetFamily.accessoryCircular {
|
||||
circular
|
||||
} else {
|
||||
Text(String("Unsupported WidgetFamily"))
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.modelContainer(for: [LoggedAccount.self])
|
||||
@ -135,13 +136,21 @@ struct FollowGoalWidget: Widget {
|
||||
}
|
||||
.configurationDisplayName("widget.follow-goal")
|
||||
.description("widget.follow-goal.description")
|
||||
#if os(iOS)
|
||||
.supportedFamilies([.systemMedium, .accessoryRectangular, .accessoryCircular])
|
||||
#else
|
||||
.supportedFamilies([.accessoryRectangular, .accessoryCircular])
|
||||
#endif
|
||||
}
|
||||
|
||||
struct Provider: AppIntentTimelineProvider {
|
||||
func recommendations() -> [AppIntentRecommendation<AccountGoalAppIntent>] {
|
||||
return []
|
||||
}
|
||||
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
let placeholder: UIImage = UIImage(systemName: "person.crop.circle") ?? UIImage()
|
||||
placeholder.withTintColor(UIColor.label)
|
||||
placeholder.withTintColor(UIColor.actualLabel)
|
||||
return SimpleEntry(date: Date(), pfp: placeholder, followers: 38, configuration: AccountGoalAppIntent())
|
||||
}
|
||||
|
||||
@ -168,7 +177,7 @@ struct FollowGoalWidget: Widget {
|
||||
|
||||
private func getData(configuration: AccountGoalAppIntent) async -> (UIImage, Int) {
|
||||
var pfp: UIImage = UIImage(systemName: "person.crop.circle") ?? UIImage()
|
||||
pfp.withTintColor(UIColor.label)
|
||||
pfp.withTintColor(UIColor.actualLabel)
|
||||
if let account = configuration.account {
|
||||
do {
|
||||
let acc = try await account.client.getString(endpoint: Accounts.verifyCredentials, forceVersion: .v1)
|
||||
@ -196,3 +205,23 @@ struct FollowGoalWidget: Widget {
|
||||
let configuration: AccountGoalAppIntent
|
||||
}
|
||||
}
|
||||
|
||||
private extension Color {
|
||||
private static var label: Color {
|
||||
#if os(iOS)
|
||||
return Color(uiColor: UIColor.label)
|
||||
#else
|
||||
return Color.white
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIColor {
|
||||
static var actualLabel: UIColor {
|
||||
#if os(iOS)
|
||||
return UIColor.label
|
||||
#else
|
||||
return UIColor.white
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import UIKit
|
||||
|
||||
// this is messy but it's alr
|
||||
|
||||
#if os(iOS)
|
||||
public class AppDelegate: NSObject, UIWindowSceneDelegate, Sendable, UIApplicationDelegate {
|
||||
public var window: UIWindow?
|
||||
public private(set) var windowWidth: CGFloat = UIScreen.main.bounds.size.width
|
||||
@ -70,6 +71,7 @@ public class AppDelegate: NSObject, UIWindowSceneDelegate, Sendable, UIApplicati
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public extension URL {
|
||||
static let placeholder: URL = URL(string: "https://cdn.pixabay.com/photo/2023/08/28/20/32/flower-8220018_1280.jpg")!
|
||||
@ -222,3 +224,10 @@ public struct LinkHandler {
|
||||
}
|
||||
|
||||
extension LinkHandler: Sendable {}
|
||||
|
||||
public extension Array where Element: Hashable {
|
||||
func uniqued() -> [Element] {
|
||||
var seen = Set<Element>()
|
||||
return filter { seen.insert($0).inserted }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user