@akanjs/next 0.0.52 → 0.0.54
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/bootCsr.js +138 -0
- package/{createNextMiddleware.ts → createNextMiddleware.js} +16 -12
- package/createRobotPage.js +15 -0
- package/createSitemapPage.js +7 -0
- package/index.js +41 -0
- package/lazy.js +6 -0
- package/makePageProto.js +114 -0
- package/package.json +3 -21
- package/types.js +0 -0
- package/{useCamera.tsx → useCamera.js} +20 -37
- package/useCodepush.js +74 -0
- package/{useContact.tsx → useContact.js} +10 -19
- package/{useCsrValues.ts → useCsrValues.js} +171 -188
- package/useDebounce.js +18 -0
- package/{useFetch.ts → useFetch.js} +6 -7
- package/{useGeoLocation.tsx → useGeoLocation.js} +4 -4
- package/{useHistory.ts → useHistory.js} +18 -27
- package/{useInterval.ts → useInterval.js} +5 -4
- package/useLocation.js +59 -0
- package/{usePurchase.tsx → usePurchase.js} +38 -73
- package/{usePushNoti.tsx → usePushNoti.js} +14 -11
- package/useThrottle.js +20 -0
- package/bootCsr.tsx +0 -180
- package/createRobotPage.ts +0 -14
- package/createSitemapPage.ts +0 -6
- package/index.ts +0 -23
- package/lazy.ts +0 -9
- package/makePageProto.tsx +0 -117
- package/types.ts +0 -7
- package/useCodepush.tsx +0 -98
- package/useDebounce.ts +0 -23
- package/useLocation.ts +0 -65
- package/useThrottle.ts +0 -17
package/bootCsr.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { device, initAuth, storage } from "@akanjs/client";
|
|
3
|
+
import {
|
|
4
|
+
csrContext,
|
|
5
|
+
DEFAULT_BOTTOM_INSET,
|
|
6
|
+
DEFAULT_TOP_INSET,
|
|
7
|
+
defaultPageState
|
|
8
|
+
} from "@akanjs/client";
|
|
9
|
+
import { Logger } from "@akanjs/common";
|
|
10
|
+
import React from "react";
|
|
11
|
+
import * as ReactDOM from "react-dom/client";
|
|
12
|
+
import { useCsrValues } from "./useCsrValues";
|
|
13
|
+
const supportLanguages = ["en", "ko"];
|
|
14
|
+
const bootCsr = async (context, rootPath, entryPath = "/route") => {
|
|
15
|
+
const [, jwt] = await Promise.all([device.init({ supportLanguages }), storage.getItem("jwt")]);
|
|
16
|
+
if (jwt)
|
|
17
|
+
initAuth({ jwt });
|
|
18
|
+
Logger.verbose(`Set default language: ${device.lang}`);
|
|
19
|
+
const pages = {};
|
|
20
|
+
await Promise.all(
|
|
21
|
+
Object.entries(context).map(async ([key, value]) => {
|
|
22
|
+
pages[key] = await value();
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
const getPageState = (csrConfig) => {
|
|
26
|
+
const { transition, safeArea, topInset, bottomInset, gesture, cache } = csrConfig ?? {};
|
|
27
|
+
const pageState = {
|
|
28
|
+
transition: transition ?? "none",
|
|
29
|
+
topSafeArea: safeArea === false ? 0 : device.topSafeArea,
|
|
30
|
+
bottomSafeArea: safeArea === false ? 0 : device.bottomSafeArea,
|
|
31
|
+
// topSafeArea: safeArea === false || device.info.platform === "android" ? 0 : device.topSafeArea,
|
|
32
|
+
// bottomSafeArea: safeArea === false || device.info.platform === "android" ? 0 : device.bottomSafeArea,
|
|
33
|
+
topInset: topInset === true ? DEFAULT_TOP_INSET : topInset === false ? 0 : topInset ?? 0,
|
|
34
|
+
bottomInset: bottomInset === true ? DEFAULT_BOTTOM_INSET : bottomInset === false ? 0 : bottomInset ?? 0,
|
|
35
|
+
gesture: gesture ?? true,
|
|
36
|
+
cache: cache ?? false
|
|
37
|
+
};
|
|
38
|
+
return pageState;
|
|
39
|
+
};
|
|
40
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
41
|
+
routeMap.set("/", { path: "/", children: /* @__PURE__ */ new Map() });
|
|
42
|
+
for (const filePath of Object.keys(pages)) {
|
|
43
|
+
const fileName = /\.\/(.*)\.tsx$/.exec(filePath)?.[1];
|
|
44
|
+
if (!fileName)
|
|
45
|
+
continue;
|
|
46
|
+
const fileType = fileName.endsWith("page") ? "page" : fileName.endsWith("layout") ? "layout" : null;
|
|
47
|
+
if (!fileType)
|
|
48
|
+
continue;
|
|
49
|
+
const pathSegments = [
|
|
50
|
+
"/",
|
|
51
|
+
...fileName.split("/").slice(0, -1).map((segment) => `/${segment.replace(/\[(.*?)\]/g, ":$1")}`)
|
|
52
|
+
];
|
|
53
|
+
const targetRouteMap = pathSegments.slice(0, -1).reduce((rMap, path) => {
|
|
54
|
+
if (!rMap.has(path))
|
|
55
|
+
rMap.set(path, { path, children: /* @__PURE__ */ new Map() });
|
|
56
|
+
return rMap.get(path)?.children;
|
|
57
|
+
}, routeMap);
|
|
58
|
+
if (!targetRouteMap)
|
|
59
|
+
continue;
|
|
60
|
+
const targetPath = pathSegments[pathSegments.length - 1];
|
|
61
|
+
targetRouteMap.set(targetPath, {
|
|
62
|
+
// action: pages[path]?.action,
|
|
63
|
+
// ErrorBoundary: pages[path]?.ErrorBoundary,
|
|
64
|
+
...targetRouteMap.get(targetPath) ?? { path: targetPath, children: /* @__PURE__ */ new Map() },
|
|
65
|
+
...fileType === "layout" ? { Layout: pages[filePath].default } : {
|
|
66
|
+
Page: pages[filePath].default,
|
|
67
|
+
pageState: getPageState(pages[filePath].default.csrConfig),
|
|
68
|
+
csrConfig: pages[filePath].default.csrConfig
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const pathname = window.location.pathname;
|
|
73
|
+
const initialPath = device.lang + entryPath;
|
|
74
|
+
window.document.body.style.overflow = "hidden";
|
|
75
|
+
const getPathRoutes = (route, parentRootLayouts = [], parentLayouts = [], parentPaths = []) => {
|
|
76
|
+
const parentPath = parentPaths.filter((path2) => path2 !== "/").join("");
|
|
77
|
+
const currentPathSegment = /^\/\(.*\)$/.test(route.path) ? "" : route.path;
|
|
78
|
+
const isRoot = ["/", "/:lang"].includes(parentPath + currentPathSegment) && parentRootLayouts.length < 2;
|
|
79
|
+
const path = parentPath + currentPathSegment;
|
|
80
|
+
const pathSegments = [...parentPaths, ...currentPathSegment ? [currentPathSegment] : []];
|
|
81
|
+
const RootLayouts = [...parentRootLayouts, ...isRoot && route.Layout ? [route.Layout] : []];
|
|
82
|
+
const Layouts = [...parentLayouts, ...!isRoot && route.Layout ? [route.Layout] : []];
|
|
83
|
+
return [
|
|
84
|
+
...route.Page ? [
|
|
85
|
+
{
|
|
86
|
+
path,
|
|
87
|
+
pathSegments,
|
|
88
|
+
Page: route.Page,
|
|
89
|
+
RootLayouts,
|
|
90
|
+
Layouts,
|
|
91
|
+
pageState: route.pageState ?? defaultPageState
|
|
92
|
+
}
|
|
93
|
+
] : [],
|
|
94
|
+
...route.children.size ? [...route.children.values()].flatMap((child) => getPathRoutes(child, RootLayouts, Layouts, pathSegments)) : []
|
|
95
|
+
];
|
|
96
|
+
};
|
|
97
|
+
const rootRoute = routeMap.get("/");
|
|
98
|
+
if (!rootRoute)
|
|
99
|
+
throw new Error("No root route");
|
|
100
|
+
const pathRoutes = getPathRoutes(rootRoute);
|
|
101
|
+
const routeGuide = { pathSegment: "/", children: {} };
|
|
102
|
+
pathRoutes.forEach((pathRoute) => {
|
|
103
|
+
const pathSegments = pathRoute.pathSegments.slice(1);
|
|
104
|
+
pathSegments.reduce((routeGuide2, pathSegment, index) => {
|
|
105
|
+
const child = routeGuide2.children[pathSegment];
|
|
106
|
+
routeGuide2.children[pathSegment] = {
|
|
107
|
+
...child ?? {},
|
|
108
|
+
pathSegment,
|
|
109
|
+
...index === pathSegments.length - 1 ? { pathRoute } : {},
|
|
110
|
+
children: child?.children ?? {}
|
|
111
|
+
};
|
|
112
|
+
return routeGuide2.children[pathSegment];
|
|
113
|
+
}, routeGuide);
|
|
114
|
+
});
|
|
115
|
+
const RouterProvider = () => {
|
|
116
|
+
const csrValues = useCsrValues(routeGuide, pathRoutes);
|
|
117
|
+
const { location } = csrValues;
|
|
118
|
+
return /* @__PURE__ */ React.createElement(csrContext.Provider, { value: csrValues }, location.pathRoute.RootLayouts.reduceRight(
|
|
119
|
+
(children, Layout) => {
|
|
120
|
+
return /* @__PURE__ */ React.createElement(Layout, { params: location.params, searchParams: location.searchParams }, children);
|
|
121
|
+
},
|
|
122
|
+
/* @__PURE__ */ React.createElement(React.Fragment, null)
|
|
123
|
+
));
|
|
124
|
+
};
|
|
125
|
+
if (pathname !== `/${initialPath}`) {
|
|
126
|
+
window.location.replace(initialPath);
|
|
127
|
+
return;
|
|
128
|
+
} else {
|
|
129
|
+
const el = document.getElementById("root");
|
|
130
|
+
if (!el)
|
|
131
|
+
throw new Error("No root element");
|
|
132
|
+
const root = ReactDOM.createRoot(el);
|
|
133
|
+
root.render(/* @__PURE__ */ React.createElement(RouterProvider, null));
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
bootCsr
|
|
138
|
+
};
|
|
@@ -2,15 +2,14 @@ import { logo } from "@akanjs/base";
|
|
|
2
2
|
import { Logger } from "@akanjs/common";
|
|
3
3
|
import { match as matchLocale } from "@formatjs/intl-localematcher";
|
|
4
4
|
import Negotiator from "negotiator";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { NextResponse } from "next/server";
|
|
7
6
|
const i18n = { defaultLocale: "en", locales: ["en", "ko"] };
|
|
8
7
|
const basePaths = process.env.basePaths ? process.env.basePaths.split(",") : [];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const negotiatorHeaders
|
|
13
|
-
request.headers.forEach((value, key) =>
|
|
8
|
+
function getLocale(request) {
|
|
9
|
+
if (!request.headers.get("accept-language"))
|
|
10
|
+
return i18n.defaultLocale;
|
|
11
|
+
const negotiatorHeaders = {};
|
|
12
|
+
request.headers.forEach((value, key) => negotiatorHeaders[key] = value);
|
|
14
13
|
try {
|
|
15
14
|
const languages = new Negotiator({ headers: negotiatorHeaders }).languages();
|
|
16
15
|
return matchLocale(languages, i18n.locales, i18n.defaultLocale);
|
|
@@ -18,12 +17,12 @@ function getLocale(request: NextRequest): string | undefined {
|
|
|
18
17
|
return i18n.defaultLocale;
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
|
-
|
|
20
|
+
const createNextMiddleware = () => {
|
|
22
21
|
Logger.rawLog(logo, "console");
|
|
23
|
-
const middleware = (request
|
|
22
|
+
const middleware = (request) => {
|
|
24
23
|
const pathname = request.nextUrl.pathname;
|
|
25
24
|
const pathnameIsMissingLocale = i18n.locales.every(
|
|
26
|
-
(
|
|
25
|
+
(locale2) => !pathname.startsWith(`/${locale2}/`) && pathname !== `/${locale2}`
|
|
27
26
|
);
|
|
28
27
|
if (pathnameIsMissingLocale)
|
|
29
28
|
return NextResponse.redirect(
|
|
@@ -37,9 +36,14 @@ export const createNextMiddleware = () => {
|
|
|
37
36
|
const searchParamJwt = searchParams.get("jwt");
|
|
38
37
|
headers.set("x-locale", locale);
|
|
39
38
|
headers.set("x-path", "/" + splits.slice(2).join("/"));
|
|
40
|
-
if (basePath)
|
|
41
|
-
|
|
39
|
+
if (basePath)
|
|
40
|
+
headers.set("x-base-path", basePath);
|
|
41
|
+
if (searchParamJwt)
|
|
42
|
+
headers.set("jwt", searchParamJwt);
|
|
42
43
|
return NextResponse.next({ request: { headers } });
|
|
43
44
|
};
|
|
44
45
|
return middleware;
|
|
45
46
|
};
|
|
47
|
+
export {
|
|
48
|
+
createNextMiddleware
|
|
49
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const createRobotPage = (clientHttpUri, config) => {
|
|
2
|
+
return {
|
|
3
|
+
...config ?? {},
|
|
4
|
+
rules: {
|
|
5
|
+
userAgent: "*",
|
|
6
|
+
allow: "/",
|
|
7
|
+
disallow: "/admin/",
|
|
8
|
+
...config?.rules ?? {}
|
|
9
|
+
},
|
|
10
|
+
sitemap: `${clientHttpUri}/sitemap.xml`
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
createRobotPage
|
|
15
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useFetch } from "./useFetch";
|
|
2
|
+
import { lazy } from "./lazy";
|
|
3
|
+
import { makePageProto } from "./makePageProto";
|
|
4
|
+
import { useDebounce } from "./useDebounce";
|
|
5
|
+
import { useInterval } from "./useInterval";
|
|
6
|
+
import { bootCsr } from "./bootCsr";
|
|
7
|
+
import { useCamera } from "./useCamera";
|
|
8
|
+
import { useContact } from "./useContact";
|
|
9
|
+
import { usePushNoti } from "./usePushNoti";
|
|
10
|
+
import { useGeoLocation } from "./useGeoLocation";
|
|
11
|
+
import { useCodepush } from "./useCodepush";
|
|
12
|
+
import { usePurchase } from "./usePurchase";
|
|
13
|
+
import { useCsrValues } from "./useCsrValues";
|
|
14
|
+
import { createRobotPage } from "./createRobotPage";
|
|
15
|
+
import { createSitemapPage } from "./createSitemapPage";
|
|
16
|
+
import { createNextMiddleware } from "./createNextMiddleware";
|
|
17
|
+
//! PageAgent csr에서 말썽 일으킨다
|
|
18
|
+
import { useThrottle } from "./useThrottle";
|
|
19
|
+
import { useHistory } from "./useHistory";
|
|
20
|
+
import { useLocation } from "./useLocation";
|
|
21
|
+
export {
|
|
22
|
+
bootCsr,
|
|
23
|
+
createNextMiddleware,
|
|
24
|
+
createRobotPage,
|
|
25
|
+
createSitemapPage,
|
|
26
|
+
lazy,
|
|
27
|
+
makePageProto,
|
|
28
|
+
useCamera,
|
|
29
|
+
useCodepush,
|
|
30
|
+
useContact,
|
|
31
|
+
useCsrValues,
|
|
32
|
+
useDebounce,
|
|
33
|
+
useFetch,
|
|
34
|
+
useGeoLocation,
|
|
35
|
+
useHistory,
|
|
36
|
+
useInterval,
|
|
37
|
+
useLocation,
|
|
38
|
+
usePurchase,
|
|
39
|
+
usePushNoti,
|
|
40
|
+
useThrottle
|
|
41
|
+
};
|
package/lazy.js
ADDED
package/makePageProto.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { baseClientEnv } from "@akanjs/base";
|
|
2
|
+
import { getHeader } from "@akanjs/client";
|
|
3
|
+
import { Logger, pathGet } from "@akanjs/common";
|
|
4
|
+
const getPageInfo = () => {
|
|
5
|
+
if (baseClientEnv.side !== "server") {
|
|
6
|
+
return {
|
|
7
|
+
locale: window.location.pathname.split("/")[1] ?? "en",
|
|
8
|
+
path: "/" + window.location.pathname.split("/").slice(2).join("/")
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const locale = getHeader("x-locale") ?? "en";
|
|
12
|
+
const path = getHeader("x-path") ?? "/";
|
|
13
|
+
return { locale, path };
|
|
14
|
+
};
|
|
15
|
+
const langIdx = { en: 0, ko: 1, zhChs: 2, zhCht: 3 };
|
|
16
|
+
const dictionary = {};
|
|
17
|
+
const translator = (lang, key, param) => {
|
|
18
|
+
const idx = langIdx[lang];
|
|
19
|
+
try {
|
|
20
|
+
const msg = pathGet(key, dictionary)?.[idx];
|
|
21
|
+
if (!msg) {
|
|
22
|
+
Logger.error(`No translation for ${key}`);
|
|
23
|
+
return key;
|
|
24
|
+
}
|
|
25
|
+
return param ? msg.replace(/{([^}]+)}/g, (_, key2) => param[key2]) : msg;
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
translator.rich = (lang, key, param) => {
|
|
31
|
+
const idx = langIdx[lang];
|
|
32
|
+
const msg = pathGet(key, dictionary)?.[idx];
|
|
33
|
+
if (!msg) {
|
|
34
|
+
Logger.error(`No translation for ${key}`);
|
|
35
|
+
return key;
|
|
36
|
+
}
|
|
37
|
+
return param ? msg.replace(/{([^}]+)}/g, (_, key2) => param[key2]) : msg;
|
|
38
|
+
};
|
|
39
|
+
const makePageProto = (locales) => {
|
|
40
|
+
locales.forEach((locale) => {
|
|
41
|
+
Object.keys(locale).forEach((key) => dictionary[key] = Object.assign(dictionary[key] ?? {}, locale[key]));
|
|
42
|
+
});
|
|
43
|
+
return () => {
|
|
44
|
+
const { locale, path } = getPageInfo();
|
|
45
|
+
const lang = locale;
|
|
46
|
+
const l = (key, param) => translator(lang, key, param);
|
|
47
|
+
l.rich = (key, param) => /* @__PURE__ */ React.createElement(
|
|
48
|
+
"span",
|
|
49
|
+
{
|
|
50
|
+
dangerouslySetInnerHTML: {
|
|
51
|
+
__html: translator.rich(lang, key, {
|
|
52
|
+
...param,
|
|
53
|
+
// strong: (chunks: string) => `<b>${chunks}</b>`,
|
|
54
|
+
// "bg-primary": (chunks: string) => `<span className="bg-primary text-base-100">${chunks}</span>`,
|
|
55
|
+
// primary: (chunks: string) => `<span className="bg-base-100 text-primary">${chunks}</span>`,
|
|
56
|
+
br: `<br />`
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
l.field = (model, field) => {
|
|
62
|
+
const key = `${model}.${field}`;
|
|
63
|
+
return l(key);
|
|
64
|
+
};
|
|
65
|
+
l.desc = (model, field) => {
|
|
66
|
+
const key = `${model}.desc-${field}`;
|
|
67
|
+
return l(key);
|
|
68
|
+
};
|
|
69
|
+
l.enum = (model, field, value) => {
|
|
70
|
+
const key = `${model}.enum-${field}-${value}`;
|
|
71
|
+
return l(key);
|
|
72
|
+
};
|
|
73
|
+
l.enumdesc = (model, field, value) => {
|
|
74
|
+
const key = `${model}.enumdesc-${field}-${value}`;
|
|
75
|
+
return l(key);
|
|
76
|
+
};
|
|
77
|
+
l.api = (model, endpoint) => {
|
|
78
|
+
const key = `${model}.api-${endpoint}`;
|
|
79
|
+
return l(key);
|
|
80
|
+
};
|
|
81
|
+
l.apidesc = (model, endpoint) => {
|
|
82
|
+
const key = `${model}.apidesc-${endpoint}`;
|
|
83
|
+
return l(key);
|
|
84
|
+
};
|
|
85
|
+
l.arg = (model, endpoint, arg) => {
|
|
86
|
+
const key = `${model}.arg-${endpoint}-${arg}`;
|
|
87
|
+
return l(key);
|
|
88
|
+
};
|
|
89
|
+
l.argdesc = (model, endpoint, arg) => {
|
|
90
|
+
const key = `${model}.argdesc-${endpoint}-${arg}`;
|
|
91
|
+
return l(key);
|
|
92
|
+
};
|
|
93
|
+
l.qry = (model, queryKey) => {
|
|
94
|
+
const key = `${model}.qry-${queryKey}`;
|
|
95
|
+
return l(key);
|
|
96
|
+
};
|
|
97
|
+
l.qrydesc = (model, queryKey) => {
|
|
98
|
+
const key = `${model}.qrydesc-${queryKey}`;
|
|
99
|
+
return l(key);
|
|
100
|
+
};
|
|
101
|
+
l.qarg = (model, queryKey, arg) => {
|
|
102
|
+
const key = `${model}.qarg-${queryKey}-${arg}`;
|
|
103
|
+
return l(key);
|
|
104
|
+
};
|
|
105
|
+
l.qargdesc = (model, queryKey, arg) => {
|
|
106
|
+
const key = `${model}.qargdesc-${queryKey}-${arg}`;
|
|
107
|
+
return l(key);
|
|
108
|
+
};
|
|
109
|
+
return { path, l, lang };
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
export {
|
|
113
|
+
makePageProto
|
|
114
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/next",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"type": "
|
|
3
|
+
"version": "0.0.54",
|
|
4
|
+
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -14,23 +14,5 @@
|
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=22"
|
|
16
16
|
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"@capacitor-community/contacts": "^6.1.1",
|
|
19
|
-
"@capacitor-community/fcm": "^7.1.1",
|
|
20
|
-
"@capacitor/app": "^7.0.1",
|
|
21
|
-
"@capacitor/camera": "^7.0.1",
|
|
22
|
-
"@capacitor/device": "^7.0.1",
|
|
23
|
-
"@capacitor/geolocation": "^7.1.2",
|
|
24
|
-
"@capacitor/push-notifications": "^7.0.1",
|
|
25
|
-
"@capgo/capacitor-updater": "^7.0.50",
|
|
26
|
-
"@formatjs/intl-localematcher": "^0.5.10",
|
|
27
|
-
"@react-spring/web": "^9.7.5",
|
|
28
|
-
"@use-gesture/react": "^10.3.1",
|
|
29
|
-
"axios": "^1.7.9",
|
|
30
|
-
"cordova-plugin-purchase": "^13.12.1",
|
|
31
|
-
"negotiator": "^1.0.0",
|
|
32
|
-
"next": "^15.3.2",
|
|
33
|
-
"react": "^18.3.1",
|
|
34
|
-
"react-dom": "^18.3.1"
|
|
35
|
-
}
|
|
17
|
+
"dependencies": {}
|
|
36
18
|
}
|
package/types.js
ADDED
|
File without changes
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { device } from "@akanjs/client";
|
|
3
|
-
import { Camera, CameraResultType, CameraSource
|
|
3
|
+
import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 최초로 킬 경우 권한은 prompt 상태이다.
|
|
11
|
-
* prompt 상태일 경우 권한을 요청한다.
|
|
12
|
-
* 권한이 denied 상태일 경우 설정으로 이동한다.
|
|
13
|
-
* 이후 state의 permission을 업데이트해야한다.
|
|
14
|
-
*
|
|
15
|
-
*/
|
|
16
|
-
const checkPermission = async (type: "photos" | "camera" | "all") => {
|
|
5
|
+
const useCamera = () => {
|
|
6
|
+
const [permissions, setPermissions] = useState({ camera: "prompt", photos: "prompt" });
|
|
7
|
+
const checkPermission = async (type) => {
|
|
17
8
|
try {
|
|
18
9
|
if (type === "photos") {
|
|
19
10
|
if (permissions.photos === "prompt") {
|
|
@@ -33,27 +24,18 @@ export const useCamera = () => {
|
|
|
33
24
|
}
|
|
34
25
|
} else {
|
|
35
26
|
if (permissions.camera === "prompt" || permissions.photos === "prompt") {
|
|
36
|
-
const
|
|
37
|
-
setPermissions(
|
|
27
|
+
const permissions2 = await Camera.requestPermissions();
|
|
28
|
+
setPermissions(permissions2);
|
|
38
29
|
} else if (permissions.camera === "denied" || permissions.photos === "denied") {
|
|
39
30
|
location.assign("app-settings:");
|
|
40
31
|
return;
|
|
41
32
|
}
|
|
42
33
|
}
|
|
43
34
|
} catch (e) {
|
|
44
|
-
//
|
|
45
35
|
}
|
|
46
36
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const source =
|
|
50
|
-
device.info.platform !== "web"
|
|
51
|
-
? src === "prompt"
|
|
52
|
-
? CameraSource.Prompt
|
|
53
|
-
: src === "camera"
|
|
54
|
-
? CameraSource.Camera
|
|
55
|
-
: CameraSource.Photos
|
|
56
|
-
: CameraSource.Photos;
|
|
37
|
+
const getPhoto = async (src = "prompt") => {
|
|
38
|
+
const source = device.info.platform !== "web" ? src === "prompt" ? CameraSource.Prompt : src === "camera" ? CameraSource.Camera : CameraSource.Photos : CameraSource.Photos;
|
|
57
39
|
const permission = src === "prompt" ? "all" : src === "camera" ? "camera" : "photos";
|
|
58
40
|
void checkPermission(permission);
|
|
59
41
|
try {
|
|
@@ -62,33 +44,34 @@ export const useCamera = () => {
|
|
|
62
44
|
source,
|
|
63
45
|
allowEditing: false,
|
|
64
46
|
resultType: CameraResultType.DataUrl,
|
|
65
|
-
promptLabelHeader: "
|
|
66
|
-
promptLabelPhoto: "
|
|
67
|
-
promptLabelPicture: "
|
|
68
|
-
promptLabelCancel: "
|
|
47
|
+
promptLabelHeader: "\uD504\uB85C\uD544 \uC0AC\uC9C4\uC744 \uC62C\uB824\uC8FC\uC138\uC694",
|
|
48
|
+
promptLabelPhoto: "\uC568\uBC94\uC5D0\uC11C \uC120\uD0DD\uD558\uAE30",
|
|
49
|
+
promptLabelPicture: "\uC0AC\uC9C4 \uCC0D\uAE30",
|
|
50
|
+
promptLabelCancel: "\uCDE8\uC18C"
|
|
69
51
|
});
|
|
70
52
|
return photo;
|
|
71
53
|
} catch (e) {
|
|
72
|
-
if (e === "User cancelled photos app")
|
|
54
|
+
if (e === "User cancelled photos app")
|
|
55
|
+
return;
|
|
73
56
|
}
|
|
74
57
|
};
|
|
75
|
-
|
|
76
58
|
const pickImage = async () => {
|
|
77
59
|
void checkPermission("photos");
|
|
78
60
|
const photo = await Camera.pickImages({
|
|
79
|
-
quality: 90
|
|
61
|
+
quality: 90
|
|
80
62
|
});
|
|
81
|
-
|
|
82
63
|
return photo;
|
|
83
64
|
};
|
|
84
|
-
|
|
85
65
|
useEffect(() => {
|
|
86
66
|
void (async () => {
|
|
87
67
|
if (device.info.platform !== "web") {
|
|
88
|
-
const
|
|
89
|
-
setPermissions(
|
|
68
|
+
const permissions2 = await Camera.checkPermissions();
|
|
69
|
+
setPermissions(permissions2);
|
|
90
70
|
}
|
|
91
71
|
})();
|
|
92
72
|
}, []);
|
|
93
73
|
return { permissions, getPhoto, pickImage, checkPermission };
|
|
94
74
|
};
|
|
75
|
+
export {
|
|
76
|
+
useCamera
|
|
77
|
+
};
|
package/useCodepush.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { mergeVersion, splitVersion } from "@akanjs/common";
|
|
3
|
+
import { App } from "@capacitor/app";
|
|
4
|
+
import { Device } from "@capacitor/device";
|
|
5
|
+
import { CapacitorUpdater } from "@capgo/capacitor-updater";
|
|
6
|
+
import axios from "axios";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
const useCodepush = ({ serverUrl, branch }) => {
|
|
9
|
+
const [update, setUpdate] = useState(false);
|
|
10
|
+
const [version, setVersion] = useState("");
|
|
11
|
+
const initialize = async () => {
|
|
12
|
+
await CapacitorUpdater.notifyAppReady();
|
|
13
|
+
};
|
|
14
|
+
const checkNewRelease = async () => {
|
|
15
|
+
const info = await Device.getInfo();
|
|
16
|
+
const app = await App.getInfo();
|
|
17
|
+
const pluginVersion = await CapacitorUpdater.getPluginVersion();
|
|
18
|
+
const { deviceId } = await CapacitorUpdater.getDeviceId();
|
|
19
|
+
const { bundle: version2, native } = await CapacitorUpdater.current();
|
|
20
|
+
const builtInversion = await CapacitorUpdater.getBuiltinVersion();
|
|
21
|
+
const appId = app.id;
|
|
22
|
+
const platform = info.platform;
|
|
23
|
+
window.alert(
|
|
24
|
+
`getBuildinVersion:${builtInversion.version}
|
|
25
|
+
current.bundle:${version2.version}
|
|
26
|
+
currennt.native:${native}`
|
|
27
|
+
);
|
|
28
|
+
const { major, minor, patch } = splitVersion(version2.version === "builtin" ? app.version : version2.version);
|
|
29
|
+
const appName = process.env.NEXT_PUBLIC_APP_NAME ?? "";
|
|
30
|
+
const appInfo = {
|
|
31
|
+
appId,
|
|
32
|
+
appName,
|
|
33
|
+
deviceId,
|
|
34
|
+
platform,
|
|
35
|
+
branch,
|
|
36
|
+
isEmulator: info.isVirtual,
|
|
37
|
+
major: parseInt(major),
|
|
38
|
+
minor: parseInt(minor),
|
|
39
|
+
patch: parseInt(patch),
|
|
40
|
+
buildNum: app.build,
|
|
41
|
+
//앱내 빌드시 버전 횟수 모르면 고한테 물어보기
|
|
42
|
+
versionOs: info.osVersion
|
|
43
|
+
};
|
|
44
|
+
const url = serverUrl.replace("lu", "akasys");
|
|
45
|
+
const release = (await axios.post(`${url}/release/codepush`, {
|
|
46
|
+
data: { ...appInfo }
|
|
47
|
+
})).data;
|
|
48
|
+
if (!release)
|
|
49
|
+
return;
|
|
50
|
+
const file = (await axios.get(`${url}/file/file/${release.appBuild}`)).data;
|
|
51
|
+
return { release, bundleFile: file };
|
|
52
|
+
};
|
|
53
|
+
const codepush = async () => {
|
|
54
|
+
const isNewRelease = await checkNewRelease();
|
|
55
|
+
if (!isNewRelease)
|
|
56
|
+
return;
|
|
57
|
+
const { release, bundleFile } = isNewRelease;
|
|
58
|
+
setUpdate(true);
|
|
59
|
+
const bundle = await CapacitorUpdater.download({
|
|
60
|
+
url: bundleFile.url,
|
|
61
|
+
version: mergeVersion(release.major, release.minor, release.patch)
|
|
62
|
+
});
|
|
63
|
+
await CapacitorUpdater.set(bundle);
|
|
64
|
+
};
|
|
65
|
+
const getVersion = async () => {
|
|
66
|
+
return await CapacitorUpdater.getBuiltinVersion();
|
|
67
|
+
};
|
|
68
|
+
const statManager = async () => {
|
|
69
|
+
};
|
|
70
|
+
return { update, version, initialize, checkNewRelease, codepush, statManager };
|
|
71
|
+
};
|
|
72
|
+
export {
|
|
73
|
+
useCodepush
|
|
74
|
+
};
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { device } from "@akanjs/client";
|
|
3
|
-
import { Contacts
|
|
3
|
+
import { Contacts } from "@capacitor-community/contacts";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const [permissions, setPermissions] = useState<PermissionStatus>({ contacts: "prompt" });
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 최초로 킬 경우 권한은 prompt 상태이다.
|
|
11
|
-
* prompt 상태일 경우 권한을 요청한다.
|
|
12
|
-
* 권한이 denied 상태일 경우 설정으로 이동한다.
|
|
13
|
-
* 이후 state의 permission을 업데이트해야한다.
|
|
14
|
-
*
|
|
15
|
-
*/
|
|
5
|
+
const useContact = () => {
|
|
6
|
+
const [permissions, setPermissions] = useState({ contacts: "prompt" });
|
|
16
7
|
const checkPermission = async () => {
|
|
17
8
|
try {
|
|
18
9
|
if (permissions.contacts === "prompt") {
|
|
@@ -23,23 +14,23 @@ export const useContact = () => {
|
|
|
23
14
|
return;
|
|
24
15
|
}
|
|
25
16
|
} catch (e) {
|
|
26
|
-
//
|
|
27
17
|
}
|
|
28
18
|
};
|
|
29
|
-
|
|
30
19
|
const getContacts = async () => {
|
|
31
20
|
await checkPermission();
|
|
32
21
|
const { contacts } = await Contacts.getContacts({ projection: { name: true, phones: true } });
|
|
33
22
|
return contacts;
|
|
34
23
|
};
|
|
35
|
-
|
|
36
24
|
useEffect(() => {
|
|
37
25
|
void (async () => {
|
|
38
|
-
if (device.info.platform === "web")
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
if (device.info.platform === "web")
|
|
27
|
+
return;
|
|
28
|
+
const permissions2 = await Contacts.checkPermissions();
|
|
29
|
+
setPermissions(permissions2);
|
|
41
30
|
})();
|
|
42
31
|
}, []);
|
|
43
|
-
|
|
44
32
|
return { permissions, getContacts, checkPermission };
|
|
45
33
|
};
|
|
34
|
+
export {
|
|
35
|
+
useContact
|
|
36
|
+
};
|