akanjs 2.3.12-rc.1 → 2.3.13-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +85 -2
- package/package.json +1 -5
- package/types/index.d.ts +93 -2
- package/types/webkit/index.d.ts +0 -5
- package/webkit/index.ts +0 -12
- package/types/webkit/useCamera.d.ts +0 -19
- package/types/webkit/useCodepush.d.ts +0 -16
- package/types/webkit/useContact.d.ts +0 -11
- package/types/webkit/useGeoLocation.d.ts +0 -8
- package/types/webkit/usePurchase.d.ts +0 -19
- package/types/webkit/usePushNotification.d.ts +0 -37
- package/webkit/useCamera.tsx +0 -103
- package/webkit/useCodepush.tsx +0 -99
- package/webkit/useContact.tsx +0 -52
- package/webkit/useGeoLocation.tsx +0 -24
- package/webkit/usePurchase.tsx +0 -156
- package/webkit/usePushNotification.tsx +0 -288
package/index.ts
CHANGED
|
@@ -78,6 +78,87 @@ export interface AkanMobileConfig extends AkanCapacitorLikeConfig {
|
|
|
78
78
|
targets: Record<string, AkanMobileTargetConfig>;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
export interface PluginRuntimeContext {
|
|
82
|
+
readonly appName: string;
|
|
83
|
+
readonly mobile: AkanMobileConfig;
|
|
84
|
+
/** True when any mobile target declares the given permission. */
|
|
85
|
+
hasMobilePermission(permission: MobilePermission): boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AkanScanInfo {
|
|
89
|
+
/** Transitive lib dependencies of the app (or direct deps of a lib). */
|
|
90
|
+
getLibs(): string[];
|
|
91
|
+
getDatabaseModules(): string[];
|
|
92
|
+
getServiceModules(): string[];
|
|
93
|
+
getScalarModules(): string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface AkanExecutor {
|
|
97
|
+
readonly name: string;
|
|
98
|
+
readonly type: "app" | "lib";
|
|
99
|
+
readonly cwdPath: string;
|
|
100
|
+
getPath(rel: string): string;
|
|
101
|
+
exists(rel: string): Promise<boolean>;
|
|
102
|
+
readFile(rel: string): Promise<string>;
|
|
103
|
+
writeFile(rel: string, content: string, opts?: { overwrite?: boolean; silent?: boolean }): Promise<unknown>;
|
|
104
|
+
mkdir(rel: string): Promise<unknown>;
|
|
105
|
+
removeDir(rel: string): Promise<unknown>;
|
|
106
|
+
cp(src: string, dest: string): Promise<void>;
|
|
107
|
+
readdir(rel: string): Promise<string[]>;
|
|
108
|
+
getFilesAndDirs(rel: string): Promise<{ files: string[]; dirs: string[] }>;
|
|
109
|
+
scan(options?: { refresh?: boolean; write?: boolean }): Promise<AkanScanInfo>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface AkanSyncContext {
|
|
113
|
+
readonly appName: string;
|
|
114
|
+
readonly appPath: string;
|
|
115
|
+
/** The full app/lib executor for advanced scan-result access and file operations. */
|
|
116
|
+
readonly executor: AkanExecutor;
|
|
117
|
+
getPath(rel: string): string;
|
|
118
|
+
fileExists(rel: string): Promise<boolean>;
|
|
119
|
+
writeFile(rel: string, content: string, opts?: { overwrite?: boolean }): Promise<void>;
|
|
120
|
+
/** Resolves `env/env.client.ts` and returns its exported `env`, or null when absent/invalid. */
|
|
121
|
+
readEnvClient(): Promise<Record<string, unknown> | null>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface AkanNativeContext {
|
|
125
|
+
readonly appPath: string;
|
|
126
|
+
/** The app executor for advanced scan-result access and file operations. */
|
|
127
|
+
readonly executor: AkanExecutor;
|
|
128
|
+
readonly target: AkanMobileTargetConfig;
|
|
129
|
+
readonly operation: "local" | "release";
|
|
130
|
+
readonly env: MobileEnv;
|
|
131
|
+
/** Set NS-prefixed usage descriptions in the iOS Info.plist (Debug + Release). */
|
|
132
|
+
setIosUsageDescriptions(descriptions: Record<string, string>): Promise<void>;
|
|
133
|
+
/** Merge raw key/values into the iOS Info.plist (Debug + Release). */
|
|
134
|
+
updateIosInfoPlist(values: Record<string, unknown>): Promise<void>;
|
|
135
|
+
/** Contribute entries to the iOS entitlements file (merged, then written once per prepare). */
|
|
136
|
+
addIosEntitlements(entitlements: Record<string, string | string[]>): void;
|
|
137
|
+
/** Transform `ios/App/App/AppDelegate.swift` in place (no-op when the file is absent). */
|
|
138
|
+
editIosAppDelegate(transform: (content: string) => string): Promise<void>;
|
|
139
|
+
/** Add `uses-permission` entries to the Android manifest (without the `android.permission.` prefix). */
|
|
140
|
+
addAndroidPermissions(permissions: string[]): void;
|
|
141
|
+
/** Add `uses-feature` entries to the Android manifest. */
|
|
142
|
+
addAndroidFeatures(features: string[]): void;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface AkanPluginCapacitorConfig {
|
|
146
|
+
/** Mobile permission that activates this plugin's native config (reuses the existing permission model). */
|
|
147
|
+
permission?: MobilePermission;
|
|
148
|
+
/** Imperative native (Capacitor) project configuration. */
|
|
149
|
+
configureNative?: (ctx: AkanNativeContext) => Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface AkanPlugin {
|
|
153
|
+
name: string;
|
|
154
|
+
/** Runtime npm packages this plugin needs; installed on demand by the CLI (e.g. firebase for push). */
|
|
155
|
+
runtimePackages?: (ctx: PluginRuntimeContext) => string[];
|
|
156
|
+
/** Native (Capacitor) project configuration. */
|
|
157
|
+
capacitor?: AkanPluginCapacitorConfig;
|
|
158
|
+
/** Build-time asset generation (e.g. `public/firebase-messaging-sw.js`). */
|
|
159
|
+
syncAssets?: (ctx: AkanSyncContext) => Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
|
|
81
162
|
export interface AppConfigResult {
|
|
82
163
|
docker: DockerConfig;
|
|
83
164
|
defaultDatabaseMode: DatabaseMode;
|
|
@@ -110,8 +191,10 @@ export interface LibConfigContext {
|
|
|
110
191
|
readonly type: "lib";
|
|
111
192
|
}
|
|
112
193
|
|
|
113
|
-
export type
|
|
114
|
-
export type
|
|
194
|
+
export type AppConfigInput = DeepPartial<AppConfigResult> & { plugins?: AkanPlugin[] };
|
|
195
|
+
export type LibConfigInput = DeepPartial<LibConfigResult> & { plugins?: AkanPlugin[] };
|
|
196
|
+
export type AppConfig = AppConfigInput | ((app: AppConfigContext) => AppConfigInput);
|
|
197
|
+
export type LibConfig = LibConfigInput | ((lib: LibConfigContext) => LibConfigInput);
|
|
115
198
|
export type AkanConfigFile = object;
|
|
116
199
|
|
|
117
200
|
export interface FileConventionScanResult {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akanjs",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.13-rc.0",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -195,7 +195,6 @@
|
|
|
195
195
|
"bullmq": "^5.76.10",
|
|
196
196
|
"capacitor-plugin-safe-area": "^5.0.0",
|
|
197
197
|
"chance": "^1.1.13",
|
|
198
|
-
"cordova-plugin-purchase": "^13.16.0",
|
|
199
198
|
"croner": "^10.0.1",
|
|
200
199
|
"daisyui": "5.5.23",
|
|
201
200
|
"firebase": "^12.13.0",
|
|
@@ -280,9 +279,6 @@
|
|
|
280
279
|
"chance": {
|
|
281
280
|
"optional": true
|
|
282
281
|
},
|
|
283
|
-
"cordova-plugin-purchase": {
|
|
284
|
-
"optional": true
|
|
285
|
-
},
|
|
286
282
|
"croner": {
|
|
287
283
|
"optional": true
|
|
288
284
|
},
|
package/types/index.d.ts
CHANGED
|
@@ -72,6 +72,91 @@ export interface AkanMobileConfig extends AkanCapacitorLikeConfig {
|
|
|
72
72
|
buildNum: number;
|
|
73
73
|
targets: Record<string, AkanMobileTargetConfig>;
|
|
74
74
|
}
|
|
75
|
+
export interface PluginRuntimeContext {
|
|
76
|
+
readonly appName: string;
|
|
77
|
+
readonly mobile: AkanMobileConfig;
|
|
78
|
+
/** True when any mobile target declares the given permission. */
|
|
79
|
+
hasMobilePermission(permission: MobilePermission): boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface AkanScanInfo {
|
|
82
|
+
/** Transitive lib dependencies of the app (or direct deps of a lib). */
|
|
83
|
+
getLibs(): string[];
|
|
84
|
+
getDatabaseModules(): string[];
|
|
85
|
+
getServiceModules(): string[];
|
|
86
|
+
getScalarModules(): string[];
|
|
87
|
+
}
|
|
88
|
+
export interface AkanExecutor {
|
|
89
|
+
readonly name: string;
|
|
90
|
+
readonly type: "app" | "lib";
|
|
91
|
+
readonly cwdPath: string;
|
|
92
|
+
getPath(rel: string): string;
|
|
93
|
+
exists(rel: string): Promise<boolean>;
|
|
94
|
+
readFile(rel: string): Promise<string>;
|
|
95
|
+
writeFile(rel: string, content: string, opts?: {
|
|
96
|
+
overwrite?: boolean;
|
|
97
|
+
silent?: boolean;
|
|
98
|
+
}): Promise<unknown>;
|
|
99
|
+
mkdir(rel: string): Promise<unknown>;
|
|
100
|
+
removeDir(rel: string): Promise<unknown>;
|
|
101
|
+
cp(src: string, dest: string): Promise<void>;
|
|
102
|
+
readdir(rel: string): Promise<string[]>;
|
|
103
|
+
getFilesAndDirs(rel: string): Promise<{
|
|
104
|
+
files: string[];
|
|
105
|
+
dirs: string[];
|
|
106
|
+
}>;
|
|
107
|
+
scan(options?: {
|
|
108
|
+
refresh?: boolean;
|
|
109
|
+
write?: boolean;
|
|
110
|
+
}): Promise<AkanScanInfo>;
|
|
111
|
+
}
|
|
112
|
+
export interface AkanSyncContext {
|
|
113
|
+
readonly appName: string;
|
|
114
|
+
readonly appPath: string;
|
|
115
|
+
/** The full app/lib executor for advanced scan-result access and file operations. */
|
|
116
|
+
readonly executor: AkanExecutor;
|
|
117
|
+
getPath(rel: string): string;
|
|
118
|
+
fileExists(rel: string): Promise<boolean>;
|
|
119
|
+
writeFile(rel: string, content: string, opts?: {
|
|
120
|
+
overwrite?: boolean;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
/** Resolves `env/env.client.ts` and returns its exported `env`, or null when absent/invalid. */
|
|
123
|
+
readEnvClient(): Promise<Record<string, unknown> | null>;
|
|
124
|
+
}
|
|
125
|
+
export interface AkanNativeContext {
|
|
126
|
+
readonly appPath: string;
|
|
127
|
+
/** The app executor for advanced scan-result access and file operations. */
|
|
128
|
+
readonly executor: AkanExecutor;
|
|
129
|
+
readonly target: AkanMobileTargetConfig;
|
|
130
|
+
readonly operation: "local" | "release";
|
|
131
|
+
readonly env: MobileEnv;
|
|
132
|
+
/** Set NS-prefixed usage descriptions in the iOS Info.plist (Debug + Release). */
|
|
133
|
+
setIosUsageDescriptions(descriptions: Record<string, string>): Promise<void>;
|
|
134
|
+
/** Merge raw key/values into the iOS Info.plist (Debug + Release). */
|
|
135
|
+
updateIosInfoPlist(values: Record<string, unknown>): Promise<void>;
|
|
136
|
+
/** Contribute entries to the iOS entitlements file (merged, then written once per prepare). */
|
|
137
|
+
addIosEntitlements(entitlements: Record<string, string | string[]>): void;
|
|
138
|
+
/** Transform `ios/App/App/AppDelegate.swift` in place (no-op when the file is absent). */
|
|
139
|
+
editIosAppDelegate(transform: (content: string) => string): Promise<void>;
|
|
140
|
+
/** Add `uses-permission` entries to the Android manifest (without the `android.permission.` prefix). */
|
|
141
|
+
addAndroidPermissions(permissions: string[]): void;
|
|
142
|
+
/** Add `uses-feature` entries to the Android manifest. */
|
|
143
|
+
addAndroidFeatures(features: string[]): void;
|
|
144
|
+
}
|
|
145
|
+
export interface AkanPluginCapacitorConfig {
|
|
146
|
+
/** Mobile permission that activates this plugin's native config (reuses the existing permission model). */
|
|
147
|
+
permission?: MobilePermission;
|
|
148
|
+
/** Imperative native (Capacitor) project configuration. */
|
|
149
|
+
configureNative?: (ctx: AkanNativeContext) => Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
export interface AkanPlugin {
|
|
152
|
+
name: string;
|
|
153
|
+
/** Runtime npm packages this plugin needs; installed on demand by the CLI (e.g. firebase for push). */
|
|
154
|
+
runtimePackages?: (ctx: PluginRuntimeContext) => string[];
|
|
155
|
+
/** Native (Capacitor) project configuration. */
|
|
156
|
+
capacitor?: AkanPluginCapacitorConfig;
|
|
157
|
+
/** Build-time asset generation (e.g. `public/firebase-messaging-sw.js`). */
|
|
158
|
+
syncAssets?: (ctx: AkanSyncContext) => Promise<void>;
|
|
159
|
+
}
|
|
75
160
|
export interface AppConfigResult {
|
|
76
161
|
docker: DockerConfig;
|
|
77
162
|
defaultDatabaseMode: DatabaseMode;
|
|
@@ -99,8 +184,14 @@ export interface LibConfigContext {
|
|
|
99
184
|
readonly name: string;
|
|
100
185
|
readonly type: "lib";
|
|
101
186
|
}
|
|
102
|
-
export type
|
|
103
|
-
|
|
187
|
+
export type AppConfigInput = DeepPartial<AppConfigResult> & {
|
|
188
|
+
plugins?: AkanPlugin[];
|
|
189
|
+
};
|
|
190
|
+
export type LibConfigInput = DeepPartial<LibConfigResult> & {
|
|
191
|
+
plugins?: AkanPlugin[];
|
|
192
|
+
};
|
|
193
|
+
export type AppConfig = AppConfigInput | ((app: AppConfigContext) => AppConfigInput);
|
|
194
|
+
export type LibConfig = LibConfigInput | ((lib: LibConfigContext) => LibConfigInput);
|
|
104
195
|
export type AkanConfigFile = object;
|
|
105
196
|
export interface FileConventionScanResult {
|
|
106
197
|
constant: {
|
package/types/webkit/index.d.ts
CHANGED
|
@@ -3,15 +3,10 @@ export { createRobotPage } from "./createRobotPage.d.ts";
|
|
|
3
3
|
export { createSitemapPage } from "./createSitemapPage.d.ts";
|
|
4
4
|
export { lazy } from "./lazy.d.ts";
|
|
5
5
|
export type * from "./types.d.ts";
|
|
6
|
-
export { useCamera } from "./useCamera.d.ts";
|
|
7
|
-
export { useCodepush } from "./useCodepush.d.ts";
|
|
8
|
-
export { useContact } from "./useContact.d.ts";
|
|
9
6
|
export { useCsrValues } from "./useCsrValues.d.ts";
|
|
10
7
|
export { useDebounce } from "./useDebounce.d.ts";
|
|
11
8
|
export { useFetch, useFetchFn } from "./useFetch.d.ts";
|
|
12
|
-
export { useGeoLocation } from "./useGeoLocation.d.ts";
|
|
13
9
|
export { useHistory } from "./useHistory.d.ts";
|
|
14
10
|
export { useInterval } from "./useInterval.d.ts";
|
|
15
11
|
export { useLocation } from "./useLocation.d.ts";
|
|
16
|
-
export { initPushNotificationClickBridge, type PushNotificationPermission, type PushNotificationPlatform, type PushNotificationProvider, type PushToken, usePushNotification, } from "./usePushNotification.d.ts";
|
|
17
12
|
export { useThrottle } from "./useThrottle.d.ts";
|
package/webkit/index.ts
CHANGED
|
@@ -3,22 +3,10 @@ export { createRobotPage } from "./createRobotPage";
|
|
|
3
3
|
export { createSitemapPage } from "./createSitemapPage";
|
|
4
4
|
export { lazy } from "./lazy";
|
|
5
5
|
export type * from "./types";
|
|
6
|
-
export { useCamera } from "./useCamera";
|
|
7
|
-
export { useCodepush } from "./useCodepush";
|
|
8
|
-
export { useContact } from "./useContact";
|
|
9
6
|
export { useCsrValues } from "./useCsrValues";
|
|
10
7
|
export { useDebounce } from "./useDebounce";
|
|
11
8
|
export { useFetch, useFetchFn } from "./useFetch";
|
|
12
|
-
export { useGeoLocation } from "./useGeoLocation";
|
|
13
9
|
export { useHistory } from "./useHistory";
|
|
14
10
|
export { useInterval } from "./useInterval";
|
|
15
11
|
export { useLocation } from "./useLocation";
|
|
16
|
-
export {
|
|
17
|
-
initPushNotificationClickBridge,
|
|
18
|
-
type PushNotificationPermission,
|
|
19
|
-
type PushNotificationPlatform,
|
|
20
|
-
type PushNotificationProvider,
|
|
21
|
-
type PushToken,
|
|
22
|
-
usePushNotification,
|
|
23
|
-
} from "./usePushNotification";
|
|
24
12
|
export { useThrottle } from "./useThrottle";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type CapacitorPermissionState } from "akanjs/client/capacitor";
|
|
2
|
-
type PermissionStatus = {
|
|
3
|
-
camera: CapacitorPermissionState;
|
|
4
|
-
photos: CapacitorPermissionState;
|
|
5
|
-
};
|
|
6
|
-
/** Capacitor camera/photos hook with permission checks and app-settings fallback. */
|
|
7
|
-
export declare const useCamera: () => {
|
|
8
|
-
permissions: PermissionStatus;
|
|
9
|
-
getPhoto: (src?: "prompt" | "camera" | "photos") => Promise<{
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
dataUrl?: string;
|
|
12
|
-
} | undefined>;
|
|
13
|
-
pickImage: () => Promise<{
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
photos: unknown[];
|
|
16
|
-
}>;
|
|
17
|
-
checkPermission: (type: "photos" | "camera" | "all") => Promise<void>;
|
|
18
|
-
};
|
|
19
|
-
export {};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ProtoAppInfo, ProtoFile } from "akanjs/constant";
|
|
2
|
-
export declare const useCodepush: ({ serverUrl }: {
|
|
3
|
-
serverUrl: string;
|
|
4
|
-
}) => {
|
|
5
|
-
update: boolean;
|
|
6
|
-
version: string;
|
|
7
|
-
initialize: () => Promise<void>;
|
|
8
|
-
checkNewRelease: () => Promise<{
|
|
9
|
-
release: ProtoAppInfo & {
|
|
10
|
-
appBuild: string;
|
|
11
|
-
};
|
|
12
|
-
bundleFile: ProtoFile;
|
|
13
|
-
} | undefined>;
|
|
14
|
-
codepush: () => Promise<void>;
|
|
15
|
-
statManager: () => Promise<void>;
|
|
16
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type CapacitorPermissionState } from "akanjs/client/capacitor";
|
|
2
|
-
type PermissionStatus = {
|
|
3
|
-
contacts: CapacitorPermissionState;
|
|
4
|
-
};
|
|
5
|
-
/** Capacitor contacts hook with permission checks and contact loading helpers. */
|
|
6
|
-
export declare const useContact: () => {
|
|
7
|
-
permissions: PermissionStatus;
|
|
8
|
-
getContacts: () => Promise<unknown[]>;
|
|
9
|
-
checkPermission: () => Promise<void>;
|
|
10
|
-
};
|
|
11
|
-
export {};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/** Capacitor geolocation hook with permission checks and current position lookup. */
|
|
2
|
-
export declare const useGeoLocation: () => {
|
|
3
|
-
checkPermission: () => Promise<{
|
|
4
|
-
geolocation: string;
|
|
5
|
-
coarseLocation: string;
|
|
6
|
-
}>;
|
|
7
|
-
getPosition: () => Promise<unknown>;
|
|
8
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import "cordova-plugin-purchase/www/store";
|
|
2
|
-
export type PlatformType = "android" | "ios" | "all";
|
|
3
|
-
export interface ProductType {
|
|
4
|
-
id: string;
|
|
5
|
-
type: keyof typeof CdvPurchase.ProductType;
|
|
6
|
-
}
|
|
7
|
-
export type CdvProductType = CdvPurchase.ProductType;
|
|
8
|
-
export declare const usePurchase: ({ platform, productInfo, url, onPay, onSubscribe, }: {
|
|
9
|
-
platform: PlatformType;
|
|
10
|
-
productInfo: ProductType[];
|
|
11
|
-
url: string;
|
|
12
|
-
onPay?: (transaction: CdvPurchase.Transaction) => void | Promise<void>;
|
|
13
|
-
onSubscribe?: (transaction: CdvPurchase.Transaction) => void | Promise<void>;
|
|
14
|
-
}) => {
|
|
15
|
-
isLoading: boolean;
|
|
16
|
-
products: CdvPurchase.Product[];
|
|
17
|
-
purchaseProduct: (product: CdvPurchase.Product) => Promise<void>;
|
|
18
|
-
restorePurchases: () => Promise<void>;
|
|
19
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type PushNotificationPlatform = "web" | "ios" | "android";
|
|
2
|
-
export type PushNotificationProvider = "fcm";
|
|
3
|
-
export interface FirebaseOptions {
|
|
4
|
-
apiKey?: string;
|
|
5
|
-
authDomain?: string;
|
|
6
|
-
databaseURL?: string;
|
|
7
|
-
projectId?: string;
|
|
8
|
-
storageBucket?: string;
|
|
9
|
-
messagingSenderId?: string;
|
|
10
|
-
appId?: string;
|
|
11
|
-
measurementId?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface PushToken {
|
|
14
|
-
token: string;
|
|
15
|
-
platform: PushNotificationPlatform;
|
|
16
|
-
provider: PushNotificationProvider;
|
|
17
|
-
deviceId?: string;
|
|
18
|
-
}
|
|
19
|
-
export type PushNotificationPermission = "prompt" | "granted" | "denied" | string;
|
|
20
|
-
export interface PushNotificationClientEnv {
|
|
21
|
-
firebase?: FirebaseOptions & {
|
|
22
|
-
vapidKey?: string;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
declare global {
|
|
26
|
-
var __AKAN_PUSH_CLICK_BRIDGE__: Promise<boolean> | undefined;
|
|
27
|
-
var __AKAN_CLIENT_ENV__: PushNotificationClientEnv | undefined;
|
|
28
|
-
}
|
|
29
|
-
export declare const initPushNotificationClickBridge: () => Promise<boolean>;
|
|
30
|
-
export declare const usePushNotification: () => {
|
|
31
|
-
isSupported: () => Promise<boolean>;
|
|
32
|
-
getPermission: () => Promise<PushNotificationPermission>;
|
|
33
|
-
requestPermission: () => Promise<PushNotificationPermission>;
|
|
34
|
-
register: () => Promise<PushToken | undefined>;
|
|
35
|
-
getToken: () => Promise<PushToken | undefined>;
|
|
36
|
-
initClickBridge: () => Promise<boolean>;
|
|
37
|
-
};
|
package/webkit/useCamera.tsx
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { Device, isMobileDevice } from "akanjs/client";
|
|
3
|
-
import { type CapacitorPermissionState, loadCapacitorCamera } from "akanjs/client/capacitor";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
|
-
|
|
6
|
-
type PermissionStatus = {
|
|
7
|
-
camera: CapacitorPermissionState;
|
|
8
|
-
photos: CapacitorPermissionState;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/** Capacitor camera/photos hook with permission checks and app-settings fallback. */
|
|
12
|
-
export const useCamera = () => {
|
|
13
|
-
const [permissions, setPermissions] = useState<PermissionStatus>({ camera: "prompt", photos: "prompt" });
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 최초로 킬 경우 권한은 prompt 상태이다.
|
|
17
|
-
* prompt 상태일 경우 권한을 요청한다.
|
|
18
|
-
* 권한이 denied 상태일 경우 설정으로 이동한다.
|
|
19
|
-
* 이후 state의 permission을 업데이트해야한다.
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
const checkPermission = async (type: "photos" | "camera" | "all") => {
|
|
23
|
-
try {
|
|
24
|
-
const { Camera } = await loadCapacitorCamera();
|
|
25
|
-
if (type === "photos") {
|
|
26
|
-
if (permissions.photos === "prompt") {
|
|
27
|
-
const { photos } = await Camera.requestPermissions();
|
|
28
|
-
setPermissions((prev) => ({ ...prev, photos }));
|
|
29
|
-
} else if (permissions.photos === "denied") {
|
|
30
|
-
location.assign("app-settings:");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
} else if (type === "camera") {
|
|
34
|
-
if (permissions.camera === "prompt") {
|
|
35
|
-
const { camera } = await Camera.requestPermissions();
|
|
36
|
-
setPermissions((prev) => ({ ...prev, camera }));
|
|
37
|
-
} else if (permissions.camera === "denied") {
|
|
38
|
-
location.assign("app-settings:");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
if (permissions.camera === "prompt" || permissions.photos === "prompt") {
|
|
43
|
-
const permissions = await Camera.requestPermissions();
|
|
44
|
-
setPermissions(permissions);
|
|
45
|
-
} else if (permissions.camera === "denied" || permissions.photos === "denied") {
|
|
46
|
-
location.assign("app-settings:");
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const getPhoto = async (src: "prompt" | "camera" | "photos" = "prompt") => {
|
|
55
|
-
const { Camera, CameraResultType, CameraSource } = await loadCapacitorCamera();
|
|
56
|
-
const source =
|
|
57
|
-
Device.getDevice().info.platform !== "web"
|
|
58
|
-
? src === "prompt"
|
|
59
|
-
? CameraSource.Prompt
|
|
60
|
-
: src === "camera"
|
|
61
|
-
? CameraSource.Camera
|
|
62
|
-
: CameraSource.Photos
|
|
63
|
-
: CameraSource.Photos;
|
|
64
|
-
const permission = src === "prompt" ? "all" : src === "camera" ? "camera" : "photos";
|
|
65
|
-
await checkPermission(permission);
|
|
66
|
-
try {
|
|
67
|
-
const photo = await Camera.getPhoto({
|
|
68
|
-
quality: 100,
|
|
69
|
-
source,
|
|
70
|
-
allowEditing: false,
|
|
71
|
-
resultType: CameraResultType.DataUrl,
|
|
72
|
-
promptLabelHeader: "프로필 사진을 올려주세요",
|
|
73
|
-
promptLabelPhoto: "앨범에서 선택하기",
|
|
74
|
-
promptLabelPicture: "사진 찍기",
|
|
75
|
-
promptLabelCancel: "취소",
|
|
76
|
-
});
|
|
77
|
-
return photo;
|
|
78
|
-
} catch (e) {
|
|
79
|
-
if (e === "User cancelled photos app") return;
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const pickImage = async () => {
|
|
84
|
-
await checkPermission("photos");
|
|
85
|
-
const { Camera } = await loadCapacitorCamera();
|
|
86
|
-
const photo = await Camera.pickImages({
|
|
87
|
-
quality: 90,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return photo;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
void (async () => {
|
|
95
|
-
if (isMobileDevice()) {
|
|
96
|
-
const { Camera } = await loadCapacitorCamera();
|
|
97
|
-
const permissions = await Camera.checkPermissions();
|
|
98
|
-
setPermissions(permissions);
|
|
99
|
-
}
|
|
100
|
-
})();
|
|
101
|
-
}, []);
|
|
102
|
-
return { permissions, getPhoto, pickImage, checkPermission };
|
|
103
|
-
};
|
package/webkit/useCodepush.tsx
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { loadCapacitorApp, loadCapacitorDevice, loadCapacitorUpdater } from "akanjs/client/capacitor";
|
|
3
|
-
import { HttpClient, mergeVersion, splitVersion } from "akanjs/common";
|
|
4
|
-
import type { ProtoAppInfo, ProtoFile } from "akanjs/constant";
|
|
5
|
-
import { useState } from "react";
|
|
6
|
-
|
|
7
|
-
export const useCodepush = ({ serverUrl }: { serverUrl: string }) => {
|
|
8
|
-
const [update, setUpdate] = useState(false);
|
|
9
|
-
const [version, setVersion] = useState("");
|
|
10
|
-
|
|
11
|
-
const initialize = async () => {
|
|
12
|
-
const { CapacitorUpdater } = await loadCapacitorUpdater();
|
|
13
|
-
await CapacitorUpdater.notifyAppReady();
|
|
14
|
-
};
|
|
15
|
-
const checkNewRelease = async () => {
|
|
16
|
-
const [{ App }, { Device }, { CapacitorUpdater }] = await Promise.all([
|
|
17
|
-
loadCapacitorApp(),
|
|
18
|
-
loadCapacitorDevice(),
|
|
19
|
-
loadCapacitorUpdater(),
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
const info = await Device.getInfo();
|
|
23
|
-
const app = await App.getInfo();
|
|
24
|
-
const pluginVersion = await CapacitorUpdater.getPluginVersion();
|
|
25
|
-
const { deviceId } = await CapacitorUpdater.getDeviceId();
|
|
26
|
-
const { bundle: version, native } = await CapacitorUpdater.current();
|
|
27
|
-
const builtInversion = await CapacitorUpdater.getBuiltinVersion();
|
|
28
|
-
const appId = app.id;
|
|
29
|
-
const platform = info.platform;
|
|
30
|
-
|
|
31
|
-
window.alert(
|
|
32
|
-
`getBuildinVersion:${builtInversion.version}\ncurrent.bundle:${version.version}\ncurrennt.native:${native}`,
|
|
33
|
-
);
|
|
34
|
-
/**
|
|
35
|
-
* "version_name": "builtin",
|
|
36
|
-
* "version_code": "1",
|
|
37
|
-
* "app_id": "com.lu.app",
|
|
38
|
-
* "plugin_version": "5.6.9",
|
|
39
|
-
* "version_build": "1.0",
|
|
40
|
-
* "is_prod": true,
|
|
41
|
-
* "version_os": "17.0.1",
|
|
42
|
-
* "is_emulator": true,
|
|
43
|
-
* "custom_id": "",
|
|
44
|
-
* "device_id": "C77000B1-7D28-4697-ADE0-74452F47C350",
|
|
45
|
-
* "platform": "ios",
|
|
46
|
-
* "defaultChannel": ""
|
|
47
|
-
*/
|
|
48
|
-
const { major, minor, patch } = splitVersion(version.version === "builtin" ? app.version : version.version);
|
|
49
|
-
const appName = process.env.AKAN_PUBLIC_APP_NAME ?? "";
|
|
50
|
-
|
|
51
|
-
const appInfo: ProtoAppInfo = {
|
|
52
|
-
appId,
|
|
53
|
-
appName,
|
|
54
|
-
deviceId: deviceId,
|
|
55
|
-
platform: platform as "ios" | "android",
|
|
56
|
-
branch: process.env.AKAN_PUBLIC_ENV ?? "debug",
|
|
57
|
-
isEmulator: info.isVirtual,
|
|
58
|
-
major: parseInt(major),
|
|
59
|
-
minor: parseInt(minor),
|
|
60
|
-
patch: parseInt(patch),
|
|
61
|
-
buildNum: app.build, //앱내 빌드시 버전 횟수 모르면 고한테 물어보기
|
|
62
|
-
versionOs: info.osVersion,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const url = serverUrl.replace("lu", "akasys");
|
|
66
|
-
const httpClient = new HttpClient(url);
|
|
67
|
-
const release = await httpClient.post<(ProtoAppInfo & { appBuild: string }) | null>("/release/codepush", {
|
|
68
|
-
data: { ...appInfo },
|
|
69
|
-
});
|
|
70
|
-
if (!release) return;
|
|
71
|
-
const file = await httpClient.get<ProtoFile>(`/file/file/${release.appBuild}`);
|
|
72
|
-
|
|
73
|
-
return { release: release, bundleFile: file };
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const codepush = async () => {
|
|
77
|
-
|
|
78
|
-
const newRelease = await checkNewRelease();
|
|
79
|
-
if (!newRelease) return;
|
|
80
|
-
const { release, bundleFile } = newRelease;
|
|
81
|
-
const { CapacitorUpdater } = await loadCapacitorUpdater();
|
|
82
|
-
setUpdate(true);
|
|
83
|
-
const bundle = await CapacitorUpdater.download({
|
|
84
|
-
url: bundleFile.url,
|
|
85
|
-
version: mergeVersion(release.major, release.minor, release.patch),
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
await CapacitorUpdater.set(bundle);
|
|
89
|
-
};
|
|
90
|
-
const getVersion = async () => {
|
|
91
|
-
const { CapacitorUpdater } = await loadCapacitorUpdater();
|
|
92
|
-
return await CapacitorUpdater.getBuiltinVersion();
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const statManager = async () => {
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
return { update, version, initialize, checkNewRelease, codepush, statManager };
|
|
99
|
-
};
|
package/webkit/useContact.tsx
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { Device } from "akanjs/client";
|
|
3
|
-
import { type CapacitorPermissionState, loadCapacitorContacts } from "akanjs/client/capacitor";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
|
-
|
|
6
|
-
type PermissionStatus = {
|
|
7
|
-
contacts: CapacitorPermissionState;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/** Capacitor contacts hook with permission checks and contact loading helpers. */
|
|
11
|
-
export const useContact = () => {
|
|
12
|
-
const [permissions, setPermissions] = useState<PermissionStatus>({ contacts: "prompt" });
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 최초로 킬 경우 권한은 prompt 상태이다.
|
|
16
|
-
* prompt 상태일 경우 권한을 요청한다.
|
|
17
|
-
* 권한이 denied 상태일 경우 설정으로 이동한다.
|
|
18
|
-
* 이후 state의 permission을 업데이트해야한다.
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
const checkPermission = async () => {
|
|
22
|
-
try {
|
|
23
|
-
const { Contacts } = await loadCapacitorContacts();
|
|
24
|
-
if (permissions.contacts === "prompt") {
|
|
25
|
-
const { contacts } = await Contacts.requestPermissions();
|
|
26
|
-
setPermissions((prev) => ({ ...prev, contacts }));
|
|
27
|
-
} else if (permissions.contacts === "denied") {
|
|
28
|
-
location.assign("app-settings:");
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
} catch {
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const getContacts = async () => {
|
|
36
|
-
await checkPermission();
|
|
37
|
-
const { Contacts } = await loadCapacitorContacts();
|
|
38
|
-
const { contacts } = await Contacts.getContacts({ projection: { name: true, phones: true } });
|
|
39
|
-
return contacts;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
void (async () => {
|
|
44
|
-
if (Device.getDevice().info.platform === "web") return;
|
|
45
|
-
const { Contacts } = await loadCapacitorContacts();
|
|
46
|
-
const permissions = await Contacts.checkPermissions();
|
|
47
|
-
setPermissions(permissions);
|
|
48
|
-
})();
|
|
49
|
-
}, []);
|
|
50
|
-
|
|
51
|
-
return { permissions, getContacts, checkPermission };
|
|
52
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { loadCapacitorGeolocation } from "akanjs/client/capacitor";
|
|
3
|
-
|
|
4
|
-
/** Capacitor geolocation hook with permission checks and current position lookup. */
|
|
5
|
-
export const useGeoLocation = () => {
|
|
6
|
-
const checkPermission = async (): Promise<{ geolocation: string; coarseLocation: string }> => {
|
|
7
|
-
const { Geolocation } = await loadCapacitorGeolocation();
|
|
8
|
-
const { location: geolocation, coarseLocation } = await Geolocation.requestPermissions();
|
|
9
|
-
return { geolocation, coarseLocation };
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const getPosition = async () => {
|
|
13
|
-
const { geolocation, coarseLocation } = await checkPermission();
|
|
14
|
-
if (geolocation === "denied" || coarseLocation === "denied") {
|
|
15
|
-
location.assign("app-settings:");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const { Geolocation } = await loadCapacitorGeolocation();
|
|
19
|
-
const coordinates = await Geolocation.getCurrentPosition();
|
|
20
|
-
return coordinates;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return { checkPermission, getPosition };
|
|
24
|
-
};
|
package/webkit/usePurchase.tsx
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import "cordova-plugin-purchase/www/store";
|
|
4
|
-
|
|
5
|
-
import { loadCapacitorApp } from "akanjs/client/capacitor";
|
|
6
|
-
import { useEffect, useRef, useState } from "react";
|
|
7
|
-
|
|
8
|
-
export type PlatformType = "android" | "ios" | "all";
|
|
9
|
-
export interface ProductType {
|
|
10
|
-
id: string;
|
|
11
|
-
type: keyof typeof CdvPurchase.ProductType;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type CdvProductType = CdvPurchase.ProductType;
|
|
15
|
-
|
|
16
|
-
export const usePurchase = ({
|
|
17
|
-
platform,
|
|
18
|
-
productInfo,
|
|
19
|
-
url,
|
|
20
|
-
onPay,
|
|
21
|
-
onSubscribe,
|
|
22
|
-
}: {
|
|
23
|
-
platform: PlatformType;
|
|
24
|
-
productInfo: ProductType[];
|
|
25
|
-
url: string;
|
|
26
|
-
onPay?: (transaction: CdvPurchase.Transaction) => void | Promise<void>;
|
|
27
|
-
onSubscribe?: (transaction: CdvPurchase.Transaction) => void | Promise<void>;
|
|
28
|
-
}) => {
|
|
29
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
30
|
-
const billingRef = useRef<any>(null);
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
const init = async () => {
|
|
34
|
-
if (CdvPurchase.store.isReady) {
|
|
35
|
-
setIsLoading(false);
|
|
36
|
-
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const { App } = await loadCapacitorApp();
|
|
40
|
-
const app = await App.getInfo();
|
|
41
|
-
if (platform === "all")
|
|
42
|
-
CdvPurchase.store.register([
|
|
43
|
-
...productInfo.map((prouct) => ({
|
|
44
|
-
id: prouct.id,
|
|
45
|
-
platform: CdvPurchase.Platform.GOOGLE_PLAY,
|
|
46
|
-
type: CdvPurchase.ProductType[prouct.type],
|
|
47
|
-
})),
|
|
48
|
-
...productInfo.map((prouct) => ({
|
|
49
|
-
id: prouct.id,
|
|
50
|
-
platform: CdvPurchase.Platform.APPLE_APPSTORE,
|
|
51
|
-
type: CdvPurchase.ProductType[prouct.type],
|
|
52
|
-
})),
|
|
53
|
-
]);
|
|
54
|
-
else
|
|
55
|
-
CdvPurchase.store.register(
|
|
56
|
-
productInfo.map((product) => ({
|
|
57
|
-
id: product.id,
|
|
58
|
-
platform: platform === "android" ? CdvPurchase.Platform.GOOGLE_PLAY : CdvPurchase.Platform.APPLE_APPSTORE,
|
|
59
|
-
type: CdvPurchase.ProductType[product.type],
|
|
60
|
-
})),
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
await CdvPurchase.store.initialize([
|
|
64
|
-
{ platform: CdvPurchase.Platform.APPLE_APPSTORE, options: { needAppReceipt: false } },
|
|
65
|
-
{ platform: CdvPurchase.Platform.GOOGLE_PLAY },
|
|
66
|
-
]);
|
|
67
|
-
await CdvPurchase.store.update();
|
|
68
|
-
await CdvPurchase.store.restorePurchases();
|
|
69
|
-
|
|
70
|
-
CdvPurchase.store.validator = (async (
|
|
71
|
-
request: { id: string; transaction: { id: string; purchaseToken: string; appStoreReceipt: string } },
|
|
72
|
-
callback: (result: {
|
|
73
|
-
ok: boolean;
|
|
74
|
-
data: { id: string; latest_receipt: boolean; transaction: CdvPurchase.Transaction };
|
|
75
|
-
}) => void,
|
|
76
|
-
) => {
|
|
77
|
-
const transactionId = request.transaction.id;
|
|
78
|
-
const transactions = CdvPurchase.store.localTransactions;
|
|
79
|
-
const verifingTransaction = transactions.find((transaction) => transaction.transactionId === transactionId);
|
|
80
|
-
|
|
81
|
-
if (verifingTransaction?.state !== "approved") return;
|
|
82
|
-
|
|
83
|
-
const billing = await fetch(`${url}/billing/verifyBilling`, {
|
|
84
|
-
method: "POST",
|
|
85
|
-
headers: {
|
|
86
|
-
"Content-Type": "application/json",
|
|
87
|
-
},
|
|
88
|
-
body: JSON.stringify({
|
|
89
|
-
data: {
|
|
90
|
-
platform: verifingTransaction.platform === CdvPurchase.Platform.GOOGLE_PLAY ? "google" : "apple",
|
|
91
|
-
packageName: app.id,
|
|
92
|
-
productId: verifingTransaction.products[0].id,
|
|
93
|
-
receipt:
|
|
94
|
-
verifingTransaction.platform === CdvPurchase.Platform.GOOGLE_PLAY
|
|
95
|
-
? request.transaction.purchaseToken
|
|
96
|
-
: request.transaction.appStoreReceipt,
|
|
97
|
-
transactionId: verifingTransaction.transactionId,
|
|
98
|
-
},
|
|
99
|
-
}),
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
billingRef.current = billing.json();
|
|
103
|
-
|
|
104
|
-
callback({
|
|
105
|
-
|
|
106
|
-
ok: !!billing,
|
|
107
|
-
|
|
108
|
-
data: { id: request.id, latest_receipt: true, transaction: request.transaction } as any,
|
|
109
|
-
});
|
|
110
|
-
}) as any;
|
|
111
|
-
if (CdvPurchase.store.localReceipts.length > 0) {
|
|
112
|
-
CdvPurchase.store.localReceipts.forEach((receipt) => {
|
|
113
|
-
if (receipt.platform === CdvPurchase.Platform.GOOGLE_PLAY)
|
|
114
|
-
if (receipt.transactions[0].state === CdvPurchase.TransactionState.APPROVED)
|
|
115
|
-
void receipt.transactions[0].verify();
|
|
116
|
-
else void receipt.transactions[0].finish();
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
CdvPurchase.store
|
|
121
|
-
.when()
|
|
122
|
-
.approved((transaction) => {
|
|
123
|
-
void transaction.verify();
|
|
124
|
-
})
|
|
125
|
-
.verified((receipt) => {
|
|
126
|
-
void receipt.finish();
|
|
127
|
-
})
|
|
128
|
-
.finished((transaction) => {
|
|
129
|
-
void inAppPurchase(transaction);
|
|
130
|
-
});
|
|
131
|
-
setIsLoading(false);
|
|
132
|
-
};
|
|
133
|
-
void init();
|
|
134
|
-
}, []);
|
|
135
|
-
const purchaseProduct = async (product: CdvPurchase.Product) => {
|
|
136
|
-
await product.getOffer()?.order();
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const restorePurchases = async () => {
|
|
140
|
-
await CdvPurchase.store.restorePurchases();
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const inAppPurchase = async (transaction: CdvPurchase.Transaction) => {
|
|
144
|
-
const product = CdvPurchase.store.get(transaction.products[0].id);
|
|
145
|
-
if (product?.type === "consumable") await onPay?.(transaction);
|
|
146
|
-
else await onSubscribe?.(transaction);
|
|
147
|
-
await transaction.finish();
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
isLoading,
|
|
152
|
-
products: CdvPurchase.store.products,
|
|
153
|
-
purchaseProduct,
|
|
154
|
-
restorePurchases,
|
|
155
|
-
};
|
|
156
|
-
};
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { router } from "akanjs/client";
|
|
4
|
-
import { loadCapacitorDevice, loadCapacitorFcm, loadCapacitorPushNotifications } from "akanjs/client/capacitor";
|
|
5
|
-
import { useEffect } from "react";
|
|
6
|
-
|
|
7
|
-
export type PushNotificationPlatform = "web" | "ios" | "android";
|
|
8
|
-
export type PushNotificationProvider = "fcm";
|
|
9
|
-
|
|
10
|
-
export interface FirebaseOptions {
|
|
11
|
-
apiKey?: string;
|
|
12
|
-
authDomain?: string;
|
|
13
|
-
databaseURL?: string;
|
|
14
|
-
projectId?: string;
|
|
15
|
-
storageBucket?: string;
|
|
16
|
-
messagingSenderId?: string;
|
|
17
|
-
appId?: string;
|
|
18
|
-
measurementId?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface PushToken {
|
|
22
|
-
token: string;
|
|
23
|
-
platform: PushNotificationPlatform;
|
|
24
|
-
provider: PushNotificationProvider;
|
|
25
|
-
deviceId?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type PushNotificationPermission = "prompt" | "granted" | "denied" | string;
|
|
29
|
-
|
|
30
|
-
export interface PushNotificationClientEnv {
|
|
31
|
-
firebase?: FirebaseOptions & {
|
|
32
|
-
vapidKey?: string;
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
declare global {
|
|
37
|
-
|
|
38
|
-
var __AKAN_PUSH_CLICK_BRIDGE__: Promise<boolean> | undefined;
|
|
39
|
-
|
|
40
|
-
var __AKAN_CLIENT_ENV__: PushNotificationClientEnv | undefined;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const getClientEnv = () => globalThis.__AKAN_CLIENT_ENV__;
|
|
44
|
-
|
|
45
|
-
const getFirebaseConfig = () => getClientEnv()?.firebase;
|
|
46
|
-
|
|
47
|
-
const firebaseAppPackage = "firebase/app";
|
|
48
|
-
const firebaseMessagingPackage = "firebase/messaging";
|
|
49
|
-
|
|
50
|
-
const normalizePlatform = (platform: string): PushNotificationPlatform | null => {
|
|
51
|
-
if (platform === "web" || platform === "ios" || platform === "android") return platform;
|
|
52
|
-
return null;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const isWebRuntime = () => typeof window !== "undefined" && typeof navigator !== "undefined";
|
|
56
|
-
|
|
57
|
-
const isInternalDeepLink = (url: string) => {
|
|
58
|
-
if (url.startsWith("/") && !url.startsWith("//")) return true;
|
|
59
|
-
if (!isWebRuntime()) return false;
|
|
60
|
-
try {
|
|
61
|
-
const parsed = new URL(url, window.location.origin);
|
|
62
|
-
return parsed.origin === window.location.origin;
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const enterDeepLink = (url: string) => {
|
|
69
|
-
if (!isInternalDeepLink(url)) return false;
|
|
70
|
-
const parsed = new URL(url, isWebRuntime() ? window.location.origin : "http://localhost");
|
|
71
|
-
return router.enterDeepLink(`${parsed.pathname}${parsed.search}${parsed.hash}`);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const getNativePlatform = async () => {
|
|
75
|
-
const { Device } = await loadCapacitorDevice();
|
|
76
|
-
const device = await Device.getInfo();
|
|
77
|
-
return normalizePlatform(device.platform);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
81
|
-
|
|
82
|
-
const getNativeToken = async (options?: { retries?: number }): Promise<PushToken | undefined> => {
|
|
83
|
-
const [{ FCM }, platform] = await Promise.all([loadCapacitorFcm(), getNativePlatform()]);
|
|
84
|
-
if (!platform || platform === "web") return undefined;
|
|
85
|
-
|
|
86
|
-
const retries = options?.retries ?? 0;
|
|
87
|
-
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
88
|
-
const { token } = await FCM.getToken();
|
|
89
|
-
if (token) return { token, platform, provider: "fcm" };
|
|
90
|
-
if (attempt < retries) await sleep(500 * (attempt + 1));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return undefined;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const getWebToken = async (): Promise<PushToken | undefined> => {
|
|
97
|
-
if (!isWebRuntime() || !("serviceWorker" in navigator)) return undefined;
|
|
98
|
-
const firebaseConfig = getFirebaseConfig();
|
|
99
|
-
if (
|
|
100
|
-
!firebaseConfig?.apiKey ||
|
|
101
|
-
!firebaseConfig.projectId ||
|
|
102
|
-
!firebaseConfig.messagingSenderId ||
|
|
103
|
-
!firebaseConfig.appId
|
|
104
|
-
) {
|
|
105
|
-
return undefined;
|
|
106
|
-
}
|
|
107
|
-
const [{ getApps, initializeApp }, { getToken: getFirebaseToken, getMessaging }] = await Promise.all([
|
|
108
|
-
null as unknown as typeof import("firebase/app"), //! temporary disabled
|
|
109
|
-
null as unknown as typeof import("firebase/messaging"), //@ temporary disabled
|
|
110
|
-
]);
|
|
111
|
-
const firebase = getApps()[0] ?? initializeApp(firebaseConfig);
|
|
112
|
-
const messaging = getMessaging(firebase);
|
|
113
|
-
const serviceWorkerRegistration = await navigator.serviceWorker.register("/firebase-messaging-sw.js");
|
|
114
|
-
const token = await getFirebaseToken(messaging, {
|
|
115
|
-
vapidKey: firebaseConfig.vapidKey,
|
|
116
|
-
serviceWorkerRegistration,
|
|
117
|
-
});
|
|
118
|
-
if (!token) return undefined;
|
|
119
|
-
return { token, platform: "web", provider: "fcm" };
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const getPushUrlFromNativeEvent = (event: { notification?: { data?: Record<string, unknown> } }) => {
|
|
123
|
-
const data = event.notification?.data;
|
|
124
|
-
const fcmMessage = data?.FCM_MSG as { data?: { url?: unknown }; fcmOptions?: { link?: unknown } } | undefined;
|
|
125
|
-
const directUrl = data?.url;
|
|
126
|
-
const nestedUrl = fcmMessage?.data?.url ?? fcmMessage?.fcmOptions?.link;
|
|
127
|
-
return typeof directUrl === "string" ? directUrl : typeof nestedUrl === "string" ? nestedUrl : undefined;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const waitForNativeRegistration = async (
|
|
131
|
-
PushNotifications: Awaited<ReturnType<typeof loadCapacitorPushNotifications>>["PushNotifications"],
|
|
132
|
-
): Promise<string | undefined> => {
|
|
133
|
-
let registrationHandle: { remove?: () => Promise<void> | void } | void;
|
|
134
|
-
let errorHandle: { remove?: () => Promise<void> | void } | void;
|
|
135
|
-
|
|
136
|
-
const cleanup = async () => {
|
|
137
|
-
await registrationHandle?.remove?.();
|
|
138
|
-
await errorHandle?.remove?.();
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return await new Promise<string | undefined>((resolve) => {
|
|
142
|
-
let settled = false;
|
|
143
|
-
const finish = (token?: string) => {
|
|
144
|
-
if (settled) return;
|
|
145
|
-
settled = true;
|
|
146
|
-
clearTimeout(timeout);
|
|
147
|
-
void cleanup();
|
|
148
|
-
resolve(token);
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const timeout = setTimeout(() => finish(), 8000);
|
|
152
|
-
|
|
153
|
-
Promise.resolve(
|
|
154
|
-
PushNotifications.addListener("registration", (event) => {
|
|
155
|
-
finish(typeof event.value === "string" ? event.value : undefined);
|
|
156
|
-
}),
|
|
157
|
-
).then((handle) => {
|
|
158
|
-
registrationHandle = handle;
|
|
159
|
-
});
|
|
160
|
-
Promise.resolve(PushNotifications.addListener("registrationError", () => finish())).then((handle) => {
|
|
161
|
-
errorHandle = handle;
|
|
162
|
-
});
|
|
163
|
-
Promise.resolve(PushNotifications.register()).catch(() => finish());
|
|
164
|
-
});
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export const initPushNotificationClickBridge = async () => {
|
|
168
|
-
if (globalThis.__AKAN_PUSH_CLICK_BRIDGE__) return await globalThis.__AKAN_PUSH_CLICK_BRIDGE__;
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
const platform = await getNativePlatform();
|
|
172
|
-
if (!platform || platform === "web") return true;
|
|
173
|
-
|
|
174
|
-
globalThis.__AKAN_PUSH_CLICK_BRIDGE__ = (async () => {
|
|
175
|
-
const { PushNotifications } = await loadCapacitorPushNotifications();
|
|
176
|
-
await PushNotifications.addListener("pushNotificationActionPerformed", (event) => {
|
|
177
|
-
const url = getPushUrlFromNativeEvent(event);
|
|
178
|
-
if (!url) return;
|
|
179
|
-
try {
|
|
180
|
-
enterDeepLink(url);
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
return true;
|
|
185
|
-
})();
|
|
186
|
-
|
|
187
|
-
return await globalThis.__AKAN_PUSH_CLICK_BRIDGE__;
|
|
188
|
-
} catch {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
export const usePushNotification = () => {
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
void initPushNotificationClickBridge();
|
|
196
|
-
}, []);
|
|
197
|
-
|
|
198
|
-
const isSupported = async () => {
|
|
199
|
-
if (!isWebRuntime()) return false;
|
|
200
|
-
try {
|
|
201
|
-
const platform = await getNativePlatform();
|
|
202
|
-
if (platform && platform !== "web") {
|
|
203
|
-
await Promise.all([loadCapacitorFcm(), loadCapacitorPushNotifications()]);
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
} catch {
|
|
207
|
-
}
|
|
208
|
-
return Boolean(getFirebaseConfig()?.apiKey && "Notification" in window && "serviceWorker" in navigator);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const getPermission = async (): Promise<PushNotificationPermission> => {
|
|
212
|
-
try {
|
|
213
|
-
const platform = await getNativePlatform();
|
|
214
|
-
if (platform && platform !== "web") {
|
|
215
|
-
const { PushNotifications } = await loadCapacitorPushNotifications();
|
|
216
|
-
const { receive } = await PushNotifications.checkPermissions();
|
|
217
|
-
return receive;
|
|
218
|
-
}
|
|
219
|
-
} catch {
|
|
220
|
-
}
|
|
221
|
-
if (!isWebRuntime() || !("Notification" in window)) return "denied";
|
|
222
|
-
return Notification.permission;
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const requestPermission = async (): Promise<PushNotificationPermission> => {
|
|
226
|
-
try {
|
|
227
|
-
const platform = await getNativePlatform();
|
|
228
|
-
if (platform && platform !== "web") {
|
|
229
|
-
const { PushNotifications } = await loadCapacitorPushNotifications();
|
|
230
|
-
const { receive } = await PushNotifications.requestPermissions();
|
|
231
|
-
return receive;
|
|
232
|
-
}
|
|
233
|
-
} catch {
|
|
234
|
-
}
|
|
235
|
-
if (!isWebRuntime() || !("Notification" in window)) return "denied";
|
|
236
|
-
return await Notification.requestPermission();
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const getToken = async () => {
|
|
240
|
-
try {
|
|
241
|
-
const platform = await getNativePlatform();
|
|
242
|
-
if (platform && platform !== "web") return await getNativeToken();
|
|
243
|
-
} catch {
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
return await getWebToken();
|
|
247
|
-
} catch {
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const register = async () => {
|
|
253
|
-
try {
|
|
254
|
-
const permission = await requestPermission();
|
|
255
|
-
|
|
256
|
-
const platform = await getNativePlatform().catch(() => null);
|
|
257
|
-
if (platform && platform !== "web") {
|
|
258
|
-
const [{ FCM }, { PushNotifications }] = await Promise.all([
|
|
259
|
-
loadCapacitorFcm(),
|
|
260
|
-
loadCapacitorPushNotifications(),
|
|
261
|
-
]);
|
|
262
|
-
await FCM.setAutoInit({ enabled: true });
|
|
263
|
-
|
|
264
|
-
if (platform === "android") {
|
|
265
|
-
return await getNativeToken({ retries: 5 });
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (permission !== "granted") return undefined;
|
|
269
|
-
await waitForNativeRegistration(PushNotifications);
|
|
270
|
-
return await getNativeToken({ retries: 5 });
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (permission !== "granted") return undefined;
|
|
274
|
-
return await getWebToken();
|
|
275
|
-
} catch {
|
|
276
|
-
return undefined;
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
isSupported,
|
|
282
|
-
getPermission,
|
|
283
|
-
requestPermission,
|
|
284
|
-
register,
|
|
285
|
-
getToken,
|
|
286
|
-
initClickBridge: initPushNotificationClickBridge,
|
|
287
|
-
};
|
|
288
|
-
};
|