BTHLABS-58: Share Extension in Apple Apps
This commit is contained in:
parent
0c12f52569
commit
99e9226338
|
@ -7,11 +7,33 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
4C1159202E8B055F003B34AD /* Save to HotPocket.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4CBCEA4F2E81CB9500722009 /* Save to HotPocket.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
4C2F0C692E851BBD0033F5C2 /* Save to HotPocket.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4C2F0C5E2E851BBD0033F5C2 /* Save to HotPocket.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
4C70F30D2E8869FB00320048 /* HPShareExtensionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F30C2E8869FB00320048 /* HPShareExtensionHelper.m */; };
|
||||||
|
4C70F30E2E8869FB00320048 /* HPShareExtensionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F30C2E8869FB00320048 /* HPShareExtensionHelper.m */; };
|
||||||
|
4C70F3152E886A8F00320048 /* HPSharedItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F3142E886A8F00320048 /* HPSharedItem.m */; };
|
||||||
|
4C70F3162E886A8F00320048 /* HPSharedItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F3142E886A8F00320048 /* HPSharedItem.m */; };
|
||||||
|
4C70F3192E886ADD00320048 /* HPSharedItemsContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F3182E886ADD00320048 /* HPSharedItemsContainer.m */; };
|
||||||
|
4C70F31A2E886ADD00320048 /* HPSharedItemsContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70F3182E886ADD00320048 /* HPSharedItemsContainer.m */; };
|
||||||
4CABCAD62E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
4CABCAD62E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
4CABCAE02E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
4CABCAE02E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
4C1159212E8B055F003B34AD /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 4CABCA922E56F0C800D8A354 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 4CBCEA4E2E81CB9500722009;
|
||||||
|
remoteInfo = "macOS (Share Extension)";
|
||||||
|
};
|
||||||
|
4C2F0C672E851BBD0033F5C2 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 4CABCA922E56F0C800D8A354 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 4C2F0C5D2E851BBD0033F5C2;
|
||||||
|
remoteInfo = "iOS (Share Extension)";
|
||||||
|
};
|
||||||
4CABCAD72E56F0C900D8A354 /* PBXContainerItemProxy */ = {
|
4CABCAD72E56F0C900D8A354 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 4CABCA922E56F0C800D8A354 /* Project object */;
|
containerPortal = 4CABCA922E56F0C800D8A354 /* Project object */;
|
||||||
|
@ -36,6 +58,7 @@
|
||||||
dstSubfolderSpec = 13;
|
dstSubfolderSpec = 13;
|
||||||
files = (
|
files = (
|
||||||
4CABCAD62E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */,
|
4CABCAD62E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */,
|
||||||
|
4C2F0C692E851BBD0033F5C2 /* Save to HotPocket.appex in Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
name = "Embed Foundation Extensions";
|
name = "Embed Foundation Extensions";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -46,6 +69,7 @@
|
||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 13;
|
dstSubfolderSpec = 13;
|
||||||
files = (
|
files = (
|
||||||
|
4C1159202E8B055F003B34AD /* Save to HotPocket.appex in Embed Foundation Extensions */,
|
||||||
4CABCAE02E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */,
|
4CABCAE02E56F0C900D8A354 /* HotPocket Extension.appex in Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
name = "Embed Foundation Extensions";
|
name = "Embed Foundation Extensions";
|
||||||
|
@ -54,22 +78,64 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
4C2F0C5E2E851BBD0033F5C2 /* Save to HotPocket.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Save to HotPocket.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
4C70F30B2E8869FB00320048 /* HPShareExtensionHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPShareExtensionHelper.h; sourceTree = "<group>"; };
|
||||||
|
4C70F30C2E8869FB00320048 /* HPShareExtensionHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPShareExtensionHelper.m; sourceTree = "<group>"; };
|
||||||
|
4C70F3132E886A8F00320048 /* HPSharedItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPSharedItem.h; sourceTree = "<group>"; };
|
||||||
|
4C70F3142E886A8F00320048 /* HPSharedItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPSharedItem.m; sourceTree = "<group>"; };
|
||||||
|
4C70F3172E886ADD00320048 /* HPSharedItemsContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPSharedItemsContainer.h; sourceTree = "<group>"; };
|
||||||
|
4C70F3182E886ADD00320048 /* HPSharedItemsContainer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPSharedItemsContainer.m; sourceTree = "<group>"; };
|
||||||
4CABCAB02E56F0C900D8A354 /* HotPocket.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HotPocket.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
4CABCAB02E56F0C900D8A354 /* HotPocket.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HotPocket.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4CABCAC62E56F0C900D8A354 /* HotPocket.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HotPocket.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
4CABCAC62E56F0C900D8A354 /* HotPocket.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HotPocket.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "HotPocket Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "HotPocket Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "HotPocket Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "HotPocket Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
4CBCEA4F2E81CB9500722009 /* Save to HotPocket.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Save to HotPocket.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
4C2F0C6D2E851BBD0033F5C2 /* Exceptions for "iOS (Share Extension)" folder in "iOS (Share Extension)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Info.plist,
|
||||||
|
);
|
||||||
|
target = 4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */;
|
||||||
|
};
|
||||||
|
4C2F0C6F2E851BF90033F5C2 /* Exceptions for "Shared (App)" folder in "iOS (Share Extension)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Assets.xcassets,
|
||||||
|
HPAPI.m,
|
||||||
|
HPCredentialsHelper.m,
|
||||||
|
HPRPCClient.m,
|
||||||
|
"NSURL+HotPocketExtensions.m",
|
||||||
|
"Resources/icon-mac-384.png",
|
||||||
|
);
|
||||||
|
target = 4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */;
|
||||||
|
};
|
||||||
|
4C3B958C2E83C83A00F4F82C /* Exceptions for "macOS (App)" folder in "HotPocket (macOS)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Info.plist,
|
||||||
|
);
|
||||||
|
target = 4CABCAC52E56F0C900D8A354 /* HotPocket (macOS) */;
|
||||||
|
};
|
||||||
|
4C7A01792E867D6200DEA460 /* Exceptions for "iOS (App)" folder in "iOS (Share Extension)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
MultilineLabel.m,
|
||||||
|
);
|
||||||
|
target = 4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */;
|
||||||
|
};
|
||||||
4CABCB042E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (iOS)" target */ = {
|
4CABCB042E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (iOS)" target */ = {
|
||||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
"/Localized: Resources/Main.html",
|
|
||||||
Assets.xcassets,
|
Assets.xcassets,
|
||||||
|
HPAPI.m,
|
||||||
|
HPAuthFlow.m,
|
||||||
|
HPCredentialsHelper.m,
|
||||||
|
HPRPCClient.m,
|
||||||
|
"NSURL+HotPocketExtensions.m",
|
||||||
"Resources/icon-mac-384.png",
|
"Resources/icon-mac-384.png",
|
||||||
Resources/Script.js,
|
|
||||||
Resources/Style.css,
|
|
||||||
ViewController.m,
|
|
||||||
);
|
);
|
||||||
target = 4CABCAAF2E56F0C900D8A354 /* HotPocket (iOS) */;
|
target = 4CABCAAF2E56F0C900D8A354 /* HotPocket (iOS) */;
|
||||||
};
|
};
|
||||||
|
@ -90,12 +156,13 @@
|
||||||
4CABCB0E2E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (macOS)" target */ = {
|
4CABCB0E2E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (macOS)" target */ = {
|
||||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
"/Localized: Resources/Main.html",
|
|
||||||
Assets.xcassets,
|
Assets.xcassets,
|
||||||
|
HPAPI.m,
|
||||||
|
HPAuthFlow.m,
|
||||||
|
HPCredentialsHelper.m,
|
||||||
|
HPRPCClient.m,
|
||||||
|
"NSURL+HotPocketExtensions.m",
|
||||||
"Resources/icon-mac-384.png",
|
"Resources/icon-mac-384.png",
|
||||||
Resources/Script.js,
|
|
||||||
Resources/Style.css,
|
|
||||||
ViewController.m,
|
|
||||||
);
|
);
|
||||||
target = 4CABCAC52E56F0C900D8A354 /* HotPocket (macOS) */;
|
target = 4CABCAC52E56F0C900D8A354 /* HotPocket (macOS) */;
|
||||||
};
|
};
|
||||||
|
@ -134,14 +201,43 @@
|
||||||
);
|
);
|
||||||
target = 4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */;
|
target = 4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */;
|
||||||
};
|
};
|
||||||
|
4CBCEA612E81CB9500722009 /* Exceptions for "macOS (Share Extension)" folder in "macOS (Share Extension)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Info.plist,
|
||||||
|
);
|
||||||
|
target = 4CBCEA4E2E81CB9500722009 /* macOS (Share Extension) */;
|
||||||
|
};
|
||||||
|
4CBCEA632E81CBC800722009 /* Exceptions for "Shared (App)" folder in "macOS (Share Extension)" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
Assets.xcassets,
|
||||||
|
HPAPI.m,
|
||||||
|
HPCredentialsHelper.m,
|
||||||
|
HPRPCClient.m,
|
||||||
|
"NSURL+HotPocketExtensions.m",
|
||||||
|
"Resources/icon-mac-384.png",
|
||||||
|
);
|
||||||
|
target = 4CBCEA4E2E81CB9500722009 /* macOS (Share Extension) */;
|
||||||
|
};
|
||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
4C2F0C5F2E851BBD0033F5C2 /* iOS (Share Extension) */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
4C2F0C6D2E851BBD0033F5C2 /* Exceptions for "iOS (Share Extension)" folder in "iOS (Share Extension)" target */,
|
||||||
|
);
|
||||||
|
path = "iOS (Share Extension)";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CABCA962E56F0C800D8A354 /* Shared (App) */ = {
|
4CABCA962E56F0C800D8A354 /* Shared (App) */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
exceptions = (
|
exceptions = (
|
||||||
4CABCB042E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (iOS)" target */,
|
4CABCB042E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (iOS)" target */,
|
||||||
4CABCB0E2E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (macOS)" target */,
|
4CABCB0E2E56F0C900D8A354 /* Exceptions for "Shared (App)" folder in "HotPocket (macOS)" target */,
|
||||||
|
4CBCEA632E81CBC800722009 /* Exceptions for "Shared (App)" folder in "macOS (Share Extension)" target */,
|
||||||
|
4C2F0C6F2E851BF90033F5C2 /* Exceptions for "Shared (App)" folder in "iOS (Share Extension)" target */,
|
||||||
);
|
);
|
||||||
path = "Shared (App)";
|
path = "Shared (App)";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -163,12 +259,16 @@
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
exceptions = (
|
exceptions = (
|
||||||
4CABCB0D2E56F0C900D8A354 /* Exceptions for "iOS (App)" folder in "HotPocket (iOS)" target */,
|
4CABCB0D2E56F0C900D8A354 /* Exceptions for "iOS (App)" folder in "HotPocket (iOS)" target */,
|
||||||
|
4C7A01792E867D6200DEA460 /* Exceptions for "iOS (App)" folder in "iOS (Share Extension)" target */,
|
||||||
);
|
);
|
||||||
path = "iOS (App)";
|
path = "iOS (App)";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4CABCAC72E56F0C900D8A354 /* macOS (App) */ = {
|
4CABCAC72E56F0C900D8A354 /* macOS (App) */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
4C3B958C2E83C83A00F4F82C /* Exceptions for "macOS (App)" folder in "HotPocket (macOS)" target */,
|
||||||
|
);
|
||||||
path = "macOS (App)";
|
path = "macOS (App)";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -188,9 +288,24 @@
|
||||||
path = "macOS (Extension)";
|
path = "macOS (Extension)";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CBCEA502E81CB9500722009 /* macOS (Share Extension) */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
4CBCEA612E81CB9500722009 /* Exceptions for "macOS (Share Extension)" folder in "macOS (Share Extension)" target */,
|
||||||
|
);
|
||||||
|
path = "macOS (Share Extension)";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
4C2F0C5B2E851BBD0033F5C2 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
4CABCAAD2E56F0C900D8A354 /* Frameworks */ = {
|
4CABCAAD2E56F0C900D8A354 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -219,18 +334,49 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
4CBCEA4C2E81CB9500722009 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
4C11591F2E8B055F003B34AD /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
4C70F30A2E8869D200320048 /* Shared (Share Extension) */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4C70F30B2E8869FB00320048 /* HPShareExtensionHelper.h */,
|
||||||
|
4C70F30C2E8869FB00320048 /* HPShareExtensionHelper.m */,
|
||||||
|
4C70F3132E886A8F00320048 /* HPSharedItem.h */,
|
||||||
|
4C70F3142E886A8F00320048 /* HPSharedItem.m */,
|
||||||
|
4C70F3172E886ADD00320048 /* HPSharedItemsContainer.h */,
|
||||||
|
4C70F3182E886ADD00320048 /* HPSharedItemsContainer.m */,
|
||||||
|
);
|
||||||
|
path = "Shared (Share Extension)";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CABCA912E56F0C800D8A354 = {
|
4CABCA912E56F0C800D8A354 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CABCA962E56F0C800D8A354 /* Shared (App) */,
|
4CABCA962E56F0C800D8A354 /* Shared (App) */,
|
||||||
4CABCAA02E56F0C900D8A354 /* Shared (Extension) */,
|
4CABCAA02E56F0C900D8A354 /* Shared (Extension) */,
|
||||||
|
4C70F30A2E8869D200320048 /* Shared (Share Extension) */,
|
||||||
4CABCAB22E56F0C900D8A354 /* iOS (App) */,
|
4CABCAB22E56F0C900D8A354 /* iOS (App) */,
|
||||||
4CABCAC72E56F0C900D8A354 /* macOS (App) */,
|
4CABCAC72E56F0C900D8A354 /* macOS (App) */,
|
||||||
4CABCAD92E56F0C900D8A354 /* iOS (Extension) */,
|
4CABCAD92E56F0C900D8A354 /* iOS (Extension) */,
|
||||||
4CABCAE32E56F0C900D8A354 /* macOS (Extension) */,
|
4CABCAE32E56F0C900D8A354 /* macOS (Extension) */,
|
||||||
|
4CBCEA502E81CB9500722009 /* macOS (Share Extension) */,
|
||||||
|
4C2F0C5F2E851BBD0033F5C2 /* iOS (Share Extension) */,
|
||||||
|
4C11591F2E8B055F003B34AD /* Frameworks */,
|
||||||
4CABCAB12E56F0C900D8A354 /* Products */,
|
4CABCAB12E56F0C900D8A354 /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -242,6 +388,8 @@
|
||||||
4CABCAC62E56F0C900D8A354 /* HotPocket.app */,
|
4CABCAC62E56F0C900D8A354 /* HotPocket.app */,
|
||||||
4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */,
|
4CABCAD52E56F0C900D8A354 /* HotPocket Extension.appex */,
|
||||||
4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */,
|
4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */,
|
||||||
|
4CBCEA4F2E81CB9500722009 /* Save to HotPocket.appex */,
|
||||||
|
4C2F0C5E2E851BBD0033F5C2 /* Save to HotPocket.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -249,6 +397,28 @@
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 4C2F0C6A2E851BBD0033F5C2 /* Build configuration list for PBXNativeTarget "iOS (Share Extension)" */;
|
||||||
|
buildPhases = (
|
||||||
|
4C2F0C5A2E851BBD0033F5C2 /* Sources */,
|
||||||
|
4C2F0C5B2E851BBD0033F5C2 /* Frameworks */,
|
||||||
|
4C2F0C5C2E851BBD0033F5C2 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
4C2F0C5F2E851BBD0033F5C2 /* iOS (Share Extension) */,
|
||||||
|
);
|
||||||
|
name = "iOS (Share Extension)";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = "iOS (Share Extension)";
|
||||||
|
productReference = 4C2F0C5E2E851BBD0033F5C2 /* Save to HotPocket.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
4CABCAAF2E56F0C900D8A354 /* HotPocket (iOS) */ = {
|
4CABCAAF2E56F0C900D8A354 /* HotPocket (iOS) */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 4CABCB0A2E56F0C900D8A354 /* Build configuration list for PBXNativeTarget "HotPocket (iOS)" */;
|
buildConfigurationList = 4CABCB0A2E56F0C900D8A354 /* Build configuration list for PBXNativeTarget "HotPocket (iOS)" */;
|
||||||
|
@ -262,6 +432,7 @@
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
4CABCAD82E56F0C900D8A354 /* PBXTargetDependency */,
|
4CABCAD82E56F0C900D8A354 /* PBXTargetDependency */,
|
||||||
|
4C2F0C682E851BBD0033F5C2 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
4CABCAB22E56F0C900D8A354 /* iOS (App) */,
|
4CABCAB22E56F0C900D8A354 /* iOS (App) */,
|
||||||
|
@ -286,6 +457,7 @@
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
4CABCAE22E56F0C900D8A354 /* PBXTargetDependency */,
|
4CABCAE22E56F0C900D8A354 /* PBXTargetDependency */,
|
||||||
|
4C1159222E8B055F003B34AD /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
4CABCAC72E56F0C900D8A354 /* macOS (App) */,
|
4CABCAC72E56F0C900D8A354 /* macOS (App) */,
|
||||||
|
@ -341,6 +513,28 @@
|
||||||
productReference = 4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */;
|
productReference = 4CABCADF2E56F0C900D8A354 /* HotPocket Extension.appex */;
|
||||||
productType = "com.apple.product-type.app-extension";
|
productType = "com.apple.product-type.app-extension";
|
||||||
};
|
};
|
||||||
|
4CBCEA4E2E81CB9500722009 /* macOS (Share Extension) */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 4CBCEA602E81CB9500722009 /* Build configuration list for PBXNativeTarget "macOS (Share Extension)" */;
|
||||||
|
buildPhases = (
|
||||||
|
4CBCEA4B2E81CB9500722009 /* Sources */,
|
||||||
|
4CBCEA4C2E81CB9500722009 /* Frameworks */,
|
||||||
|
4CBCEA4D2E81CB9500722009 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
4CBCEA502E81CB9500722009 /* macOS (Share Extension) */,
|
||||||
|
);
|
||||||
|
name = "macOS (Share Extension)";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = "macOS (Share Extension)";
|
||||||
|
productReference = 4CBCEA4F2E81CB9500722009 /* Save to HotPocket.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
|
@ -350,6 +544,9 @@
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastUpgradeCheck = 1640;
|
LastUpgradeCheck = 1640;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
4C2F0C5D2E851BBD0033F5C2 = {
|
||||||
|
CreatedOnToolsVersion = 26.0;
|
||||||
|
};
|
||||||
4CABCAAF2E56F0C900D8A354 = {
|
4CABCAAF2E56F0C900D8A354 = {
|
||||||
CreatedOnToolsVersion = 16.4;
|
CreatedOnToolsVersion = 16.4;
|
||||||
};
|
};
|
||||||
|
@ -362,6 +559,9 @@
|
||||||
4CABCADE2E56F0C900D8A354 = {
|
4CABCADE2E56F0C900D8A354 = {
|
||||||
CreatedOnToolsVersion = 16.4;
|
CreatedOnToolsVersion = 16.4;
|
||||||
};
|
};
|
||||||
|
4CBCEA4E2E81CB9500722009 = {
|
||||||
|
CreatedOnToolsVersion = 16.4;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 4CABCA952E56F0C800D8A354 /* Build configuration list for PBXProject "HotPocket" */;
|
buildConfigurationList = 4CABCA952E56F0C800D8A354 /* Build configuration list for PBXProject "HotPocket" */;
|
||||||
|
@ -382,11 +582,20 @@
|
||||||
4CABCAC52E56F0C900D8A354 /* HotPocket (macOS) */,
|
4CABCAC52E56F0C900D8A354 /* HotPocket (macOS) */,
|
||||||
4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */,
|
4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */,
|
||||||
4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */,
|
4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */,
|
||||||
|
4CBCEA4E2E81CB9500722009 /* macOS (Share Extension) */,
|
||||||
|
4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
4C2F0C5C2E851BBD0033F5C2 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
4CABCAAE2E56F0C900D8A354 /* Resources */ = {
|
4CABCAAE2E56F0C900D8A354 /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -415,9 +624,26 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
4CBCEA4D2E81CB9500722009 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
4C2F0C5A2E851BBD0033F5C2 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
4C70F30E2E8869FB00320048 /* HPShareExtensionHelper.m in Sources */,
|
||||||
|
4C70F3152E886A8F00320048 /* HPSharedItem.m in Sources */,
|
||||||
|
4C70F3192E886ADD00320048 /* HPSharedItemsContainer.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
4CABCAAC2E56F0C900D8A354 /* Sources */ = {
|
4CABCAAC2E56F0C900D8A354 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -446,9 +672,29 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
4CBCEA4B2E81CB9500722009 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
4C70F3162E886A8F00320048 /* HPSharedItem.m in Sources */,
|
||||||
|
4C70F30D2E8869FB00320048 /* HPShareExtensionHelper.m in Sources */,
|
||||||
|
4C70F31A2E886ADD00320048 /* HPSharedItemsContainer.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
4C1159222E8B055F003B34AD /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 4CBCEA4E2E81CB9500722009 /* macOS (Share Extension) */;
|
||||||
|
targetProxy = 4C1159212E8B055F003B34AD /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
4C2F0C682E851BBD0033F5C2 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 4C2F0C5D2E851BBD0033F5C2 /* iOS (Share Extension) */;
|
||||||
|
targetProxy = 4C2F0C672E851BBD0033F5C2 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
4CABCAD82E56F0C900D8A354 /* PBXTargetDependency */ = {
|
4CABCAD82E56F0C900D8A354 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */;
|
target = 4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */;
|
||||||
|
@ -462,6 +708,71 @@
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
4C2F0C6B2E851BBD0033F5C2 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 2025092901;
|
||||||
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to HotPocket";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 25.9.17;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
|
||||||
|
PRODUCT_NAME = "Save to HotPocket";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
4C2F0C6C2E851BBD0033F5C2 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to HotPocket";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 25.9.17;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
|
||||||
|
PRODUCT_NAME = "Save to HotPocket";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
4CABCB062E56F0C900D8A354 /* Debug */ = {
|
4CABCB062E56F0C900D8A354 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -487,6 +798,10 @@
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
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_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
|
@ -517,6 +832,10 @@
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
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_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
@ -528,6 +847,7 @@
|
||||||
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 = "iOS (App)/HotPocket (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2025091701;
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
@ -539,7 +859,8 @@
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -569,6 +890,7 @@
|
||||||
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 = "iOS (App)/HotPocket (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2025091701;
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
@ -580,7 +902,8 @@
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -610,7 +933,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2025091701;
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
@ -625,7 +948,7 @@
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 25.9.17;
|
MARKETING_VERSION = 25.9.17;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
|
@ -633,6 +956,7 @@
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
@ -658,7 +982,7 @@
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 25.9.17;
|
MARKETING_VERSION = 25.9.17;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
|
@ -666,6 +990,7 @@
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
@ -678,12 +1003,13 @@
|
||||||
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 = "macOS (App)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2025091701;
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "macOS (App)/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
@ -721,6 +1047,7 @@
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "macOS (App)/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
@ -859,9 +1186,76 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
4CBCEA5E2E81CB9500722009 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "macOS (Share Extension)/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to HotPocket";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@executable_path/../../../../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
|
MARKETING_VERSION = 25.9.17;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
|
||||||
|
PRODUCT_NAME = "Save to HotPocket";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
4CBCEA5F2E81CB9500722009 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 2025091701;
|
||||||
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "macOS (Share Extension)/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to HotPocket";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@executable_path/../../../../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
|
MARKETING_VERSION = 25.9.17;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
|
||||||
|
PRODUCT_NAME = "Save to HotPocket";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
4C2F0C6A2E851BBD0033F5C2 /* Build configuration list for PBXNativeTarget "iOS (Share Extension)" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
4C2F0C6B2E851BBD0033F5C2 /* Debug */,
|
||||||
|
4C2F0C6C2E851BBD0033F5C2 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
4CABCA952E56F0C800D8A354 /* Build configuration list for PBXProject "HotPocket" */ = {
|
4CABCA952E56F0C800D8A354 /* Build configuration list for PBXProject "HotPocket" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
@ -907,6 +1301,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
4CBCEA602E81CB9500722009 /* Build configuration list for PBXNativeTarget "macOS (Share Extension)" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
4CBCEA5E2E81CB9500722009 /* Debug */,
|
||||||
|
4CBCEA5F2E81CB9500722009 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 4CABCA922E56F0C800D8A354 /* Project object */;
|
rootObject = 4CABCA922E56F0C800D8A354 /* Project object */;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "2600"
|
||||||
|
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 = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.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 = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "2600"
|
||||||
|
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 = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.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 = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "2600"
|
||||||
|
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 = "4CABCAD42E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket Extension.appex"
|
||||||
|
BlueprintName = "HotPocket Extension (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.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">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAAF2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (iOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "2600"
|
||||||
|
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 = "4CABCADE2E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket Extension.appex"
|
||||||
|
BlueprintName = "HotPocket Extension (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.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">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "2600"
|
||||||
|
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 = "4CBCEA4E2E81CB9500722009"
|
||||||
|
BuildableName = "Save to HotPocket.appex"
|
||||||
|
BlueprintName = "macOS (Share Extension)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.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">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4CABCAC52E56F0C900D8A354"
|
||||||
|
BuildableName = "HotPocket.app"
|
||||||
|
BlueprintName = "HotPocket (macOS)"
|
||||||
|
ReferencedContainer = "container:HotPocket.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.463",
|
||||||
|
"green" : "0.392",
|
||||||
|
"red" : "0.933"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x67",
|
||||||
|
"green" : "0xA7",
|
||||||
|
"red" : "0x0E"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x2E",
|
||||||
|
"green" : "0x96",
|
||||||
|
"red" : "0xF1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
32
services/apple/Shared (App)/HPAPI.h
Normal file
32
services/apple/Shared (App)/HPAPI.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// HPAPI.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 23/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "HPRPCClient.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef void (^HPAPICheckAuthCompletionHandler)(BOOL authValid, NSError * _Nullable error, NSString * _Nullable callId);
|
||||||
|
|
||||||
|
@interface HPAPI : NSObject
|
||||||
|
|
||||||
|
@property (nullable) HPRPCClient *rpcClient;
|
||||||
|
@property NSMutableSet *callIds;
|
||||||
|
|
||||||
|
-(id)initWithRPCClientDelegate:(id<HPRPCClientDelegate>)delegate;
|
||||||
|
|
||||||
|
+(NSDictionary *)getAccessTokenMeta;
|
||||||
|
|
||||||
|
-(NSString *)checkAuth;
|
||||||
|
-(void)checkAuth:(HPAPICheckAuthCompletionHandler)completionHandler;
|
||||||
|
-(NSString *)save:(NSURL *)url;
|
||||||
|
-(BOOL)save:(NSURL *)url completionHandler:(HPRPCClientCompletionHandler)completionHandler;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
148
services/apple/Shared (App)/HPAPI.m
Normal file
148
services/apple/Shared (App)/HPAPI.m
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
//
|
||||||
|
// HPAPI.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 23/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "HPAPI.h"
|
||||||
|
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "HPRPCClient.h"
|
||||||
|
|
||||||
|
@implementation HPAPI (HPAPIPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
-(void)updateRPCClientCredentials {
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
self.rpcClient.baseURL = credentials.rpcURL;
|
||||||
|
self.rpcClient.accessToken = credentials.accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPAPI
|
||||||
|
|
||||||
|
#pragma mark - Initialization
|
||||||
|
|
||||||
|
-(id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.rpcClient = [[HPRPCClient alloc] initWithBaseURL:nil accessToken:nil];
|
||||||
|
|
||||||
|
[self updateRPCClientCredentials];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(onCredentialsChanged:)
|
||||||
|
name:@"HPCredentialsChanged"
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initWithRPCClientDelegate:(id<HPRPCClientDelegate>)rpcClientDelegate {
|
||||||
|
if (self = [self init]) {
|
||||||
|
self.rpcClient.delegate = rpcClientDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)dealloc {
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public interface
|
||||||
|
|
||||||
|
+(NSDictionary *)getAccessTokenMeta {
|
||||||
|
NSString *platform = @"macOS";
|
||||||
|
#ifdef TARGET_OS_IOS
|
||||||
|
platform = @"iPhone";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return @{
|
||||||
|
@"version": [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleShortVersionString"],
|
||||||
|
@"platform": platform,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *)checkAuth {
|
||||||
|
if (self.rpcClient.hasCredentials == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *callId = [[NSUUID UUID] UUIDString];
|
||||||
|
|
||||||
|
BOOL callResult = [self.rpcClient call:callId
|
||||||
|
method:@"accounts.auth.check_access_token"
|
||||||
|
params:@[self.rpcClient.accessToken, [HPAPI getAccessTokenMeta]]
|
||||||
|
endopoint:@"/accounts/rpc/"];
|
||||||
|
if (callResult == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callId;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)checkAuth:(HPAPICheckAuthCompletionHandler)completionHandler {
|
||||||
|
if (self.rpcClient.hasCredentials == NO) {
|
||||||
|
completionHandler(NO, nil, nil);
|
||||||
|
} else {
|
||||||
|
NSString *callId = [[NSUUID UUID] UUIDString];
|
||||||
|
BOOL callResult = [self.rpcClient call:callId
|
||||||
|
method:@"accounts.auth.check_access_token"
|
||||||
|
params:@[self.rpcClient.accessToken, [HPAPI getAccessTokenMeta]]
|
||||||
|
endopoint:@"/accounts/rpc/" completionHandler:^(NSString *finalCallId, HPRPCCallResult *result) {
|
||||||
|
BOOL authValid = YES;
|
||||||
|
if (result.error != nil) {
|
||||||
|
authValid = NO;
|
||||||
|
} else if ([(NSNumber *)result.result boolValue] == NO) {
|
||||||
|
authValid = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(authValid, result.error, finalCallId);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *)save:(NSURL *)url {
|
||||||
|
if (self.rpcClient.hasCredentials == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *callId = [[NSUUID UUID] UUIDString];
|
||||||
|
BOOL callResult = [self.rpcClient call:callId method:@"saves.create" params:@[[url absoluteString]]];
|
||||||
|
if (callResult == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callId;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)save:(NSURL *)url completionHandler:(HPRPCClientCompletionHandler)completionHandler {
|
||||||
|
if (self.rpcClient.hasCredentials == NO) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == nil) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *callId = [[NSUUID UUID] UUIDString];
|
||||||
|
return [self.rpcClient call:callId
|
||||||
|
method:@"saves.create"
|
||||||
|
params:@[[url absoluteString]]
|
||||||
|
completionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Notification handlers
|
||||||
|
|
||||||
|
-(void)onCredentialsChanged:(NSNotification *)notification {
|
||||||
|
[self updateRPCClientCredentials];
|
||||||
|
}
|
||||||
|
@end
|
30
services/apple/Shared (App)/HPAuthFlow.h
Normal file
30
services/apple/Shared (App)/HPAuthFlow.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// HPAuthFlow.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 21/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface HPAuthParams : NSObject
|
||||||
|
|
||||||
|
@property (copy) NSString *authKey;
|
||||||
|
@property (copy) NSString *sessionToken;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface HPAuthFlow : NSObject
|
||||||
|
|
||||||
|
@property (nullable) NSURL *baseURL;
|
||||||
|
@property (nullable) NSString *sessionToken;
|
||||||
|
|
||||||
|
-(NSURL *)start;
|
||||||
|
-(HPAuthParams *)handlePostAuthenticateURL:(NSURL *)url;
|
||||||
|
-(BOOL)handleAuthParams:(HPAuthParams *)authParams;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
140
services/apple/Shared (App)/HPAuthFlow.m
Normal file
140
services/apple/Shared (App)/HPAuthFlow.m
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
//
|
||||||
|
// HPAuthFlow.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 21/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
|
||||||
|
#import "HPAPI.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "HPRPCClient.h"
|
||||||
|
|
||||||
|
@implementation HPAuthParams
|
||||||
|
|
||||||
|
#pragma mark - HPAuthParams implementation
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPAuthFlow (HPAuthFlowPrivate)
|
||||||
|
|
||||||
|
#pragma mark - HPAuthFlow private interface
|
||||||
|
|
||||||
|
-(NSURL *)resolveAuthenticateURL {
|
||||||
|
if (self.baseURL == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *authURL = [self.baseURL URLByAppendingPathComponent:@"/integrations/extension/authenticate/"];
|
||||||
|
|
||||||
|
if (authURL.scheme == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||||
|
|
||||||
|
NSURLComponents *authURLComponents = [NSURLComponents componentsWithURL:authURL resolvingAgainstBaseURL:NO];
|
||||||
|
authURLComponents.queryItems = @[
|
||||||
|
[NSURLQueryItem queryItemWithName:@"source" value:[[mainBundle infoDictionary] valueForKey:@"HPAuthFlowSource"]],
|
||||||
|
[NSURLQueryItem queryItemWithName:@"session_token" value:self.sessionToken],
|
||||||
|
];
|
||||||
|
|
||||||
|
return authURLComponents.URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPAuthFlow
|
||||||
|
|
||||||
|
#pragma mark - Initialization
|
||||||
|
|
||||||
|
-(id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.baseURL = nil;
|
||||||
|
self.sessionToken = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public interface
|
||||||
|
|
||||||
|
-(NSURL *)start {
|
||||||
|
if (self.baseURL == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.sessionToken == nil) {
|
||||||
|
self.sessionToken = [[NSUUID UUID] UUIDString];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [self resolveAuthenticateURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(HPAuthParams *)handlePostAuthenticateURL:(NSURL *)url {
|
||||||
|
if (url == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *postAuthenticateURLParams = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"HPAuthFlowPostAuthenticateURLParts"];
|
||||||
|
if (postAuthenticateURLParams == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||||
|
|
||||||
|
if ([urlComponents.scheme isEqualToString:[postAuthenticateURLParams valueForKey:@"scheme"]] == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([urlComponents.host isEqualToString:[postAuthenticateURLParams valueForKey:@"host"]] == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
HPAuthParams *result = [[HPAuthParams alloc] init];
|
||||||
|
for (NSURLQueryItem *queryItem in urlComponents.queryItems) {
|
||||||
|
if ([queryItem.name isEqualToString:@"auth_key"] == YES) {
|
||||||
|
result.authKey = queryItem.value;
|
||||||
|
} else if ([queryItem.name isEqualToString:@"session_token"] == YES) {
|
||||||
|
result.sessionToken = queryItem.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self.sessionToken isEqualToString:result.sessionToken] == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)handleAuthParams:(HPAuthParams *)authParams {
|
||||||
|
HPRPCClient *rpcClient = [[HPRPCClient alloc] initWithBaseURL:self.baseURL accessToken:nil];
|
||||||
|
|
||||||
|
NSArray *callParams = @[
|
||||||
|
authParams.authKey,
|
||||||
|
[HPAPI getAccessTokenMeta],
|
||||||
|
];
|
||||||
|
|
||||||
|
BOOL callResult = [rpcClient call:self.sessionToken
|
||||||
|
method:@"accounts.access_tokens.create"
|
||||||
|
params:callParams endopoint:@"/accounts/rpc/"
|
||||||
|
completionHandler:^(NSString *callId, HPRPCCallResult *result) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (result.error != nil) {
|
||||||
|
NSLog(@"-[HPAuthFlow handleAuthParams:] error=`%@`", result.error);
|
||||||
|
} else {
|
||||||
|
HPCredentialsHelper *credentialsHelper = [HPCredentialsHelper sharedHelper];
|
||||||
|
[credentialsHelper saveCredentials:[self.baseURL absoluteString] accessToken:(NSString *)result.result];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sessionToken = nil;
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"AuthFlowDidFinish" object:self];
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|
||||||
|
return callResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
32
services/apple/Shared (App)/HPCredentialsHelper.h
Normal file
32
services/apple/Shared (App)/HPCredentialsHelper.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// HPCredentialsHelper.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 19/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface HPCredentials : NSObject
|
||||||
|
|
||||||
|
@property (nullable) NSString *baseURL;
|
||||||
|
@property (nullable) NSString *accessToken;
|
||||||
|
|
||||||
|
@property (readonly) BOOL usable;
|
||||||
|
@property (readonly) NSURL *rpcURL;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface HPCredentialsHelper : NSObject
|
||||||
|
|
||||||
|
+(instancetype)sharedHelper;
|
||||||
|
|
||||||
|
-(HPCredentials *)getCredentials;
|
||||||
|
-(BOOL)saveCredentials:(NSString *)baseURL accessToken:(NSString *)accessToken;
|
||||||
|
-(BOOL)clearCredentials;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
218
services/apple/Shared (App)/HPCredentialsHelper.m
Normal file
218
services/apple/Shared (App)/HPCredentialsHelper.m
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
//
|
||||||
|
// HPCredentialsHelper.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 19/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Security/Security.h>
|
||||||
|
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
|
||||||
|
@implementation HPCredentials
|
||||||
|
|
||||||
|
#pragma mark - HPCredentials implementation
|
||||||
|
|
||||||
|
-(id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.baseURL = nil;
|
||||||
|
self.accessToken = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)usable {
|
||||||
|
return (self.baseURL != nil && self.accessToken != nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSURL *)rpcURL {
|
||||||
|
return [NSURL URLWithString:self.baseURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *)description {
|
||||||
|
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||||
|
|
||||||
|
if (self.baseURL == nil) {
|
||||||
|
[attributes setValue:@"(null)" forKey:@"baseURL"];
|
||||||
|
} else {
|
||||||
|
[attributes setValue:self.baseURL forKey:@"baseURL"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.accessToken == nil) {
|
||||||
|
[attributes setValue:@"(null)" forKey:@"accessToken"];
|
||||||
|
} else {
|
||||||
|
[attributes setValue:@"***" forKey:@"accessToken"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p; %@>", NSStringFromClass([self class]), self, attributes];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPCredentialsHelper (HPCredentialsHelperPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
-(NSString *)getService {
|
||||||
|
#ifdef DEBUG
|
||||||
|
return @"pl.bthlabs.HotPocket.Debug";
|
||||||
|
#else
|
||||||
|
return @"pl.bthlabs.HotPocket";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSData *)getKeychainItem:(NSString *)service account:(NSString *)account {
|
||||||
|
if (service == nil || account == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *query = @{
|
||||||
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||||
|
(__bridge id)kSecAttrService: service,
|
||||||
|
(__bridge id)kSecAttrAccount: account,
|
||||||
|
(__bridge id)kSecReturnData: @YES,
|
||||||
|
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
|
||||||
|
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||||
|
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||||
|
};
|
||||||
|
|
||||||
|
CFTypeRef resultData = NULL;
|
||||||
|
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &resultData);
|
||||||
|
|
||||||
|
if (status == errSecSuccess && resultData != NULL) {
|
||||||
|
NSData *result = (__bridge_transfer NSData *)resultData;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||||
|
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||||
|
NSLog(@"-[HPCredentialsHelper getKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)createKeychainItemWithValue:(NSData *)value service:(NSString *)service account:(NSString *)account {
|
||||||
|
if (value == nil || service == nil || account == nil) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *attributes = @{
|
||||||
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||||
|
(__bridge id)kSecAttrService: service,
|
||||||
|
(__bridge id)kSecAttrAccount: account,
|
||||||
|
(__bridge id)kSecValueData: value,
|
||||||
|
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||||
|
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||||
|
};
|
||||||
|
|
||||||
|
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
|
||||||
|
|
||||||
|
if (status != errSecSuccess) {
|
||||||
|
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||||
|
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||||
|
NSLog(@"-[HPCredentialsHelper createKeychainItemWithValue:service:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)deleteKeychainItem:(NSString *)service account:(NSString *)account {
|
||||||
|
if (service == nil || account == nil) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *query = @{
|
||||||
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||||
|
(__bridge id)kSecAttrService: service,
|
||||||
|
(__bridge id)kSecAttrAccount: account,
|
||||||
|
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||||
|
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||||
|
};
|
||||||
|
|
||||||
|
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||||
|
|
||||||
|
if (status != errSecSuccess) {
|
||||||
|
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||||
|
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||||
|
NSLog(@"-[HPCredentialsHelper deleteKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPCredentialsHelper
|
||||||
|
|
||||||
|
#pragma mark - Initialization
|
||||||
|
|
||||||
|
+(instancetype)sharedHelper {
|
||||||
|
static HPCredentialsHelper *sharedInstance = nil;
|
||||||
|
static dispatch_once_t initToken;
|
||||||
|
dispatch_once(&initToken, ^{
|
||||||
|
sharedInstance = [[self alloc] init];
|
||||||
|
});
|
||||||
|
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public interface
|
||||||
|
|
||||||
|
-(HPCredentials *)getCredentials {
|
||||||
|
HPCredentials *result = [[HPCredentials alloc] init];
|
||||||
|
|
||||||
|
NSData *itemData = [self getKeychainItem:[self getService] account:@"RPC"];
|
||||||
|
if (itemData != nil) {
|
||||||
|
NSError *error;
|
||||||
|
NSDictionary *itemPayload = [NSJSONSerialization JSONObjectWithData:itemData
|
||||||
|
options:NSJSONReadingTopLevelDictionaryAssumed
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (error != nil) {
|
||||||
|
NSLog(@"-[HPCredentialsHalper getCredentials] error=`%@`", error);
|
||||||
|
} else if (itemPayload != nil) {
|
||||||
|
result.baseURL = [itemPayload valueForKey:@"baseURL"];
|
||||||
|
result.accessToken = [itemPayload valueForKey:@"accessToken"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)saveCredentials:(NSString *)baseURL accessToken:(NSString *)accessToken {
|
||||||
|
NSMutableDictionary *itemPayload = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||||
|
|
||||||
|
if (baseURL != nil) {
|
||||||
|
[itemPayload setValue:baseURL forKey:@"baseURL"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessToken != nil) {
|
||||||
|
[itemPayload setValue:accessToken forKey:@"accessToken"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSError *error;
|
||||||
|
NSData *itemData = [NSJSONSerialization dataWithJSONObject:itemPayload options:0 error:&error];
|
||||||
|
|
||||||
|
if (error != nil) {
|
||||||
|
NSLog(@"-[HPCredentialsHalper saveCredentials:accessToken:] error=`%@`", error);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL saveResult = [self createKeychainItemWithValue:itemData service:[self getService] account:@"RPC"];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
|
||||||
|
|
||||||
|
return saveResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)clearCredentials {
|
||||||
|
BOOL deleteResult = [self deleteKeychainItem:[self getService] account:@"RPC"];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
|
||||||
|
|
||||||
|
return deleteResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
44
services/apple/Shared (App)/HPRPCClient.h
Normal file
44
services/apple/Shared (App)/HPRPCClient.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// HPRPCClient.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 19/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface HPRPCCallResult : NSObject
|
||||||
|
|
||||||
|
@property (nullable) NSError *error;
|
||||||
|
@property (nullable) id result;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@protocol HPRPCClientDelegate <NSObject>
|
||||||
|
|
||||||
|
-(void)rpcClientDidReceiveResult:(HPRPCCallResult *)result callId:(NSString *)callId;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
typedef void (^HPRPCClientCompletionHandler)(NSString * _Nullable callId, HPRPCCallResult * _Nullable result);
|
||||||
|
|
||||||
|
@interface HPRPCClient : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, weak) id<HPRPCClientDelegate> delegate;
|
||||||
|
@property NSURL *baseURL;
|
||||||
|
@property NSString *accessToken;
|
||||||
|
@property NSURLSession *session;
|
||||||
|
|
||||||
|
-(id)initWithBaseURL:(nullable NSURL *)baseURL accessToken:(nullable NSString *)accessToken;
|
||||||
|
|
||||||
|
-(BOOL)hasCredentials;
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint completionHandler:(HPRPCClientCompletionHandler)completionHandler;
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint;
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params;
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params completionHandler:(HPRPCClientCompletionHandler)completionHandler;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
185
services/apple/Shared (App)/HPRPCClient.m
Normal file
185
services/apple/Shared (App)/HPRPCClient.m
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
//
|
||||||
|
// HPRPCClient.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 19/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "HPRPCClient.h"
|
||||||
|
|
||||||
|
@implementation HPRPCCallResult
|
||||||
|
|
||||||
|
#pragma mark - HPRPCCallResult implementation
|
||||||
|
|
||||||
|
-(id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.error = nil;
|
||||||
|
self.result = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *)description {
|
||||||
|
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||||
|
|
||||||
|
if (self.error == nil) {
|
||||||
|
[attributes setValue:@"(null)" forKey:@"error"];
|
||||||
|
} else {
|
||||||
|
[attributes setValue:self.error forKey:@"error"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.result == nil) {
|
||||||
|
[attributes setValue:@"(null)" forKey:@"result"];
|
||||||
|
} else {
|
||||||
|
[attributes setValue:self.result forKey:@"result"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p; %@>", NSStringFromClass([self class]), self, attributes];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPRPCClient
|
||||||
|
|
||||||
|
#pragma mark - Initialization
|
||||||
|
|
||||||
|
-(id)initWithBaseURL:(nullable NSURL *)baseURL accessToken:(nullable NSString *)accessToken {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.baseURL = baseURL;
|
||||||
|
self.accessToken = accessToken;
|
||||||
|
self.session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public interface
|
||||||
|
|
||||||
|
-(BOOL)hasCredentials {
|
||||||
|
return (self.baseURL != nil && self.accessToken != nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint completionHandler:(HPRPCClientCompletionHandler)completionHandler {
|
||||||
|
if (self.baseURL == nil) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callId == nil) {
|
||||||
|
callId = [[NSUUID UUID] UUIDString];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint == nil) {
|
||||||
|
endpoint = @"/rpc/";
|
||||||
|
}
|
||||||
|
|
||||||
|
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||||
|
|
||||||
|
NSDictionary *payload = @{
|
||||||
|
@"jsonrpc": @"2.0",
|
||||||
|
@"id": callId,
|
||||||
|
@"method": method,
|
||||||
|
@"params": params,
|
||||||
|
};
|
||||||
|
|
||||||
|
NSError *error;
|
||||||
|
NSData *jsonPayload = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&error];
|
||||||
|
if (!jsonPayload) {
|
||||||
|
NSLog(@"-[HPRPCClient call:method:params:endpoint:] Unable to serialize payload: error=`%@`", error);
|
||||||
|
HPRPCCallResult *result = [[HPRPCCallResult alloc] init];
|
||||||
|
result.error = error;
|
||||||
|
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate rpcClientDidReceiveResult:result callId:callId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[self.baseURL URLByAppendingPathComponent:endpoint] resolvingAgainstBaseURL:NO];
|
||||||
|
urlComponents.queryItems = @[
|
||||||
|
[NSURLQueryItem queryItemWithName:@"method" value:method],
|
||||||
|
];
|
||||||
|
|
||||||
|
NSURL *callURL = [urlComponents URL];
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[HPRPCClient call:method:params:endpoint:] callURL=`%@`", callURL.absoluteString);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:callURL];
|
||||||
|
request.HTTPMethod = @"POST";
|
||||||
|
request.HTTPBody = jsonPayload;
|
||||||
|
|
||||||
|
[request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
|
||||||
|
[request setValue:[[mainBundle infoDictionary] valueForKey:@"HPRPCClientOrigin"] forHTTPHeaderField:@"Origin"];
|
||||||
|
|
||||||
|
if (self.accessToken != nil) {
|
||||||
|
NSString *authorization = [NSString stringWithFormat:@"Bearer %@", self.accessToken];
|
||||||
|
[request setValue:authorization forHTTPHeaderField:@"Authorization"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *build = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||||
|
NSString *userAgent = [NSString stringWithFormat:@"HotPocket/%@", build];
|
||||||
|
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
|
||||||
|
|
||||||
|
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request
|
||||||
|
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||||
|
HPRPCCallResult *result = [[HPRPCCallResult alloc] init];
|
||||||
|
if (error != nil) {
|
||||||
|
result.error = error;
|
||||||
|
} else {
|
||||||
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||||
|
if (httpResponse.statusCode != 200) {
|
||||||
|
result.error = [[NSError alloc] initWithDomain:@"pl.bthlabs.HotPocket.HPRPCClient" code:-32000 userInfo:@{
|
||||||
|
@"callId": callId,
|
||||||
|
@"url": httpResponse.URL,
|
||||||
|
@"statusCode": [NSNumber numberWithInteger:httpResponse.statusCode],
|
||||||
|
@"response": response,
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
NSError *jsonDecodeError;
|
||||||
|
NSDictionary *callResult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingTopLevelDictionaryAssumed error:&error];
|
||||||
|
if (jsonDecodeError != nil) {
|
||||||
|
result.error = jsonDecodeError;
|
||||||
|
} else {
|
||||||
|
NSDictionary *rpcError = [callResult valueForKey:@"error"];
|
||||||
|
if (rpcError != nil) {
|
||||||
|
NSNumber *rpcErrorCode = [rpcError valueForKey:@"code"];
|
||||||
|
if (rpcErrorCode == nil) {
|
||||||
|
rpcErrorCode = [NSNumber numberWithInt:-32000];
|
||||||
|
}
|
||||||
|
|
||||||
|
result.error = [[NSError alloc] initWithDomain:@"pl.bthlabs.HotPocket.HPRPCClient" code:[rpcErrorCode integerValue] userInfo:rpcError];
|
||||||
|
} else {
|
||||||
|
result.result = [callResult valueForKey:@"result"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completionHandler) {
|
||||||
|
completionHandler(callId, result);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
[task resume];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint {
|
||||||
|
return [self call:callId method:method params:params endopoint:endpoint completionHandler:^(NSString *callId, HPRPCCallResult *result) {
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate rpcClientDidReceiveResult:result callId:callId];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params {
|
||||||
|
return [self call:callId method:method params:params endopoint:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params completionHandler:(HPRPCClientCompletionHandler)completionHandler {
|
||||||
|
return [self call:callId method:method params:params endopoint:nil completionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
18
services/apple/Shared (App)/NSURL+HotPocketExtensions.h
Normal file
18
services/apple/Shared (App)/NSURL+HotPocketExtensions.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// NSURL+HotPocketExtensions.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSURL (HotPocketExtensions)
|
||||||
|
|
||||||
|
-(BOOL)isUsableInHotPocket;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
30
services/apple/Shared (App)/NSURL+HotPocketExtensions.m
Normal file
30
services/apple/Shared (App)/NSURL+HotPocketExtensions.m
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// NSURL+HotPocketExtensions.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "NSURL+HotPocketExtensions.h"
|
||||||
|
|
||||||
|
@implementation NSURL (HotPocketExtensions)
|
||||||
|
|
||||||
|
-(BOOL)isUsableInHotPocket {
|
||||||
|
static NSArray *supportedSchemes = @[@"http", @"https"];
|
||||||
|
|
||||||
|
if (self.baseURL != nil) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.scheme == nil || [supportedSchemes containsObject:self.scheme] == NO) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.host == nil || [@"" isEqualToString:self.host] == YES) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,20 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="../Style.css">
|
|
||||||
<script src="../Script.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img src="../icon-mac-384.png" width="128" height="128" alt="HotPocket Icon">
|
|
||||||
<p class="platform-ios">You can turn on Save to Hotpocket Safari extension in Settings.</p>
|
|
||||||
<p class="platform-mac state-unknown">You can turn on Save to Hotpocket extension in Safari Extensions preferences.</p>
|
|
||||||
<p class="platform-mac state-on">Save to Hotpocket extension is currently on. You can turn it off in Safari Extensions preferences.</p>
|
|
||||||
<p class="platform-mac state-off">Save to Hotpocket extension is currently off. You can turn it on in Safari Extensions preferences.</p>
|
|
||||||
<button class="platform-mac open-preferences">Quit and Open Safari Extensions Preferences…</button>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,24 +0,0 @@
|
||||||
function show(platform, enabled, useSettingsInsteadOfPreferences) {
|
|
||||||
document.body.classList.add(`platform-${platform}`);
|
|
||||||
|
|
||||||
if (useSettingsInsteadOfPreferences) {
|
|
||||||
document.getElementsByClassName('platform-mac state-on')[0].innerText = "Save to Hotpocket extension is currently on. You can turn it off in the Extensions section of Safari Settings.";
|
|
||||||
document.getElementsByClassName('platform-mac state-off')[0].innerText = "Save to Hotpocket extension is currently off. You can turn it on in the Extensions section of Safari Settings.";
|
|
||||||
document.getElementsByClassName('platform-mac state-unknown')[0].innerText = "You can turn on Save to Hotpocket extension in the Extensions section of Safari Settings.";
|
|
||||||
document.getElementsByClassName('platform-mac open-preferences')[0].innerText = "Quit and Open Safari Settings…";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof enabled === "boolean") {
|
|
||||||
document.body.classList.toggle(`state-on`, enabled);
|
|
||||||
document.body.classList.toggle(`state-off`, !enabled);
|
|
||||||
} else {
|
|
||||||
document.body.classList.remove(`state-on`);
|
|
||||||
document.body.classList.remove(`state-off`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openPreferences() {
|
|
||||||
webkit.messageHandlers.controller.postMessage("open-preferences");
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelector("button.open-preferences").addEventListener("click", openPreferences);
|
|
|
@ -1,63 +0,0 @@
|
||||||
* {
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
color-scheme: light dark;
|
|
||||||
|
|
||||||
--spacing: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #212529;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
gap: var(--spacing);
|
|
||||||
margin: 0 calc(var(--spacing) * 2);
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
font: -apple-system-short-body;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
body:not(.platform-mac, .platform-ios) :is(.platform-mac, .platform-ios) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.platform-ios .platform-mac {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.platform-mac .platform-ios {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.platform-ios .platform-mac {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body:not(.state-on, .state-off) :is(.state-on, .state-off) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.state-on :is(.state-off, .state-unknown) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.state-off :is(.state-on, .state-unknown) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
//
|
|
||||||
// ViewController.h
|
|
||||||
// Shared (App)
|
|
||||||
//
|
|
||||||
// Created by Tomek Wójcik on 21/08/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <TargetConditionals.h>
|
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
typedef UIViewController PlatformViewController;
|
|
||||||
|
|
||||||
#elif TARGET_OS_OSX
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
typedef NSViewController PlatformViewController;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@interface ViewController : PlatformViewController
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,76 +0,0 @@
|
||||||
//
|
|
||||||
// ViewController.m
|
|
||||||
// Shared (App)
|
|
||||||
//
|
|
||||||
// Created by Tomek Wójcik on 21/08/2025.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ViewController.h"
|
|
||||||
|
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
|
|
||||||
#if TARGET_OS_OSX
|
|
||||||
#import <SafariServices/SafariServices.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static NSString * const extensionBundleIdentifier = @"pl.bthlabs.HotPocket.HotPocket.Extension";
|
|
||||||
|
|
||||||
@interface ViewController () <WKNavigationDelegate, WKScriptMessageHandler>
|
|
||||||
|
|
||||||
@property (nonatomic) IBOutlet WKWebView *webView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ViewController
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
|
|
||||||
_webView.navigationDelegate = self;
|
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
|
||||||
_webView.scrollView.scrollEnabled = NO;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"controller"];
|
|
||||||
|
|
||||||
[_webView loadFileURL:[NSBundle.mainBundle URLForResource:@"Main" withExtension:@"html"] allowingReadAccessToURL:NSBundle.mainBundle.resourceURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
|
||||||
#if TARGET_OS_IOS
|
|
||||||
[webView evaluateJavaScript:@"show('ios')" completionHandler:nil];
|
|
||||||
#elif TARGET_OS_OSX
|
|
||||||
[webView evaluateJavaScript:@"show('mac')" completionHandler:nil];
|
|
||||||
|
|
||||||
[SFSafariExtensionManager getStateOfSafariExtensionWithIdentifier:extensionBundleIdentifier completionHandler:^(SFSafariExtensionState *state, NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
if (!state) {
|
|
||||||
// Insert code to inform the user something went wrong.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *isExtensionEnabledAsString = state.isEnabled ? @"true" : @"false";
|
|
||||||
if (@available(macOS 13, *))
|
|
||||||
[webView evaluateJavaScript:[NSString stringWithFormat:@"show('mac', %@, true)", isExtensionEnabledAsString] completionHandler:nil];
|
|
||||||
else
|
|
||||||
[webView evaluateJavaScript:[NSString stringWithFormat:@"show('mac', %@, false)", isExtensionEnabledAsString] completionHandler:nil];
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
|
|
||||||
#if TARGET_OS_OSX
|
|
||||||
if (![message.body isEqualToString:@"open-preferences"])
|
|
||||||
return;
|
|
||||||
|
|
||||||
[SFSafariApplication showPreferencesForExtensionWithIdentifier:extensionBundleIdentifier completionHandler:^(NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[NSApp terminate:self];
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// HPShareExtensionHelper.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class HPSharedItemsContainer;
|
||||||
|
|
||||||
|
typedef void (^HPShareExtensionHelperHandleItemsCompletionHandler)(NSURL * _Nullable url);
|
||||||
|
|
||||||
|
@interface HPShareExtensionHelper : NSObject
|
||||||
|
|
||||||
|
@property NSExtensionContext *context;
|
||||||
|
@property HPSharedItemsContainer *items;
|
||||||
|
|
||||||
|
-(instancetype)initWithContext:(NSExtensionContext *)context;
|
||||||
|
|
||||||
|
-(void)processItems:(HPShareExtensionHelperHandleItemsCompletionHandler)completionHandler;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
102
services/apple/Shared (Share Extension)/HPShareExtensionHelper.m
Normal file
102
services/apple/Shared (Share Extension)/HPShareExtensionHelper.m
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
//
|
||||||
|
// HPShareExtensionHelper.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||||
|
|
||||||
|
#import "HPShareExtensionHelper.h"
|
||||||
|
|
||||||
|
#import "HPSharedItem.h"
|
||||||
|
#import "HPSharedItemsContainer.h"
|
||||||
|
|
||||||
|
@implementation HPShareExtensionHelper (HPShareExtensionHelperPrivate)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPShareExtensionHelper
|
||||||
|
|
||||||
|
-(instancetype)initWithContext:(NSExtensionContext *)context {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.context = context;
|
||||||
|
self.items = [[HPSharedItemsContainer alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)processItems:(HPShareExtensionHelperHandleItemsCompletionHandler)completionHandler {
|
||||||
|
// Depending on the app, the URL might be stored in `public.url` attachment or elsewhere.
|
||||||
|
// For example, the YouTube app passes it in `public.plain-text`. Because of course it does.
|
||||||
|
// Furthermore, for some bizarre reason the recommended way of extracting the URL when sharing from a browser
|
||||||
|
// is to run a JS snippet and examine its output.
|
||||||
|
// This method will iterate through all the shared items and their attachments and attempt to extract
|
||||||
|
// the URL candidates.
|
||||||
|
//
|
||||||
|
// Also note that handler for `public.url` explicitly requests the payload to be corced to `NSURL *`. Leaving it
|
||||||
|
// at `NSData *` would cause iOS to, wait for it!, fetch the URL and dump the response body in the payload :D.
|
||||||
|
//
|
||||||
|
// This is so _so_ *so* dumb. But hey, at least I learned how to to "chords" in CGD ¯\_(ツ)_/¯
|
||||||
|
UTType *propertyListType = [UTType typeWithFilenameExtension:@"plist"];
|
||||||
|
|
||||||
|
dispatch_group_t dispatchGroup = dispatch_group_create();
|
||||||
|
dispatch_queue_t queue = dispatch_queue_create("HPShareExtensionHelper.processItems.queue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
|
for (NSExtensionItem *inputItem in self.context.inputItems) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[HPShareExtensionHelper processItems:] inputItem.userInfo=`%@`", inputItem);
|
||||||
|
#endif
|
||||||
|
[inputItem.attachments enumerateObjectsUsingBlock:^(NSItemProvider *attachment, NSUInteger index, BOOL *stop) {
|
||||||
|
dispatch_group_enter(dispatchGroup);
|
||||||
|
|
||||||
|
if ([attachment hasItemConformingToTypeIdentifier:propertyListType.identifier] == YES) {
|
||||||
|
[attachment loadItemForTypeIdentifier:propertyListType.identifier
|
||||||
|
options:nil
|
||||||
|
completionHandler:^(NSDictionary *payload, NSError *error) {
|
||||||
|
dispatch_async(queue, ^{
|
||||||
|
self.items.primaryItem = [[HPSharedItem alloc] initWithPayload:payload
|
||||||
|
typeIdentifier:propertyListType.identifier
|
||||||
|
error:error];
|
||||||
|
|
||||||
|
dispatch_group_leave(dispatchGroup);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
} else if ([attachment hasItemConformingToTypeIdentifier:@"public.url"] == YES) {
|
||||||
|
[attachment loadItemForTypeIdentifier:@"public.url"
|
||||||
|
options:nil
|
||||||
|
completionHandler:^(NSURL *payload, NSError *error) {
|
||||||
|
dispatch_async(queue, ^{
|
||||||
|
[self.items.candidateItems addObject:[[HPSharedItem alloc] initWithPayload:payload
|
||||||
|
typeIdentifier:@"public.url"
|
||||||
|
error:error]];
|
||||||
|
|
||||||
|
dispatch_group_leave(dispatchGroup);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
} else if ([attachment hasItemConformingToTypeIdentifier:@"public.plain-text"] == YES) {
|
||||||
|
[attachment loadItemForTypeIdentifier:@"public.plain-text"
|
||||||
|
options:nil
|
||||||
|
completionHandler:^(NSString *payload, NSError *error) {
|
||||||
|
dispatch_async(queue, ^{
|
||||||
|
[self.items.candidateItems addObject:[[HPSharedItem alloc] initWithPayload:payload
|
||||||
|
typeIdentifier:@"public.plain-text"
|
||||||
|
error:error]];
|
||||||
|
|
||||||
|
dispatch_group_leave(dispatchGroup);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
dispatch_group_leave(dispatchGroup);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
|
||||||
|
NSURL *result = [self.items resolveURL];
|
||||||
|
completionHandler(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
24
services/apple/Shared (Share Extension)/HPSharedItem.h
Normal file
24
services/apple/Shared (Share Extension)/HPSharedItem.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// HPSharedItem.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface HPSharedItem : NSObject
|
||||||
|
|
||||||
|
@property (nullable) id payload;
|
||||||
|
@property NSString *typeIdentifier;
|
||||||
|
@property (nullable) NSError *error;
|
||||||
|
|
||||||
|
-(instancetype)initWithPayload:(nullable id)payload typeIdentifier:(NSString *)typeIdentifier error:(nullable NSError *)error;
|
||||||
|
|
||||||
|
-(NSURL *)maybeURL;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
47
services/apple/Shared (Share Extension)/HPSharedItem.m
Normal file
47
services/apple/Shared (Share Extension)/HPSharedItem.m
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// HPSharedItem.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||||
|
|
||||||
|
#import "HPSharedItem.h"
|
||||||
|
|
||||||
|
@implementation HPSharedItem
|
||||||
|
|
||||||
|
-(instancetype)initWithPayload:(id)payload typeIdentifier:(NSString *)typeIdentifier error:(NSError *)error {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.payload = payload;
|
||||||
|
self.typeIdentifier = typeIdentifier;
|
||||||
|
self.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSURL *)maybeURL {
|
||||||
|
if (self.error != nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self.typeIdentifier isEqualToString:[UTType typeWithFilenameExtension:@"plist"].identifier] == YES) {
|
||||||
|
NSDictionary *propertyList = self.payload;
|
||||||
|
NSDictionary *jsHelperResult = [propertyList valueForKey:NSExtensionJavaScriptPreprocessingResultsKey];
|
||||||
|
|
||||||
|
if ([jsHelperResult valueForKey:@"iHateComputers"] == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSURL URLWithString:[jsHelperResult valueForKey:@"url"]];
|
||||||
|
} else if ([self.typeIdentifier isEqualToString:@"public.url"] == YES) {
|
||||||
|
return self.payload;
|
||||||
|
} if ([self.typeIdentifier isEqualToString:@"public.plain-text"] == YES) {
|
||||||
|
return [NSURL URLWithString:self.payload];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// HPSharedItemsContainer.h
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class HPSharedItem;
|
||||||
|
|
||||||
|
@interface HPSharedItemsContainer : NSObject
|
||||||
|
|
||||||
|
@property (nullable) HPSharedItem *primaryItem;
|
||||||
|
@property NSMutableArray<HPSharedItem *> *candidateItems;
|
||||||
|
|
||||||
|
-(NSURL *)resolveURL;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// HPSharedItemsContainer.m
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 27/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "HPSharedItemsContainer.h"
|
||||||
|
|
||||||
|
#import "HPSharedItem.h"
|
||||||
|
#import "NSURL+HotPocketExtensions.h"
|
||||||
|
|
||||||
|
@implementation HPSharedItemsContainer (HPSharedItemsContainerPrivate)
|
||||||
|
|
||||||
|
-(NSURL *)validatedURL:(NSURL *)url {
|
||||||
|
if (url.isUsableInHotPocket == NO) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HPSharedItemsContainer
|
||||||
|
|
||||||
|
-(instancetype)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.primaryItem = nil;
|
||||||
|
self.candidateItems = [[NSMutableArray alloc] initWithCapacity:1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSURL *)resolveURL {
|
||||||
|
NSURL *result = nil;
|
||||||
|
|
||||||
|
if (self.primaryItem != nil) {
|
||||||
|
result = [self validatedURL:[self.primaryItem maybeURL]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self.candidateItems count] > 0) {
|
||||||
|
NSUInteger itemCandidateIndex = 0;
|
||||||
|
while (result == nil) {
|
||||||
|
HPSharedItem *itemCandidate = [self.candidateItems objectAtIndex:itemCandidateIndex];
|
||||||
|
|
||||||
|
result = [self validatedURL:itemCandidate.maybeURL];
|
||||||
|
if (result != nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemCandidateIndex += 1;
|
||||||
|
if (itemCandidateIndex >= [self.candidateItems count]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class HPAuthFlow;
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
|
@property (strong, nonnull) HPAuthFlow *authFlow;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,14 +7,17 @@
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
// Override point for customization after application launch.
|
self.authFlow = [[HPAuthFlow alloc] init];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
|
-(UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
|
||||||
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
|
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// AuthorizationProgressViewController.h
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface AuthorizationProgressViewController : UIViewController
|
||||||
|
|
||||||
|
@property IBOutlet UIActivityIndicatorView *progressIndicator;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// AuthorizationProgressViewController.m
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AuthorizationProgressViewController.h"
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
|
||||||
|
@interface AuthorizationProgressViewController (AuthorizationProgressViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AuthorizationProgressViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear:(BOOL)animated {
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
[self.progressIndicator startAnimating];
|
||||||
|
|
||||||
|
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAuthFlowDidFinish:) name:@"AuthFlowDidFinish" object:appDelegate.authFlow];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillDisappear:(BOOL)animated {
|
||||||
|
[super viewWillDisappear:animated];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidDisappear:(BOOL)animated {
|
||||||
|
[super viewDidDisappear:animated];
|
||||||
|
[self.progressIndicator stopAnimating];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Notification handlers
|
||||||
|
|
||||||
|
-(void)onAuthFlowDidFinish:(NSNotification *)notification {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[AuthorizationViewController onAuthFlowDidFinish:] notification=`%@`", notification);
|
||||||
|
#endif
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
|
||||||
|
if (credentials.usable == NO) {
|
||||||
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Oops!", @"Oops!")
|
||||||
|
message:NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.")
|
||||||
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
|
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Oh well", @"Oh well")
|
||||||
|
style:UIAlertActionStyleDefault
|
||||||
|
handler:^(UIAlertAction *action) {
|
||||||
|
[alert dismissViewControllerAnimated:YES completion:^{
|
||||||
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
|
}];
|
||||||
|
}]];
|
||||||
|
|
||||||
|
[self presentViewController:alert animated:YES completion:nil];
|
||||||
|
} else {
|
||||||
|
[self.navigationController popToRootViewControllerAnimated:YES];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
22
services/apple/iOS (App)/AuthorizationViewController.h
Normal file
22
services/apple/iOS (App)/AuthorizationViewController.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// AuthorizationViewController.h
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface AuthorizationViewController : UIViewController
|
||||||
|
|
||||||
|
@property UIImageView *invalidURLWarningView;
|
||||||
|
|
||||||
|
@property IBOutlet UITextField *instanceURLField;
|
||||||
|
|
||||||
|
-(IBAction)doStartAuthorizationFlow:(id)sender;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
105
services/apple/iOS (App)/AuthorizationViewController.m
Normal file
105
services/apple/iOS (App)/AuthorizationViewController.m
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// AuthorizationViewController.m
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AuthorizationViewController.h"
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "AuthorizationProgressViewController.h"
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "MainViewController.h"
|
||||||
|
#import "NSURL+HotPocketExtensions.h"
|
||||||
|
|
||||||
|
@interface AuthorizationViewController (AuthorizationViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AuthorizationViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
self.invalidURLWarningView = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"exclamationmark.circle.fill"]];
|
||||||
|
self.invalidURLWarningView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
self.invalidURLWarningView.frame = CGRectMake(0, 0, 16, 16);
|
||||||
|
self.invalidURLWarningView.tintColor = [UIColor colorNamed:@"WarningColor"];
|
||||||
|
[self.view addSubview:self.invalidURLWarningView];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear:(BOOL)animated {
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
self.instanceURLField.rightView = self.invalidURLWarningView;
|
||||||
|
self.instanceURLField.rightViewMode = UITextFieldViewModeNever;
|
||||||
|
|
||||||
|
[self.instanceURLField addTarget:self action:@selector(onInstanceURLFieldChanged:) forControlEvents:UIControlEventEditingChanged];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)doStartAuthorizationFlow:(id)sender {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[AuthorizationViewController doStartAuthorizationFlow:] instanceURL=`%@`", self.instanceURLField.text);
|
||||||
|
#endif
|
||||||
|
if (!self.instanceURLField.text) {
|
||||||
|
// ???
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *instanceURL = [NSURL URLWithString:self.instanceURLField.text];
|
||||||
|
if (instanceURL.isUsableInHotPocket == NO) {
|
||||||
|
// ???
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIApplication *application = [UIApplication sharedApplication];
|
||||||
|
AppDelegate *appDeleate = [application delegate];
|
||||||
|
appDeleate.authFlow.baseURL = instanceURL;
|
||||||
|
|
||||||
|
NSURL *authURL = [appDeleate.authFlow start];
|
||||||
|
if (authURL == nil) {
|
||||||
|
// ???
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([application canOpenURL:authURL] == YES) {
|
||||||
|
[application openURL:authURL options:@{} completionHandler:^(BOOL result) {
|
||||||
|
if (result == YES) {
|
||||||
|
AuthorizationProgressViewController *authorizationProgressViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"AuthorizationProgressViewController"];
|
||||||
|
[self.navigationController pushViewController:authorizationProgressViewController animated:YES];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Event handlers
|
||||||
|
|
||||||
|
-(void)onInstanceURLFieldChanged:(UITextField *)sender {
|
||||||
|
sender.rightViewMode = UITextFieldViewModeNever;
|
||||||
|
|
||||||
|
if (!sender.text || [@"" isEqualToString:sender.text]) {
|
||||||
|
sender.rightViewMode = UITextFieldViewModeAlways;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *url = [NSURL URLWithString:sender.text];
|
||||||
|
if (url == nil) {
|
||||||
|
sender.rightViewMode = UITextFieldViewModeAlways;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.isUsableInHotPocket == NO) {
|
||||||
|
sender.rightViewMode = UITextFieldViewModeAlways;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||||
<capability name="Image references" minToolsVersion="12.0"/>
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
@ -16,12 +16,25 @@
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="H2q-Qq-Nf1">
|
||||||
|
<rect key="frame" x="16" y="344" width="361" height="165"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6HG-Um-bch">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6HG-Um-bch">
|
||||||
<rect key="frame" x="131" y="363" width="128" height="128"/>
|
<rect key="frame" x="116" y="0.0" width="128" height="128"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<imageReference key="image" image="LargeIcon"/>
|
<imageReference key="image" image="icon-mac-384.png"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket by BTHLabs" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="v6f-dw-6WO">
|
||||||
|
<rect key="frame" x="0.0" y="136" width="361" height="29"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" name="BackgroundColor"/>
|
<color key="backgroundColor" name="BackgroundColor"/>
|
||||||
|
@ -29,11 +42,11 @@
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
<point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="LargeIcon" width="128" height="128"/>
|
<image name="icon-mac-384.png" width="384" height="384"/>
|
||||||
<namedColor name="BackgroundColor">
|
<namedColor name="BackgroundColor">
|
||||||
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
|
|
|
@ -1,46 +1,245 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7Sa-RR-xgc">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||||
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--Main View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="MainViewController" id="BYZ-38-t0r" customClass="MainViewController" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<wkWebView contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RDB-ib-igF">
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tdb-RK-EKV">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="20" y="96" width="374" height="165"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
<color key="backgroundColor" name="BackgroundColor"/>
|
<subviews>
|
||||||
<wkWebViewConfiguration key="configuration">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="57V-kg-4Nx">
|
||||||
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
|
<rect key="frame" x="122" y="0.0" width="128" height="128"/>
|
||||||
<wkPreferences key="preferences"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
</wkWebViewConfiguration>
|
<imageReference key="image" image="icon-mac-384.png"/>
|
||||||
</wkWebView>
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket by BTHLabs" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cP6-uT-Hh4">
|
||||||
|
<rect key="frame" x="0.0" y="136" width="374" height="29"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket is configured and ready." textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ltc-Em-W9y" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="20" y="277" width="374" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Instance URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GYw-2b-qGS">
|
||||||
|
<rect key="frame" x="20" y="349" width="374" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OPO-AY-zgd">
|
||||||
|
<rect key="frame" x="20" y="378" width="374" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||||
|
<buttonConfiguration key="configuration" style="gray" title="DO NOT LOCALIZE">
|
||||||
|
<color key="baseForegroundColor" name="SecondaryColor"/>
|
||||||
|
</buttonConfiguration>
|
||||||
|
<connections>
|
||||||
|
<action selector="doOpenInstanceURL:" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="5SW-3o-wiJ"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wQZ-n6-b0o">
|
||||||
|
<rect key="frame" x="161" y="431" width="92" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<buttonConfiguration key="configuration" style="gray" title="Log out">
|
||||||
|
<color key="baseForegroundColor" name="DangerColor"/>
|
||||||
|
</buttonConfiguration>
|
||||||
|
<connections>
|
||||||
|
<action selector="doLogOut:" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="iq7-wK-GMu"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" name="BackgroundColor"/>
|
<color key="backgroundColor" name="BackgroundColor"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
</view>
|
</view>
|
||||||
|
<navigationItem key="navigationItem" id="w8s-f0-7E0"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="webView" destination="RDB-ib-igF" id="avx-RC-qRB"/>
|
<outlet property="instanceURLButton" destination="OPO-AY-zgd" id="1Wr-H9-eZ6"/>
|
||||||
|
<outlet property="logoutButton" destination="wQZ-n6-b0o" id="vco-vP-zvy"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
<point key="canvasLocation" x="962.31884057971024" y="375"/>
|
||||||
|
</scene>
|
||||||
|
<!--Authorization View Controller-->
|
||||||
|
<scene sceneID="zfn-5m-i4Y">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="AuthorizationViewController" id="1Il-xJ-X5Y" customClass="AuthorizationViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="gKn-cL-a2b">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kkk-cr-Leu">
|
||||||
|
<rect key="frame" x="20" y="96" width="374" height="165"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YXT-lU-mHV">
|
||||||
|
<rect key="frame" x="122" y="0.0" width="128" height="128"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<imageReference key="image" image="icon-mac-384.png"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket by BTHLabs" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hhk-23-s8H">
|
||||||
|
<rect key="frame" x="0.0" y="136" width="374" height="29"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Instance URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="alG-Ve-nxN">
|
||||||
|
<rect key="frame" x="20" y="277" width="374" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="v5s-Uh-qWU" customClass="InstanceURLField">
|
||||||
|
<rect key="frame" x="20" y="306" width="374" height="34"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" returnKeyType="go" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="url"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doStartAuthorizationFlow:" destination="1Il-xJ-X5Y" eventType="primaryActionTriggered" id="Rd9-1f-N6Z"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Enter the URL to your HotPocket instance, e.g. https://my.hotpocket.app" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tn1-fl-daL" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="20" y="348" width="374" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eKt-N1-DEJ">
|
||||||
|
<rect key="frame" x="20" y="428" width="374" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<state key="normal" title="Button"/>
|
||||||
|
<buttonConfiguration key="configuration" style="filled" title="Continue"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doStartAuthorizationFlow:" destination="1Il-xJ-X5Y" eventType="primaryActionTriggered" id="U0V-Pp-M2x"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="dL2-4T-yXY"/>
|
||||||
|
<color key="backgroundColor" name="BackgroundColor"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="instanceURLField" destination="v5s-Uh-qWU" id="hRQ-r8-3Dz"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="m6b-Bm-Ty7" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1726.0869565217392" y="375"/>
|
||||||
|
</scene>
|
||||||
|
<!--Navigation Controller-->
|
||||||
|
<scene sceneID="zFJ-kU-27j">
|
||||||
|
<objects>
|
||||||
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="7Sa-RR-xgc" sceneMemberID="viewController">
|
||||||
|
<toolbarItems/>
|
||||||
|
<navigationBar key="navigationBar" contentMode="scaleToFill" id="PrZ-Cz-0b5">
|
||||||
|
<rect key="frame" x="0.0" y="96" width="414" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</navigationBar>
|
||||||
|
<nil name="viewControllers"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="7mY-Zh-QsC"/>
|
||||||
|
</connections>
|
||||||
|
</navigationController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="GIS-z2-loC" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="52.173913043478265" y="375"/>
|
||||||
|
</scene>
|
||||||
|
<!--Authorization Progress View Controller-->
|
||||||
|
<scene sceneID="689-0y-Gyr">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="AuthorizationProgressViewController" id="aiy-3v-nI7" customClass="AuthorizationProgressViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="ljp-b5-lta">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wKr-WU-Iec">
|
||||||
|
<rect key="frame" x="20" y="96" width="374" height="165"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="v6u-sE-tzJ">
|
||||||
|
<rect key="frame" x="122" y="0.0" width="128" height="128"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<imageReference key="image" image="icon-mac-384.png"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket by BTHLabs" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="50v-cp-DGd">
|
||||||
|
<rect key="frame" x="0.0" y="136" width="374" height="29"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="DNy-gf-n60">
|
||||||
|
<rect key="frame" x="189" y="306" width="37" height="37"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
</activityIndicatorView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Awaiting authentication response..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qiJ-yx-nMd">
|
||||||
|
<rect key="frame" x="20" y="359" width="374" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="zyd-wv-1rn"/>
|
||||||
|
<color key="backgroundColor" name="BackgroundColor"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="progressIndicator" destination="DNy-gf-n60" id="hJF-jc-ZJ0"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="N3D-cM-5Ro" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="2532" y="375"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
<resources>
|
<resources>
|
||||||
|
<image name="icon-mac-384.png" width="384" height="384"/>
|
||||||
|
<namedColor name="AccentColor">
|
||||||
|
<color red="0.10980392156862745" green="0.72941176470588232" blue="0.92941176470588238" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
<namedColor name="BackgroundColor">
|
<namedColor name="BackgroundColor">
|
||||||
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
|
<namedColor name="DangerColor">
|
||||||
|
<color red="0.93300002813339233" green="0.3919999897480011" blue="0.46299999952316284" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="SecondaryColor">
|
||||||
|
<color red="0.0" green="0.50980392156862742" blue="0.8666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|
15
services/apple/iOS (App)/HotPocket (iOS).entitlements
Normal file
15
services/apple/iOS (App)/HotPocket (iOS).entitlements
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?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.pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocketShared</string>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -2,6 +2,32 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>CFBundleURLIconFile</key>
|
||||||
|
<string>icon-mac-384</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>HotPocketDesktopMac</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>hotpocket-mobile</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>HPAuthFlowPostAuthenticateURLParts</key>
|
||||||
|
<dict>
|
||||||
|
<key>host</key>
|
||||||
|
<string>post-authenticate</string>
|
||||||
|
<key>scheme</key>
|
||||||
|
<string>hotpocket-mobile</string>
|
||||||
|
</dict>
|
||||||
|
<key>HPAuthFlowSource</key>
|
||||||
|
<string>HotPocketMobile</string>
|
||||||
|
<key>HPRPCClientOrigin</key>
|
||||||
|
<string>hotpocket-mobile://HPRPCClient</string>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
|
16
services/apple/iOS (App)/InstanceURLField.h
Normal file
16
services/apple/iOS (App)/InstanceURLField.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// InstanceURLField.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface InstanceURLField : UITextField
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
21
services/apple/iOS (App)/InstanceURLField.m
Normal file
21
services/apple/iOS (App)/InstanceURLField.m
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// InstanceURLField.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "InstanceURLField.h"
|
||||||
|
|
||||||
|
@implementation InstanceURLField
|
||||||
|
|
||||||
|
-(CGRect)rightViewRectForBounds:(CGRect)bounds {
|
||||||
|
if (self.rightViewMode != UITextFieldViewModeNever) {
|
||||||
|
CGFloat offsetTop = (bounds.size.height - 16.0) / 2.0;
|
||||||
|
return CGRectMake(bounds.size.width - 16.0 - offsetTop, offsetTop, 16.0, 16.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CGRectNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
22
services/apple/iOS (App)/MainViewController.h
Normal file
22
services/apple/iOS (App)/MainViewController.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// MainViewController.h
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface MainViewController : UIViewController
|
||||||
|
|
||||||
|
@property IBOutlet UIButton *instanceURLButton;
|
||||||
|
@property IBOutlet UIButton *logoutButton;
|
||||||
|
|
||||||
|
-(IBAction)doOpenInstanceURL:(id)sender;
|
||||||
|
-(IBAction)doLogOut:(id)sender;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
77
services/apple/iOS (App)/MainViewController.m
Normal file
77
services/apple/iOS (App)/MainViewController.m
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// MainViewController.m
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MainViewController.h"
|
||||||
|
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
|
||||||
|
#import "AuthorizationViewController.h"
|
||||||
|
|
||||||
|
@interface MainViewController (MainViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MainViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
[self.instanceURLButton setTitle:@"" forState:UIControlStateNormal];
|
||||||
|
self.instanceURLButton.enabled = NO;
|
||||||
|
|
||||||
|
self.logoutButton.enabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear:(BOOL)animated {
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
[self.navigationController setNavigationBarHidden:YES animated:NO];
|
||||||
|
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
if (credentials.usable == NO) {
|
||||||
|
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"AuthorizationViewController"];
|
||||||
|
[self.navigationController pushViewController:authorizationViewController animated:NO];
|
||||||
|
} else {
|
||||||
|
[self.instanceURLButton setTitle:credentials.baseURL forState:UIControlStateNormal];
|
||||||
|
self.instanceURLButton.enabled = YES;
|
||||||
|
|
||||||
|
self.logoutButton.enabled = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *instanceURLText = @"";
|
||||||
|
if (credentials.baseURL != nil) {
|
||||||
|
instanceURLText = credentials.baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.instanceURLButton.titleLabel.text = instanceURLText;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)doOpenInstanceURL:(id)sender {
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
if (credentials.usable == YES) {
|
||||||
|
NSURL *instanceURL = [NSURL URLWithString:credentials.baseURL];
|
||||||
|
|
||||||
|
UIApplication *application = [UIApplication sharedApplication];
|
||||||
|
if ([application canOpenURL:instanceURL] == YES) {
|
||||||
|
[application openURL:instanceURL options:@{} completionHandler:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(IBAction)doLogOut:(id)sender {
|
||||||
|
[[HPCredentialsHelper sharedHelper] clearCredentials];
|
||||||
|
|
||||||
|
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"AuthorizationViewController"];
|
||||||
|
[self.navigationController pushViewController:authorizationViewController animated:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
16
services/apple/iOS (App)/MultilineLabel.h
Normal file
16
services/apple/iOS (App)/MultilineLabel.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// MultilineLabel.h
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface MultilineLabel : UILabel
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
24
services/apple/iOS (App)/MultilineLabel.m
Normal file
24
services/apple/iOS (App)/MultilineLabel.m
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// MultilineLabel.m
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MultilineLabel.h"
|
||||||
|
|
||||||
|
@implementation MultilineLabel
|
||||||
|
|
||||||
|
-(void)drawTextInRect:(CGRect)rect {
|
||||||
|
if (!self.text) {
|
||||||
|
[super drawTextInRect:rect];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGSize textSize = [self sizeThatFits:CGSizeMake(rect.size.width, CGFLOAT_MAX)];
|
||||||
|
CGRect textRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, MIN(rect.size.height, textSize.height));
|
||||||
|
|
||||||
|
[super drawTextInRect:textRect];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -7,6 +7,24 @@
|
||||||
|
|
||||||
#import "SceneDelegate.h"
|
#import "SceneDelegate.h"
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
|
||||||
@implementation SceneDelegate
|
@implementation SceneDelegate
|
||||||
|
|
||||||
|
-(void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
|
||||||
|
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
|
||||||
|
|
||||||
|
for (UIOpenURLContext *context in URLContexts) {
|
||||||
|
NSURL *url = context.URL;
|
||||||
|
HPAuthParams *receivedAuthParams = [appDelegate.authFlow handlePostAuthenticateURL:url];
|
||||||
|
|
||||||
|
if (receivedAuthParams == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[appDelegate.authFlow handleAuthParams:receivedAuthParams];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
|
||||||
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||||
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Share View Controller-->
|
||||||
|
<scene sceneID="ceB-am-kn3">
|
||||||
|
<objects>
|
||||||
|
<viewController id="j1y-V4-xli" customClass="ShareViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oWQ-SX-fOF">
|
||||||
|
<rect key="frame" x="20" y="118" width="353" height="165"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OAy-89-4VU">
|
||||||
|
<rect key="frame" x="111" y="0.0" width="127" height="128"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<imageReference key="image" image="icon-mac-384.png"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket by BTHLabs" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ern-ld-Ivg">
|
||||||
|
<rect key="frame" x="-2" y="136" width="355" height="29"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zdu-vr-R6t" userLabel="Saving View">
|
||||||
|
<rect key="frame" x="20" y="299" width="353" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="1HO-hL-WcQ">
|
||||||
|
<rect key="frame" x="158" y="6" width="37" height="37"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
</activityIndicatorView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CqC-1V-R49">
|
||||||
|
<rect key="frame" x="138" y="107" width="77" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<state key="normal" title="Button"/>
|
||||||
|
<buttonConfiguration key="configuration" style="gray" title="Cancel">
|
||||||
|
<color key="baseForegroundColor" name="DangerColor"/>
|
||||||
|
</buttonConfiguration>
|
||||||
|
<connections>
|
||||||
|
<action selector="doCancel:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="EvP-s3-QOt"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Dr-s7-Kqv" userLabel="Done View">
|
||||||
|
<rect key="frame" x="20" y="299" width="353" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="checkmark.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="0oo-rV-Vfd">
|
||||||
|
<rect key="frame" x="152" y="0.0" width="48" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="SuccessColor"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Your link has been saved!" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QoY-OC-nmX" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="0.0" y="57" width="353" height="42"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AHS-eb-Mk8">
|
||||||
|
<rect key="frame" x="143" y="107" width="67" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
|
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||||
|
<state key="normal" title="Close"/>
|
||||||
|
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doClose:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="6GF-4h-9aj"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DLn-U6-UcA" userLabel="Error View">
|
||||||
|
<rect key="frame" x="20" y="299" width="353" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="multiply.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="HUo-6S-xfQ">
|
||||||
|
<rect key="frame" x="152" y="0.0" width="48" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="DangerColor"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HotPocket couldn't complete this operation." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FFg-7U-S0i" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="0.0" y="57" width="353" height="42"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gFP-1G-Hef">
|
||||||
|
<rect key="frame" x="143" y="107" width="67" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
|
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||||
|
<state key="normal" title="Close"/>
|
||||||
|
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doClose:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="Z85-xF-RxD"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eqK-cv-2mf" userLabel="Needs Setup View">
|
||||||
|
<rect key="frame" x="20" y="299" width="353" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="exclamationmark.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="fpS-az-ps2">
|
||||||
|
<rect key="frame" x="152" y="0.0" width="48" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="WarningColor"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Open the HotPocket App to set it up." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xjE-hq-bDA" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="0.0" y="57" width="353" height="42"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UJs-K5-SJe">
|
||||||
|
<rect key="frame" x="143" y="107" width="67" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
|
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||||
|
<state key="normal" title="Close"/>
|
||||||
|
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doClose:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="40D-av-JQe"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" id="ckY-6z-0fe" userLabel="Unprocessable Entity VIew">
|
||||||
|
<rect key="frame" x="20" y="299" width="353" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="exclamationmark.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="tMs-QS-o9E">
|
||||||
|
<rect key="frame" x="152" y="0.0" width="48" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="WarningColor"/>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="This item couldn't be shared :(." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tz8-Hp-Vha" customClass="MultilineLabel">
|
||||||
|
<rect key="frame" x="20" y="57" width="313" height="42"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0WL-Hk-6Bk">
|
||||||
|
<rect key="frame" x="143" y="107" width="67" height="35"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<color key="tintColor" name="AccentColor"/>
|
||||||
|
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||||
|
<state key="normal" title="Close"/>
|
||||||
|
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doClose:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="9pE-y5-xBp"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="GQa-Md-2MI"/>
|
||||||
|
</view>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xfn-uj-KNk" userLabel="uname Label">
|
||||||
|
<rect key="frame" x="20" y="811" width="353" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||||
|
<color key="backgroundColor" name="BackgroundColor"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="doneView" destination="9Dr-s7-Kqv" id="Ote-sW-Adm"/>
|
||||||
|
<outlet property="errorView" destination="DLn-U6-UcA" id="cl1-I0-nY1"/>
|
||||||
|
<outlet property="needsSetupView" destination="eqK-cv-2mf" id="Vb3-Y3-8Y8"/>
|
||||||
|
<outlet property="progressIndicator" destination="1HO-hL-WcQ" id="1Qd-Gt-b3G"/>
|
||||||
|
<outlet property="savingView" destination="zdu-vr-R6t" id="svY-bA-Z9d"/>
|
||||||
|
<outlet property="unameLabel" destination="xfn-uj-KNk" id="TTp-PV-ttr"/>
|
||||||
|
<outlet property="unprocessableEntityView" destination="ckY-6z-0fe" id="owN-qJ-oBj"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="138.1679389312977" y="130.98591549295776"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="checkmark.circle.fill" catalog="system" width="128" height="123"/>
|
||||||
|
<image name="exclamationmark.circle.fill" catalog="system" width="128" height="123"/>
|
||||||
|
<image name="icon-mac-384.png" width="384" height="384"/>
|
||||||
|
<image name="multiply.circle.fill" catalog="system" width="128" height="123"/>
|
||||||
|
<namedColor name="AccentColor">
|
||||||
|
<color red="0.10980392156862745" green="0.72941176470588232" blue="0.92941176470588238" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="BackgroundColor">
|
||||||
|
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="DangerColor">
|
||||||
|
<color red="0.93300002813339233" green="0.3919999897480011" blue="0.46299999952316284" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="SuccessColor">
|
||||||
|
<color red="0.054901960784313725" green="0.65490196078431373" blue="0.40392156862745099" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="WarningColor">
|
||||||
|
<color red="0.94509803921568625" green="0.58823529411764708" blue="0.1803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
</resources>
|
||||||
|
</document>
|
27
services/apple/iOS (Share Extension)/Info.plist
Normal file
27
services/apple/iOS (Share Extension)/Info.plist
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?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>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionJavaScriptPreprocessingFile</key>
|
||||||
|
<string>ShareExtensionHelper</string>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
20
services/apple/iOS (Share Extension)/ShareExtensionHelper.js
Normal file
20
services/apple/iOS (Share Extension)/ShareExtensionHelper.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// ShareExtensionHelper.js
|
||||||
|
// HotPocket
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 26/09/2025.
|
||||||
|
//
|
||||||
|
var ShareExtensionHelper = function() {
|
||||||
|
// OMG I CAN'T BELIEVE I HAVE TO EMBED JS IN THE SHARE EXTENSION :D
|
||||||
|
};
|
||||||
|
|
||||||
|
ShareExtensionHelper.prototype = {
|
||||||
|
run: function(arguments) {
|
||||||
|
arguments.completionFunction({
|
||||||
|
'iHateComputers': true,
|
||||||
|
'url': document.location.href,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var ExtensionPreprocessingJS = new ShareExtensionHelper();
|
27
services/apple/iOS (Share Extension)/ShareViewController.h
Normal file
27
services/apple/iOS (Share Extension)/ShareViewController.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// ShareViewController.h
|
||||||
|
// iOS (Share Extension)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class HPAPI;
|
||||||
|
|
||||||
|
@interface ShareViewController : UIViewController
|
||||||
|
|
||||||
|
@property HPAPI *api;
|
||||||
|
|
||||||
|
@property IBOutlet UIActivityIndicatorView *progressIndicator;
|
||||||
|
@property IBOutlet UIView *savingView;
|
||||||
|
@property IBOutlet UIView *needsSetupView;
|
||||||
|
@property IBOutlet UIView *doneView;
|
||||||
|
@property IBOutlet UIView *errorView;
|
||||||
|
@property IBOutlet UIView *unprocessableEntityView;
|
||||||
|
@property IBOutlet UILabel *unameLabel;
|
||||||
|
|
||||||
|
-(IBAction)doCancel:(id)sender;
|
||||||
|
-(IBAction)doClose:(id)sender;
|
||||||
|
|
||||||
|
@end
|
126
services/apple/iOS (Share Extension)/ShareViewController.m
Normal file
126
services/apple/iOS (Share Extension)/ShareViewController.m
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// ShareViewController.m
|
||||||
|
// iOS (Share Extension)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 25/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||||
|
|
||||||
|
#import "ShareViewController.h"
|
||||||
|
|
||||||
|
#import "HPAPI.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "HPShareExtensionHelper.h"
|
||||||
|
|
||||||
|
@implementation ShareViewController (ShareViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
-(void)saveURL:(NSURL *)url {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController save:] url=`%@`", url);
|
||||||
|
#endif
|
||||||
|
BOOL callResult = [self.api save:url completionHandler:^(NSString * _Nullable callId, HPRPCCallResult * _Nullable result) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
self.savingView.hidden = YES;
|
||||||
|
|
||||||
|
if (result.error != nil) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController resolveLinkAndSave] saveError=`%@`", result.error);
|
||||||
|
#endif
|
||||||
|
self.errorView.hidden = NO;
|
||||||
|
} else {
|
||||||
|
self.doneView.hidden = NO;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (callResult == NO) {
|
||||||
|
self.savingView.hidden = YES;
|
||||||
|
self.errorView.hidden = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)resolveLinkAndSave {
|
||||||
|
HPShareExtensionHelper *helper = [[HPShareExtensionHelper alloc] initWithContext:self.extensionContext];
|
||||||
|
[helper processItems:^(NSURL *url) {
|
||||||
|
if (url == nil) {
|
||||||
|
self.savingView.hidden = YES;
|
||||||
|
self.unprocessableEntityView.hidden = NO;
|
||||||
|
} else {
|
||||||
|
[self saveURL:url];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ShareViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.savingView.hidden = NO;
|
||||||
|
self.needsSetupView.hidden = YES;
|
||||||
|
self.doneView.hidden = YES;
|
||||||
|
self.errorView.hidden = YES;
|
||||||
|
self.unprocessableEntityView.hidden = YES;
|
||||||
|
|
||||||
|
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||||
|
self.unameLabel.text = [NSString stringWithFormat:@"HotPocket v%@ (%@)", [mainBundle.infoDictionary valueForKey:@"CFBundleShortVersionString"], [mainBundle.infoDictionary valueForKey:@"CFBundleVersion"]];
|
||||||
|
|
||||||
|
self.api = [[HPAPI alloc] init];
|
||||||
|
if (self.api.rpcClient.hasCredentials == YES) {
|
||||||
|
self.savingView.hidden = NO;
|
||||||
|
self.needsSetupView.hidden = YES;
|
||||||
|
} else {
|
||||||
|
self.savingView.hidden = YES;
|
||||||
|
self.needsSetupView.hidden = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear:(BOOL)animated {
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
[self.progressIndicator startAnimating];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillDisappear:(BOOL)animated {
|
||||||
|
[super viewWillDisappear:animated];
|
||||||
|
[self.progressIndicator stopAnimating];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidAppear:(BOOL)animated {
|
||||||
|
[super viewDidAppear:animated];
|
||||||
|
|
||||||
|
[self.api checkAuth:^(BOOL authValid, NSError *error, NSString *callId) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (authValid == NO) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController viewDidAppear:] checkAuthError=`%@`", error);
|
||||||
|
#endif
|
||||||
|
self.savingView.hidden = YES;
|
||||||
|
self.needsSetupView.hidden = NO;
|
||||||
|
} else {
|
||||||
|
[self resolveLinkAndSave];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)doCancel:(id)sender {
|
||||||
|
NSError *cancelError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];
|
||||||
|
[self.extensionContext cancelRequestWithError:cancelError];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(IBAction)doClose:(id)sender {
|
||||||
|
NSExtensionItem *outputItem = [[NSExtensionItem alloc] init];
|
||||||
|
|
||||||
|
NSArray *outputItems = @[outputItem];
|
||||||
|
[self.extensionContext completeRequestReturningItems:outputItems completionHandler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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.pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocketShared</string>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocket.ShareExtension</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -3,3 +3,4 @@ run:
|
||||||
pty: true
|
pty: true
|
||||||
files_to_version:
|
files_to_version:
|
||||||
- "HotPocket.xcodeproj/project.pbxproj"
|
- "HotPocket.xcodeproj/project.pbxproj"
|
||||||
|
- "pyproject.toml"
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@class HPAuthFlow;
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
|
||||||
|
@property (strong, nonnull) HPAuthFlow *authFlow;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,14 +7,32 @@
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
-(void)applicationDidFinishLaunching:(NSNotification *)notification {
|
||||||
// Override point for customization after application launch.
|
self.authFlow = [[HPAuthFlow alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
|
||||||
|
HPAuthParams *receivedAuthParams = nil;
|
||||||
|
for (NSURL *url in urls) {
|
||||||
|
receivedAuthParams = [self.authFlow handlePostAuthenticateURL:url];
|
||||||
|
|
||||||
|
if (receivedAuthParams != nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivedAuthParams != nil) {
|
||||||
|
[self.authFlow handleAuthParams:receivedAuthParams];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// AuthorizationProgressViewController.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface AuthorizationProgressViewController : NSViewController
|
||||||
|
|
||||||
|
@property IBOutlet NSProgressIndicator *progressIndicator;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// AuthorizationProgressViewController.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AuthorizationProgressViewController.h"
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "AuthorizationViewController.h"
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "MainViewController.h"
|
||||||
|
#import "ReplaceAnimator.h"
|
||||||
|
|
||||||
|
@interface AuthorizationProgressViewController (AuthorizationProgressViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AuthorizationProgressViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear {
|
||||||
|
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAuthFlowDidFinish:) name:@"AuthFlowDidFinish" object:appDelegate.authFlow];
|
||||||
|
|
||||||
|
[self.progressIndicator startAnimation:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidDisappear {
|
||||||
|
[self.progressIndicator stopAnimation:self];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Notification handlers
|
||||||
|
|
||||||
|
-(void)onAuthFlowDidFinish:(NSNotification *)notification {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
|
||||||
|
[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
|
||||||
|
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
|
||||||
|
|
||||||
|
if (credentials.usable == NO) {
|
||||||
|
NSAlert *alert = [[NSAlert alloc] init];
|
||||||
|
alert.alertStyle = NSAlertStyleCritical;
|
||||||
|
alert.messageText = NSLocalizedString(@"Oops!", @"Oops!");
|
||||||
|
alert.informativeText = NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.");
|
||||||
|
[alert runModal];
|
||||||
|
|
||||||
|
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
|
||||||
|
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
|
||||||
|
} else {
|
||||||
|
MainViewController *mainViewController = [self.storyboard instantiateControllerWithIdentifier:@"MainViewController"];
|
||||||
|
[self presentViewController:mainViewController animator:[[ReplaceAnimator alloc] init]];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
21
services/apple/macOS (App)/AuthorizationViewController.h
Normal file
21
services/apple/macOS (App)/AuthorizationViewController.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// AuthorizationViewController.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface AuthorizationViewController : NSViewController
|
||||||
|
|
||||||
|
@property (nullable) NSString *baseURL;
|
||||||
|
@property (nullable) NSString *authorizationSessionToken;
|
||||||
|
|
||||||
|
-(IBAction)doStartAuthorizationFlow:(id)sender;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
49
services/apple/macOS (App)/AuthorizationViewController.m
Normal file
49
services/apple/macOS (App)/AuthorizationViewController.m
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// AuthorizationViewController.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AuthorizationViewController.h"
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "HPAuthFlow.h"
|
||||||
|
#import "AuthorizationProgressViewController.h"
|
||||||
|
#import "ReplaceAnimator.h"
|
||||||
|
|
||||||
|
@interface AuthorizationViewController (AuthorizationViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AuthorizationViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.baseURL = nil;
|
||||||
|
self.authorizationSessionToken = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)doStartAuthorizationFlow:(id)sender {
|
||||||
|
AppDelegate *appDeleate = [[NSApplication sharedApplication] delegate];
|
||||||
|
appDeleate.authFlow.baseURL = [NSURL URLWithString:self.baseURL];
|
||||||
|
|
||||||
|
NSURL *authURL = [appDeleate.authFlow start];
|
||||||
|
if (authURL == nil) {
|
||||||
|
NSBeep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthorizationProgressViewController *authProgressViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationProgressViewController"];
|
||||||
|
[self presentViewController:authProgressViewController animator:[[ReplaceAnimator alloc] init]];
|
||||||
|
|
||||||
|
[[NSWorkspace sharedWorkspace] openURL:authURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24127"/>
|
||||||
<plugIn identifier="com.apple.WebKit2IBPlugin" version="23727"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
<scene sceneID="R2V-B0-nI4">
|
<scene sceneID="R2V-B0-nI4">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" sceneMemberID="viewController">
|
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" sceneMemberID="viewController">
|
||||||
<window key="window" title="HotPocket" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" animationBehavior="default" id="IQv-IB-iLA">
|
<window key="window" title="HotPocket" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" animationBehavior="default" toolbarStyle="expanded" id="IQv-IB-iLA">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenNone="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenNone="YES"/>
|
||||||
<rect key="contentRect" x="196" y="240" width="425" height="325"/>
|
<rect key="contentRect" x="196" y="240" width="425" height="325"/>
|
||||||
|
@ -87,38 +87,216 @@
|
||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
|
<segue destination="r5D-xE-cNT" kind="relationship" relationship="window.shadowedContentViewController" id="j8b-cd-GSP"/>
|
||||||
</connections>
|
</connections>
|
||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="75" y="250"/>
|
<point key="canvasLocation" x="75" y="250"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--View Controller-->
|
<!--Authorization View Controller-->
|
||||||
<scene sceneID="hIz-AP-VOD">
|
<scene sceneID="hIz-AP-VOD">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="XfG-lQ-9wD" customClass="ViewController" sceneMemberID="viewController">
|
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
<view key="view" appearanceType="darkAqua" id="m2S-Jp-Qdl">
|
<viewController storyboardIdentifier="AuthorizationViewController" id="XfG-lQ-9wD" customClass="AuthorizationViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="m2S-Jp-Qdl">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
|
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<wkWebView wantsLayer="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eOr-cG-IQY">
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7sM-F3-Zzf">
|
||||||
|
<rect key="frame" x="18" y="153" width="389" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="HotPocket Instance URL" id="XwM-DV-kei">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ygC-xe-m6y">
|
||||||
|
<rect key="frame" x="20" y="124" width="385" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="rHK-hP-yWO">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<binding destination="XfG-lQ-9wD" name="value" keyPath="self.baseURL" id="OhE-52-yPd">
|
||||||
|
<dictionary key="options">
|
||||||
|
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
|
||||||
|
</dictionary>
|
||||||
|
</binding>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIc-8O-uoQ">
|
||||||
|
<rect key="frame" x="18" y="68" width="389" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" title="Enter the URL to your HotPocket instance, e.g. https://my.hotpocket.app" id="Y0q-a1-oBP">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PAH-U3-ltl">
|
||||||
|
<rect key="frame" x="180" y="221" width="64" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon-mac-384" id="jnV-K2-7gf"/>
|
||||||
|
</imageView>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Zr-9i-XDS">
|
||||||
|
<rect key="frame" x="13" y="33" width="89" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Continue" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="HBR-3P-qCC">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" name="AccentColor"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doStartAuthorizationFlow:" target="XfG-lQ-9wD" id="AOi-Wt-gmL"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mQc-Ea-NNN">
|
||||||
|
<rect key="frame" x="18" y="185" width="389" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="NTZ-zl-yhk">
|
||||||
|
<font key="font" metaFont="system" size="24"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="606" y="1067"/>
|
||||||
|
</scene>
|
||||||
|
<!--Authorization Progress View Controller-->
|
||||||
|
<scene sceneID="HWJ-b5-BG6">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="AuthorizationProgressViewController" id="OX4-Oj-1cw" customClass="AuthorizationProgressViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="qln-dC-eog">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
|
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<wkWebViewConfiguration key="configuration">
|
<subviews>
|
||||||
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yRj-hC-QYS">
|
||||||
<wkPreferences key="preferences"/>
|
<rect key="frame" x="18" y="185" width="389" height="28"/>
|
||||||
</wkWebViewConfiguration>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
</wkWebView>
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="F4l-2Z-D79">
|
||||||
|
<font key="font" metaFont="system" size="24"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ca6-br-wxp">
|
||||||
|
<rect key="frame" x="180" y="221" width="64" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon-mac-384" id="faZ-e6-z8R"/>
|
||||||
|
</imageView>
|
||||||
|
<progressIndicator fixedFrame="YES" maxValue="100" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="51a-ii-yug">
|
||||||
|
<rect key="frame" x="196" y="113" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
</progressIndicator>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g9a-gR-c7o">
|
||||||
|
<rect key="frame" x="18" y="81" width="389" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Awaiting authorization response..." id="3oi-LK-vKv">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="webView" destination="eOr-cG-IQY" id="GFe-mU-dBY"/>
|
<outlet property="progressIndicator" destination="51a-ii-yug" id="hWy-Hb-3pE"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="Dav-PG-FiQ" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="75" y="655"/>
|
<point key="canvasLocation" x="605.5" y="654.5"/>
|
||||||
|
</scene>
|
||||||
|
<!--Main View Controller-->
|
||||||
|
<scene sceneID="UJA-e4-0rA">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="MainViewController" id="r5D-xE-cNT" customClass="MainViewController" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="jVt-oL-KPZ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ybL-DV-U73">
|
||||||
|
<rect key="frame" x="180" y="221" width="64" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="icon-mac-384" id="fae-mz-0sj"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-KB-3Ut">
|
||||||
|
<rect key="frame" x="18" y="185" width="389" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="r5O-Sk-IdK">
|
||||||
|
<font key="font" metaFont="system" size="24"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2h7-bN-dsa">
|
||||||
|
<rect key="frame" x="18" y="121" width="389" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" title="HotPocket is configured and ready." id="5fh-mh-WR1">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uci-UC-wxo">
|
||||||
|
<rect key="frame" x="18" y="97" width="389" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Instance URL" id="azk-ea-KeN">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LR4-eJ-jlA">
|
||||||
|
<rect key="frame" x="13" y="30" width="80" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Log out" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="LDr-35-Wph">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" name="DangerColor"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doLogOut:" target="r5D-xE-cNT" id="BMt-Zp-8v5"/>
|
||||||
|
<binding destination="r5D-xE-cNT" name="enabled" keyPath="self.logoutButtonEnabled" id="gTs-BO-USz"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8H3-oU-acU" customClass="LinkLabel">
|
||||||
|
<rect key="frame" x="18" y="73" width="389" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" allowsEditingTextAttributes="YES" id="EoA-mM-phM">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="SecondaryColor"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="instanceURLLabel" destination="8H3-oU-acU" id="EuY-xr-zar"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="dlo-Mj-lGf" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="74.5" y="654.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="icon-mac-384" width="384" height="384"/>
|
||||||
|
<namedColor name="AccentColor">
|
||||||
|
<color red="0.10980392156862745" green="0.72941176470588232" blue="0.92941176470588238" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="DangerColor">
|
||||||
|
<color red="0.93300002813339233" green="0.3919999897480011" blue="0.46299999952316284" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="SecondaryColor">
|
||||||
|
<color red="0.0" green="0.50980392156862742" blue="0.8666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -4,9 +4,18 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.files.user-selected.read-only</key>
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocketShared</string>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
32
services/apple/macOS (App)/Info.plist
Normal file
32
services/apple/macOS (App)/Info.plist
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?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>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>CFBundleURLIconFile</key>
|
||||||
|
<string>icon-mac-384</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>HotPocketDesktopMac</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>hotpocket-desktop</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>HPAuthFlowPostAuthenticateURLParts</key>
|
||||||
|
<dict>
|
||||||
|
<key>host</key>
|
||||||
|
<string>post-authenticate</string>
|
||||||
|
<key>scheme</key>
|
||||||
|
<string>hotpocket-desktop</string>
|
||||||
|
</dict>
|
||||||
|
<key>HPAuthFlowSource</key>
|
||||||
|
<string>HotPocketDesktop</string>
|
||||||
|
<key>HPRPCClientOrigin</key>
|
||||||
|
<string>hotpocket-desktop://HPRPCClient</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
16
services/apple/macOS (App)/LinkLabel.h
Normal file
16
services/apple/macOS (App)/LinkLabel.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// LinkLabel.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 24/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface LinkLabel : NSTextField
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
24
services/apple/macOS (App)/LinkLabel.m
Normal file
24
services/apple/macOS (App)/LinkLabel.m
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// LinkLabel.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 24/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "LinkLabel.h"
|
||||||
|
|
||||||
|
@implementation LinkLabel
|
||||||
|
|
||||||
|
-(void)awakeFromNib {
|
||||||
|
[super awakeFromNib];
|
||||||
|
self.allowsEditingTextAttributes = YES;
|
||||||
|
self.textColor = [NSColor colorNamed:@"SecondaryColor"];
|
||||||
|
self.selectable = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)resetCursorRects {
|
||||||
|
[super resetCursorRects];
|
||||||
|
[self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
22
services/apple/macOS (App)/MainViewController.h
Normal file
22
services/apple/macOS (App)/MainViewController.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// MainViewController.h
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 22/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface MainViewController : NSViewController
|
||||||
|
|
||||||
|
@property IBOutlet NSTextField *instanceURLLabel;
|
||||||
|
|
||||||
|
@property BOOL logoutButtonEnabled;
|
||||||
|
|
||||||
|
-(IBAction)doLogOut:(id)sender;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
65
services/apple/macOS (App)/MainViewController.m
Normal file
65
services/apple/macOS (App)/MainViewController.m
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// MainViewController.m
|
||||||
|
// HotPocket (iOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 22/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MainViewController.h"
|
||||||
|
|
||||||
|
#import "HPCredentialsHelper.h"
|
||||||
|
#import "AuthorizationViewController.h"
|
||||||
|
#import "ReplaceAnimator.h"
|
||||||
|
|
||||||
|
@interface MainViewController (MainViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MainViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.logoutButtonEnabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidAppear {
|
||||||
|
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
|
||||||
|
|
||||||
|
if (credentials.usable == NO) {
|
||||||
|
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
|
||||||
|
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
|
||||||
|
} else {
|
||||||
|
self.logoutButtonEnabled = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *instanceURLText = @"";
|
||||||
|
if (credentials.baseURL != nil) {
|
||||||
|
instanceURLText = credentials.baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableAttributedString *instanceURLValue = [[NSMutableAttributedString alloc] initWithString:instanceURLText];
|
||||||
|
if (credentials.baseURL != nil) {
|
||||||
|
[instanceURLValue addAttribute:NSLinkAttributeName
|
||||||
|
value:credentials.baseURL
|
||||||
|
range:NSMakeRange(0, instanceURLValue.length)];
|
||||||
|
}
|
||||||
|
[instanceURLValue addAttribute:NSUnderlineStyleAttributeName
|
||||||
|
value:[NSNumber numberWithInteger:NSUnderlineStyleSingle]
|
||||||
|
range:NSMakeRange(0, instanceURLValue.length)];
|
||||||
|
|
||||||
|
self.instanceURLLabel.attributedStringValue = instanceURLValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)doLogOut:(id)sender {
|
||||||
|
[[HPCredentialsHelper sharedHelper] clearCredentials];
|
||||||
|
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
|
||||||
|
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
16
services/apple/macOS (App)/ReplaceAnimator.h
Normal file
16
services/apple/macOS (App)/ReplaceAnimator.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// ReplaceAnimator.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ReplaceAnimator : NSObject<NSViewControllerPresentationAnimator>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
29
services/apple/macOS (App)/ReplaceAnimator.m
Normal file
29
services/apple/macOS (App)/ReplaceAnimator.m
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// ReplaceAnimator.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 20/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ReplaceAnimator.h"
|
||||||
|
|
||||||
|
@implementation ReplaceAnimator
|
||||||
|
|
||||||
|
-(void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
|
||||||
|
NSView *container = fromViewController.view.superview;
|
||||||
|
if (container == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[fromViewController.view removeFromSuperview];
|
||||||
|
|
||||||
|
[container addSubview:viewController.view];
|
||||||
|
viewController.view.frame = NSMakeRect(0, 0, viewController.view.frame.size.width, viewController.view.frame.size.height);
|
||||||
|
viewController.view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
|
||||||
|
[viewController.view removeFromSuperview];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
19
services/apple/macOS (App)/WindowContentView.h
Normal file
19
services/apple/macOS (App)/WindowContentView.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// WindowContentView.h
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface WindowContentView : NSView
|
||||||
|
|
||||||
|
@property (strong) NSColor *darkBackgroundColor;
|
||||||
|
@property (strong) NSColor *lightBackgroundColor;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
42
services/apple/macOS (App)/WindowContentView.m
Normal file
42
services/apple/macOS (App)/WindowContentView.m
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// WindowContentView.m
|
||||||
|
// HotPocket (macOS)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 30/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "WindowContentView.h"
|
||||||
|
|
||||||
|
@implementation WindowContentView
|
||||||
|
|
||||||
|
-(void)awakeFromNib {
|
||||||
|
[super awakeFromNib];
|
||||||
|
self.darkBackgroundColor = [NSColor colorNamed:@"BackgroundColor"];
|
||||||
|
self.lightBackgroundColor = [NSColor windowBackgroundColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)isOpaque {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)drawRect:(NSRect)dirtyRect {
|
||||||
|
[super drawRect:dirtyRect];
|
||||||
|
|
||||||
|
NSAppearance *appearance = self.effectiveAppearance;
|
||||||
|
NSAppearanceName bestMatch = [appearance bestMatchFromAppearancesWithNames:@[
|
||||||
|
NSAppearanceNameAqua,
|
||||||
|
NSAppearanceNameDarkAqua
|
||||||
|
]];
|
||||||
|
|
||||||
|
NSColor *backgroundColor = self.lightBackgroundColor;
|
||||||
|
if ([bestMatch isEqualToString:NSAppearanceNameDarkAqua] == YES) {
|
||||||
|
backgroundColor = self.darkBackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backgroundColor) {
|
||||||
|
[backgroundColor setFill];
|
||||||
|
NSRectFill(dirtyRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -4,7 +4,16 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.files.user-selected.read-only</key>
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocketShared</string>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocket.Extension</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24127" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24127"/>
|
||||||
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
|
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="ShareViewController">
|
||||||
|
<connections>
|
||||||
|
<outlet property="progressIndicator" destination="3hY-1S-Wo2" id="Dpk-yi-LVA"/>
|
||||||
|
<outlet property="view" destination="1" id="2"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="388" height="258"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="339-Mg-wWu">
|
||||||
|
<rect key="frame" x="163" y="174" width="64" height="64"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="icon-mac-384" id="NT0-XU-t9f"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KZW-gY-pvX">
|
||||||
|
<rect key="frame" x="18" y="138" width="352" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="urI-Z1-yMm">
|
||||||
|
<font key="font" metaFont="system" size="24"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<customView id="ERc-i0-Lfz" userLabel="Error View">
|
||||||
|
<rect key="frame" x="20" y="22" width="348" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hwr-tf-MxL">
|
||||||
|
<rect key="frame" x="140" y="-7" width="69" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7Kd-FS-0AY">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="-2" id="yRt-GR-jQ6"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5X8-4n-wWm">
|
||||||
|
<rect key="frame" x="-2" y="28" width="352" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="center" title="HotPocket couldn't complete this operation." id="fmg-RT-3FA">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="95C-iI-GaL">
|
||||||
|
<rect key="frame" x="158" y="65" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="multiply.circle.fill" catalog="system" id="iXK-aQ-lIy"/>
|
||||||
|
<color key="contentTintColor" name="DangerColor"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="hidden" keyPath="self.errorViewHidden" id="WUX-Pk-WLH"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
<customView id="lQA-9N-11c" userLabel="Done View">
|
||||||
|
<rect key="frame" x="20" y="22" width="348" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="QBX-Dv-VAz">
|
||||||
|
<rect key="frame" x="158" y="65" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="checkmark.circle.fill" catalog="system" id="iFW-1s-X0l"/>
|
||||||
|
<color key="contentTintColor" name="SuccessColor"/>
|
||||||
|
</imageView>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DFX-X0-zX8">
|
||||||
|
<rect key="frame" x="140" y="-7" width="69" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="2jG-9M-YQb">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="close:" target="-2" id="3aP-Lu-EzX"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mfx-pW-oi2">
|
||||||
|
<rect key="frame" x="-2" y="28" width="352" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="center" title="Your link has been saved!" id="JhJ-K4-UFb">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="hidden" keyPath="self.doneViewHidden" id="4mN-62-nDm"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
<customView id="XMP-x8-OMJ" userLabel="Needs Setup View">
|
||||||
|
<rect key="frame" x="20" y="22" width="348" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="p8k-QM-ZwX">
|
||||||
|
<rect key="frame" x="158" y="65" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="exclamationmark.circle.fill" catalog="system" id="3kO-Gq-csg"/>
|
||||||
|
<color key="contentTintColor" name="WarningColor"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLC-Bx-qKZ">
|
||||||
|
<rect key="frame" x="-2" y="28" width="352" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="center" title="Open the HotPocket application to set it up." id="eYb-eq-cbo">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ufJ-HY-8Ir">
|
||||||
|
<rect key="frame" x="136" y="-7" width="76" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aH5-RV-e1O">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="-2" id="ZRZ-Es-NM6"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="hidden" keyPath="self.needsSetupViewHidden" id="zQw-vW-9Y4"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Szo-4N-z5M" userLabel="Saving View">
|
||||||
|
<rect key="frame" x="20" y="22" width="348" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="b64-XQ-Anx">
|
||||||
|
<rect key="frame" x="136" y="-7" width="76" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7U4-so-kvt">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="-2" id="Aia-vP-eiA"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<progressIndicator fixedFrame="YES" maxValue="100" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="3hY-1S-Wo2">
|
||||||
|
<rect key="frame" x="158" y="52" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
</progressIndicator>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="hidden" keyPath="self.savingViewHidden" id="HNo-aa-0OR"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
<customView id="cFw-BG-nDd" userLabel="Unprocessable Entity View">
|
||||||
|
<rect key="frame" x="20" y="22" width="348" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="hQi-Zt-thw">
|
||||||
|
<rect key="frame" x="158" y="65" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="exclamationmark.circle.fill" catalog="system" id="66K-cT-2Vw"/>
|
||||||
|
<color key="contentTintColor" name="WarningColor"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LS4-qN-h75">
|
||||||
|
<rect key="frame" x="-2" y="28" width="352" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="center" title="This item couldn't be shared :(." id="b0i-Lf-21f">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cDA-ec-njT">
|
||||||
|
<rect key="frame" x="136" y="-7" width="76" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="lr2-gc-RFv">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="-2" id="yWF-hM-ejy"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="hidden" keyPath="self.unprocessableEntityViewHidden" id="lqC-lO-ll8"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1yJ-sU-Spr" userLabel="uname Label">
|
||||||
|
<rect key="frame" x="6" y="4" width="376" height="14"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" id="nQ0-Es-oIB">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="controlAccentColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<connections>
|
||||||
|
<binding destination="-2" name="value" keyPath="self.uname" id="rKp-CV-Mpj"/>
|
||||||
|
</connections>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<point key="canvasLocation" x="132" y="-45"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="checkmark.circle.fill" catalog="system" width="15" height="15"/>
|
||||||
|
<image name="exclamationmark.circle.fill" catalog="system" width="15" height="15"/>
|
||||||
|
<image name="icon-mac-384" width="384" height="384"/>
|
||||||
|
<image name="multiply.circle.fill" catalog="system" width="15" height="15"/>
|
||||||
|
<namedColor name="DangerColor">
|
||||||
|
<color red="0.93300002813339233" green="0.3919999897480011" blue="0.46299999952316284" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="SuccessColor">
|
||||||
|
<color red="0.054901960784313725" green="0.65490196078431373" blue="0.40392156862745099" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
<namedColor name="WarningColor">
|
||||||
|
<color red="0.94509803921568625" green="0.58823529411764708" blue="0.1803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
</resources>
|
||||||
|
</document>
|
29
services/apple/macOS (Share Extension)/Info.plist
Normal file
29
services/apple/macOS (Share Extension)/Info.plist
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?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>CFBundleIconFile</key>
|
||||||
|
<string>icon</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionJavaScriptPreprocessingFile</key>
|
||||||
|
<string>ShareExtensionHelper</string>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>ShareViewController</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
24
services/apple/macOS (Share Extension)/ShareViewController.h
Normal file
24
services/apple/macOS (Share Extension)/ShareViewController.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// ShareViewController.h
|
||||||
|
// macOS (Share Extension)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 22/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@class HPAPI;
|
||||||
|
|
||||||
|
@interface ShareViewController : NSViewController
|
||||||
|
|
||||||
|
@property HPAPI *api;
|
||||||
|
@property BOOL savingViewHidden;
|
||||||
|
@property BOOL needsSetupViewHidden;
|
||||||
|
@property BOOL doneViewHidden;
|
||||||
|
@property BOOL errorViewHidden;
|
||||||
|
@property BOOL unprocessableEntityViewHidden;
|
||||||
|
@property NSString *uname;
|
||||||
|
|
||||||
|
@property IBOutlet NSProgressIndicator *progressIndicator;
|
||||||
|
|
||||||
|
@end
|
128
services/apple/macOS (Share Extension)/ShareViewController.m
Normal file
128
services/apple/macOS (Share Extension)/ShareViewController.m
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
//
|
||||||
|
// ShareViewController.m
|
||||||
|
// macOS (Share Extension)
|
||||||
|
//
|
||||||
|
// Created by Tomek Wójcik on 22/09/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ShareViewController.h"
|
||||||
|
|
||||||
|
#import "HPAPI.h"
|
||||||
|
#import "HPShareExtensionHelper.h"
|
||||||
|
|
||||||
|
@implementation ShareViewController (ShareViewControllerPrivate)
|
||||||
|
|
||||||
|
#pragma mark - Private interface
|
||||||
|
|
||||||
|
-(void)saveURL:(NSURL *)url {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController save:] url=`%@`", url);
|
||||||
|
#endif
|
||||||
|
BOOL callResult = [self.api save:url completionHandler:^(NSString * _Nullable callId, HPRPCCallResult * _Nullable result) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
self.savingViewHidden = YES;
|
||||||
|
|
||||||
|
if (result.error != nil) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController resolveLinkAndSave] saveError=`%@`", result.error);
|
||||||
|
#endif
|
||||||
|
self.errorViewHidden = NO;
|
||||||
|
} else {
|
||||||
|
self.doneViewHidden = NO;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (callResult == NO) {
|
||||||
|
self.savingViewHidden = YES;
|
||||||
|
self.errorViewHidden = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)resolveLinkAndSave {
|
||||||
|
HPShareExtensionHelper *helper = [[HPShareExtensionHelper alloc] initWithContext:self.extensionContext];
|
||||||
|
[helper processItems:^(NSURL *url) {
|
||||||
|
if (url == nil) {
|
||||||
|
self.savingViewHidden = YES;
|
||||||
|
self.unprocessableEntityViewHidden = NO;
|
||||||
|
} else {
|
||||||
|
[self saveURL:url];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ShareViewController
|
||||||
|
|
||||||
|
#pragma mark - View lifecycle
|
||||||
|
|
||||||
|
-(NSString *)nibName {
|
||||||
|
return @"ShareViewController";
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.savingViewHidden = NO;
|
||||||
|
self.needsSetupViewHidden = YES;
|
||||||
|
self.doneViewHidden = YES;
|
||||||
|
self.errorViewHidden = YES;
|
||||||
|
self.unprocessableEntityViewHidden = YES;
|
||||||
|
|
||||||
|
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||||
|
self.uname = [NSString stringWithFormat:@"HotPocket v%@ (%@)", [mainBundle.infoDictionary valueForKey:@"CFBundleShortVersionString"], [mainBundle.infoDictionary valueForKey:@"CFBundleVersion"]];
|
||||||
|
|
||||||
|
self.api = [[HPAPI alloc] init];
|
||||||
|
if (self.api.rpcClient.hasCredentials == YES) {
|
||||||
|
self.savingViewHidden = NO;
|
||||||
|
self.needsSetupViewHidden = YES;
|
||||||
|
} else {
|
||||||
|
self.savingViewHidden = YES;
|
||||||
|
self.needsSetupViewHidden = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewWillAppear {
|
||||||
|
[super viewWillAppear];
|
||||||
|
[self.progressIndicator startAnimation:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidAppear {
|
||||||
|
[super viewDidAppear];
|
||||||
|
|
||||||
|
[self.api checkAuth:^(BOOL authValid, NSError *error, NSString *callId) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (authValid == NO) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSLog(@"-[ShareViewController viewDidAppear:] checkAuthError=`%@`", error);
|
||||||
|
#endif
|
||||||
|
self.savingViewHidden = YES;
|
||||||
|
self.needsSetupViewHidden = NO;
|
||||||
|
} else {
|
||||||
|
[self resolveLinkAndSave];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)viewDidDisappear {
|
||||||
|
[super viewDidDisappear];
|
||||||
|
[self.progressIndicator stopAnimation:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
-(IBAction)close:(id)sender {
|
||||||
|
NSExtensionItem *outputItem = [[NSExtensionItem alloc] init];
|
||||||
|
|
||||||
|
NSArray *outputItems = @[outputItem];
|
||||||
|
[self.extensionContext completeRequestReturningItems:outputItems completionHandler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(IBAction)cancel:(id)sender {
|
||||||
|
NSError *cancelError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];
|
||||||
|
[self.extensionContext cancelRequestWithError:cancelError];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
BIN
services/apple/macOS (Share Extension)/icon.icns
Normal file
BIN
services/apple/macOS (Share Extension)/icon.icns
Normal file
Binary file not shown.
|
@ -0,0 +1,21 @@
|
||||||
|
<?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.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.pl.bthlabs.HotPocket</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocketShared</string>
|
||||||
|
<string>$(AppIdentifierPrefix)pl.bthlabs.HotPocket.ShareExtension</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hotpocket-apple"
|
name = "hotpocket-apple"
|
||||||
version = "25.9.12"
|
version = "25.9.17"
|
||||||
description = "HotPocket Apple Integrations"
|
description = "HotPocket Apple Integrations"
|
||||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 5.2.3 on 2025-09-22 07:20
|
||||||
|
|
||||||
|
import uuid6
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0005_accesstoken'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AuthKey',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid6.uuid7, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('account_uuid', models.UUIDField(db_index=True, default=None)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('deleted_at', models.DateTimeField(blank=True, db_index=True, default=None, editable=False, null=True)),
|
||||||
|
('key', models.CharField(db_index=True, default=None, editable=False, max_length=128, unique=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Auth Key',
|
||||||
|
'verbose_name_plural': 'Auth Keys',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.2.3 on 2025-10-01 07:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0006_authkey'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='authkey',
|
||||||
|
name='consumed_at',
|
||||||
|
field=models.DateTimeField(blank=True, db_index=True, default=None, editable=False, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,2 +1,3 @@
|
||||||
from .access_token import AccessToken # noqa: F401
|
from .access_token import AccessToken # noqa: F401
|
||||||
from .account import Account # noqa: F401
|
from .account import Account # noqa: F401
|
||||||
|
from .auth_key import AuthKey # noqa: F401
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.core.models import Model
|
||||||
|
|
||||||
|
|
||||||
|
class ActiveAuthKeysManager(models.Manager):
|
||||||
|
def get_queryset(self) -> models.QuerySet[AuthKey]:
|
||||||
|
return super().get_queryset().filter(
|
||||||
|
deleted_at__isnull=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthKey(Model):
|
||||||
|
key = models.CharField(
|
||||||
|
blank=False,
|
||||||
|
default=None,
|
||||||
|
null=False,
|
||||||
|
max_length=128,
|
||||||
|
db_index=True,
|
||||||
|
unique=True,
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
consumed_at = models.DateTimeField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
db_index=True,
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
active_objects = ActiveAuthKeysManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Auth Key')
|
||||||
|
verbose_name_plural = _('Auth Keys')
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'<AuthKey pk={self.pk} key={self.key}>'
|
|
@ -1 +1,3 @@
|
||||||
from .access_tokens import AccessTokensService # noqa: F401
|
from .access_tokens import AccessTokensService # noqa: F401
|
||||||
|
from .accounts import AccountsService # noqa: F401
|
||||||
|
from .auth_keys import AuthKeysService # noqa: F401
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.accounts.models import Account
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AccountsService:
|
||||||
|
class AccountsServiceError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AccountNotFound(AccountsServiceError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get(self, *, pk: uuid.UUID) -> Account:
|
||||||
|
try:
|
||||||
|
query_set = Account.objects.filter(is_active=True)
|
||||||
|
|
||||||
|
return query_set.get(pk=pk)
|
||||||
|
except Account.DoesNotExist as exception:
|
||||||
|
raise self.AccountNotFound(
|
||||||
|
f'Account not found: pk=`{pk}`',
|
||||||
|
) from exception
|
|
@ -0,0 +1,72 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.utils.timezone import now
|
||||||
|
import uuid6
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.accounts.models import AuthKey
|
||||||
|
from hotpocket_backend.apps.core.conf import settings
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthKeysService:
|
||||||
|
class AuthKeysServiceError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AuthKeyNotFound(AuthKeysServiceError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AuthKeyExpired(AuthKeysServiceError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AuthKeyAccessDenied(AuthKeysServiceError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create(self, *, account_uuid: uuid.UUID) -> AuthKey:
|
||||||
|
key = str(uuid6.uuid7())
|
||||||
|
|
||||||
|
return AuthKey.objects.create(
|
||||||
|
account_uuid=account_uuid,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get(self, *, pk: uuid.UUID) -> AuthKey:
|
||||||
|
try:
|
||||||
|
query_set = AuthKey.active_objects
|
||||||
|
|
||||||
|
return query_set.get(pk=pk)
|
||||||
|
except AuthKey.DoesNotExist as exception:
|
||||||
|
raise self.AuthKeyNotFound(
|
||||||
|
f'Auth Key not found: pk=`{pk}`',
|
||||||
|
) from exception
|
||||||
|
|
||||||
|
def get_by_key(self, *, key: str, ttl: int | None = None) -> AuthKey:
|
||||||
|
try:
|
||||||
|
query_set = AuthKey.active_objects
|
||||||
|
|
||||||
|
result = query_set.get(key=key)
|
||||||
|
|
||||||
|
if ttl is None:
|
||||||
|
ttl = settings.AUTH_KEY_TTL
|
||||||
|
|
||||||
|
if ttl > 0:
|
||||||
|
if result.created_at < now() - datetime.timedelta(seconds=ttl):
|
||||||
|
raise self.AuthKeyExpired(
|
||||||
|
f'Auth Key expired: pk=`{key}`',
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.consumed_at is not None:
|
||||||
|
raise self.AuthKeyExpired(
|
||||||
|
f'Auth Key already consumed: pk=`{key}`',
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
except AuthKey.DoesNotExist as exception:
|
||||||
|
raise self.AuthKeyNotFound(
|
||||||
|
f'Auth Key not found: key=`{key}`',
|
||||||
|
) from exception
|
|
@ -30,3 +30,5 @@ class PSettings(typing.Protocol):
|
||||||
SAVES_ASSOCIATION_ADAPTER: str
|
SAVES_ASSOCIATION_ADAPTER: str
|
||||||
|
|
||||||
UPLOADS_PATH: pathlib.Path
|
UPLOADS_PATH: pathlib.Path
|
||||||
|
|
||||||
|
AUTH_KEY_TTL: int
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 5.2.3 on 2025-10-01 05:35
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('saves', '0007_association_target_description_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='save',
|
||||||
|
name='url',
|
||||||
|
field=models.CharField(default=None, validators=[django.core.validators.URLValidator(schemes=['http', 'https'])]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
@ -20,6 +21,9 @@ class Save(Model):
|
||||||
)
|
)
|
||||||
url = models.CharField(
|
url = models.CharField(
|
||||||
blank=False, null=False, default=None,
|
blank=False, null=False, default=None,
|
||||||
|
validators=[
|
||||||
|
validators.URLValidator(schemes=['http', 'https']),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
content = models.BinaryField(
|
content = models.BinaryField(
|
||||||
blank=True, null=True, default=None, editable=False,
|
blank=True, null=True, default=None, editable=False,
|
||||||
|
|
|
@ -23,3 +23,11 @@ class UIAccessTokenOriginApp(enum.Enum):
|
||||||
SAFARI_WEB_EXTENSION = _('Safari Web Extension')
|
SAFARI_WEB_EXTENSION = _('Safari Web Extension')
|
||||||
CHROME_EXTENSION = _('Chrome Extension')
|
CHROME_EXTENSION = _('Chrome Extension')
|
||||||
FIREFOX_EXTENSION = _('Firefox Extension')
|
FIREFOX_EXTENSION = _('Firefox Extension')
|
||||||
|
HOTPOCKET_DESKTOP = _('HotPocket Desktop')
|
||||||
|
HOTPOCKET_MOBILE = _('HotPocket Mobile')
|
||||||
|
|
||||||
|
|
||||||
|
class AuthSource(enum.Enum):
|
||||||
|
BROWSER_EXTENSION = 'HotPocketExtension'
|
||||||
|
DESKTOP = 'HotPocketDesktop'
|
||||||
|
MOBILE = 'HotPocketMobile'
|
||||||
|
|
|
@ -7,23 +7,37 @@ from bthlabs_jsonrpc_core import register_method
|
||||||
from django import db
|
from django import db
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
from hotpocket_soa.services import AccessTokensService
|
from hotpocket_soa.services import (
|
||||||
|
AccessTokensService,
|
||||||
|
AccountsService,
|
||||||
|
AuthKeysService,
|
||||||
|
)
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@register_method('accounts.access_tokens.create')
|
@register_method('accounts.access_tokens.create', namespace='accounts')
|
||||||
def create(request: HttpRequest,
|
def create(request: HttpRequest,
|
||||||
auth_key: str,
|
auth_key: str,
|
||||||
meta: dict,
|
meta: dict,
|
||||||
) -> str:
|
) -> str:
|
||||||
with db.transaction.atomic():
|
with db.transaction.atomic():
|
||||||
try:
|
try:
|
||||||
assert 'extension_auth_key' in request.session, 'Auth key missing'
|
auth_key_object = AuthKeysService().get_by_key(
|
||||||
assert request.session['extension_auth_key'] == auth_key, (
|
account_uuid=None,
|
||||||
'Auth key mismatch'
|
key=auth_key,
|
||||||
)
|
)
|
||||||
except AssertionError as exception:
|
except AuthKeysService.AuthKeyNotFound as exception:
|
||||||
|
LOGGER.error(
|
||||||
|
'Unable to issue access token: %s',
|
||||||
|
exception,
|
||||||
|
exc_info=exception,
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
account = AccountsService().get(pk=auth_key_object.account_uuid)
|
||||||
|
except AccountsService.AccountNotFound as exception:
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
'Unable to issue access token: %s',
|
'Unable to issue access token: %s',
|
||||||
exception,
|
exception,
|
||||||
|
@ -32,12 +46,9 @@ def create(request: HttpRequest,
|
||||||
raise
|
raise
|
||||||
|
|
||||||
access_token = AccessTokensService().create(
|
access_token = AccessTokensService().create(
|
||||||
account_uuid=request.user.pk,
|
account_uuid=account.pk,
|
||||||
origin=request.META['HTTP_ORIGIN'],
|
origin=request.META['HTTP_ORIGIN'],
|
||||||
meta=meta,
|
meta=meta,
|
||||||
)
|
)
|
||||||
|
|
||||||
request.session.pop('extension_auth_key')
|
|
||||||
request.session.save()
|
|
||||||
|
|
||||||
return access_token.key
|
return access_token.key
|
||||||
|
|
|
@ -13,16 +13,18 @@ from hotpocket_soa.services import AccessTokensService
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@register_method('accounts.auth.check')
|
@register_method('accounts.auth.check', namespace='accounts')
|
||||||
def check(request: HttpRequest) -> bool:
|
def check(request: HttpRequest) -> bool:
|
||||||
return request.user.is_anonymous is False
|
return request.user.is_anonymous is False
|
||||||
|
|
||||||
|
|
||||||
@register_method('accounts.auth.check_access_token')
|
@register_method('accounts.auth.check_access_token', namespace='accounts')
|
||||||
def check_access_token(request: HttpRequest,
|
def check_access_token(request: HttpRequest,
|
||||||
access_token: str,
|
access_token: str,
|
||||||
meta: dict | None = None,
|
meta: dict | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
assert request.user.is_anonymous is False, 'Not authenticated'
|
||||||
|
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -11,8 +11,27 @@
|
||||||
<div class="alert alert-success mt-3" role="alert">
|
<div class="alert alert-success mt-3" role="alert">
|
||||||
<h4 class="alert-heading">{% translate 'Done!' %}</h4>
|
<h4 class="alert-heading">{% translate 'Done!' %}</h4>
|
||||||
<p class="lead mb-0">
|
<p class="lead mb-0">
|
||||||
|
{% if app_redirect_url %}
|
||||||
|
{% translate "You've successfully logged in to the application." %}
|
||||||
|
{% else %}
|
||||||
{% translate "You've successfully logged in to the extension." %}
|
{% translate "You've successfully logged in to the extension." %}
|
||||||
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_scripts %}
|
||||||
|
{% if app_redirect_url %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
(() => {
|
||||||
|
window.setTimeout(
|
||||||
|
() => {
|
||||||
|
window.location.replace('{{ app_redirect_url|safe }}');
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -137,6 +137,8 @@ def render_access_token_app(access_token: AccessTokenOut) -> str:
|
||||||
AccessTokenOriginApp.SAFARI_WEB_EXTENSION,
|
AccessTokenOriginApp.SAFARI_WEB_EXTENSION,
|
||||||
AccessTokenOriginApp.CHROME_EXTENSION,
|
AccessTokenOriginApp.CHROME_EXTENSION,
|
||||||
AccessTokenOriginApp.FIREFOX_EXTENSION,
|
AccessTokenOriginApp.FIREFOX_EXTENSION,
|
||||||
|
AccessTokenOriginApp.HOTPOCKET_DESKTOP,
|
||||||
|
AccessTokenOriginApp.HOTPOCKET_MOBILE,
|
||||||
)
|
)
|
||||||
if origin_app in extension_origin_apps:
|
if origin_app in extension_origin_apps:
|
||||||
app = UIAccessTokenOriginApp[origin_app.value].value
|
app = UIAccessTokenOriginApp[origin_app.value].value
|
||||||
|
@ -152,7 +154,7 @@ def render_access_token_app(access_token: AccessTokenOut) -> str:
|
||||||
@register.filter(name='render_access_token_platform')
|
@register.filter(name='render_access_token_platform')
|
||||||
def render_access_token_platform(access_token: AccessTokenOut) -> str:
|
def render_access_token_platform(access_token: AccessTokenOut) -> str:
|
||||||
match access_token.meta.get('platform', None):
|
match access_token.meta.get('platform', None):
|
||||||
case 'MacIntel':
|
case 'MacIntel' | 'macOS':
|
||||||
return 'macOS'
|
return 'macOS'
|
||||||
|
|
||||||
case 'iPhone':
|
case 'iPhone':
|
||||||
|
|
|
@ -59,6 +59,13 @@ urlpatterns = [
|
||||||
accounts.apps.DeleteView.as_view(),
|
accounts.apps.DeleteView.as_view(),
|
||||||
name='ui.accounts.apps.delete',
|
name='ui.accounts.apps.delete',
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
'accounts/rpc/',
|
||||||
|
JSONRPCView.as_view(
|
||||||
|
namespace='accounts',
|
||||||
|
),
|
||||||
|
name='ui.accounts.rpc',
|
||||||
|
),
|
||||||
path('accounts/', accounts.index.index, name='ui.accounts.index'),
|
path('accounts/', accounts.index.index, name='ui.accounts.index'),
|
||||||
path(
|
path(
|
||||||
'imports/pocket/',
|
'imports/pocket/',
|
||||||
|
|
|
@ -2,27 +2,56 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from django import db
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.ui.constants import AuthSource
|
||||||
|
from hotpocket_soa.services import AuthKeysService
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SOURCE_TO_REDIRECT_SCHEME = {
|
||||||
|
AuthSource.DESKTOP.value: 'hotpocket-desktop',
|
||||||
|
AuthSource.MOBILE.value: 'hotpocket-mobile',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def authenticate(request: HttpRequest) -> HttpResponse:
|
def authenticate(request: HttpRequest) -> HttpResponse:
|
||||||
if request.user.is_anonymous is False:
|
source = request.GET.get(
|
||||||
auth_key = str(uuid.uuid4())
|
'source',
|
||||||
|
request.session.get('extension_source', AuthSource.BROWSER_EXTENSION.value),
|
||||||
|
)
|
||||||
|
session_token = request.GET.get(
|
||||||
|
'session_token', request.session.get('extension_session_token', None),
|
||||||
|
)
|
||||||
|
|
||||||
request.session['extension_auth_key'] = auth_key
|
if source == AuthSource.BROWSER_EXTENSION.value:
|
||||||
|
session_token = str(uuid.uuid4())
|
||||||
|
elif source in (AuthSource.DESKTOP.value, AuthSource.MOBILE.value):
|
||||||
|
assert session_token not in ('', None), 'Session token missing'
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unknown source: `{source}`')
|
||||||
|
|
||||||
|
request.session['extension_source'] = source
|
||||||
|
request.session['extension_session_token'] = session_token
|
||||||
request.session.save()
|
request.session.save()
|
||||||
|
|
||||||
|
if request.user.is_anonymous is False:
|
||||||
|
with db.transaction.atomic():
|
||||||
|
auth_key = AuthKeysService().create(
|
||||||
|
account_uuid=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'ui.integrations.extension.post_authenticate',
|
'ui.integrations.extension.post_authenticate',
|
||||||
query=[
|
query=[
|
||||||
('auth_key', auth_key),
|
('auth_key', auth_key.key),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -36,12 +65,35 @@ def post_authenticate(request: HttpRequest) -> HttpResponse:
|
||||||
assert request.user.is_anonymous is False, 'Not authenticated'
|
assert request.user.is_anonymous is False, 'Not authenticated'
|
||||||
|
|
||||||
auth_key = request.GET.get('auth_key', None)
|
auth_key = request.GET.get('auth_key', None)
|
||||||
assert request.session.get('extension_auth_key', None) == auth_key, (
|
assert auth_key is not None, 'Auth key missing'
|
||||||
'Auth key mismatch'
|
source = request.session.get('extension_source', None)
|
||||||
)
|
assert source is not None, 'Source is missing'
|
||||||
|
session_token = request.session.get('extension_session_token', None)
|
||||||
|
assert session_token is not None, 'Session token is missing'
|
||||||
|
|
||||||
|
app_redirect_url = None
|
||||||
|
if source in (AuthSource.DESKTOP.value, AuthSource.MOBILE.value):
|
||||||
|
app_redirect_url = urllib.parse.urlunsplit((
|
||||||
|
SOURCE_TO_REDIRECT_SCHEME[source],
|
||||||
|
'post-authenticate',
|
||||||
|
'/',
|
||||||
|
urllib.parse.urlencode([
|
||||||
|
('session_token', session_token),
|
||||||
|
('auth_key', auth_key),
|
||||||
|
]),
|
||||||
|
'',
|
||||||
|
))
|
||||||
|
|
||||||
|
request.session.pop('extension_source')
|
||||||
|
request.session.pop('extension_session_token')
|
||||||
|
request.session.save()
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request, 'ui/integrations/extension/post_authenticate.html',
|
request,
|
||||||
|
'ui/integrations/extension/post_authenticate.html',
|
||||||
|
{
|
||||||
|
'app_redirect_url': app_redirect_url,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
except AssertionError as exception:
|
except AssertionError as exception:
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
|
|
|
@ -79,3 +79,5 @@ CORS_ALLOW_HEADERS = (
|
||||||
*default_headers,
|
*default_headers,
|
||||||
'cookie',
|
'cookie',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AUTH_KEY_TTL = 30
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
from .access_token import AccessTokenFactory # noqa: F401,F403
|
from .access_token import AccessTokenFactory # noqa: F401,F403
|
||||||
from .account import AccountFactory # noqa: F401,F403
|
from .account import AccountFactory # noqa: F401,F403
|
||||||
|
from .auth_key import AuthKeyFactory # noqa: F401,F403
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import factory
|
||||||
|
|
||||||
|
from hotpocket_backend.apps.accounts.models import AuthKey
|
||||||
|
|
||||||
|
|
||||||
|
class AuthKeyFactory(factory.django.DjangoModelFactory):
|
||||||
|
account_uuid = None
|
||||||
|
key = factory.LazyFunction(lambda: str(uuid.uuid4()))
|
||||||
|
consumed_at = None
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AuthKey
|
|
@ -1,3 +1,4 @@
|
||||||
from .access_token import * # noqa: F401,F403
|
from .access_token import * # noqa: F401,F403
|
||||||
from .account import * # noqa: F401,F403
|
from .account import * # noqa: F401,F403
|
||||||
from .apps import * # noqa: F401,F403
|
from .apps import * # noqa: F401,F403
|
||||||
|
from .auth_key import * # noqa: F401,F403
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user