summaryrefslogtreecommitdiff
path: root/src/client/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/scripts')
-rw-r--r--src/client/scripts/i18n.ts44
-rw-r--r--src/client/scripts/initialize-sw.ts68
2 files changed, 112 insertions, 0 deletions
diff --git a/src/client/scripts/i18n.ts b/src/client/scripts/i18n.ts
new file mode 100644
index 0000000000..d535e236bb
--- /dev/null
+++ b/src/client/scripts/i18n.ts
@@ -0,0 +1,44 @@
+// Notice: Service Workerでも使用します
+export class I18n<T extends Record<string, any>> {
+ public locale: T;
+
+ constructor(locale: T) {
+ this.locale = locale;
+
+ if (_DEV_) {
+ console.log('i18n', this.locale);
+ }
+
+ //#region BIND
+ this.t = this.t.bind(this);
+ //#endregion
+ }
+
+ // string にしているのは、ドット区切りでのパス指定を許可するため
+ // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
+ public t(key: string, args?: Record<string, any>): string {
+ try {
+ let str = key.split('.').reduce((o, i) => o[i], this.locale) as string;
+
+ if (_DEV_) {
+ if (!str.includes('{')) {
+ console.warn(`i18n: '${key}' has no any arg. so ref prop directly instead of call this method.`);
+ }
+ }
+
+ if (args) {
+ for (const [k, v] of Object.entries(args)) {
+ str = str.replace(`{${k}}`, v);
+ }
+ }
+ return str;
+ } catch (e) {
+ if (_DEV_) {
+ console.warn(`missing localization '${key}'`);
+ return `⚠'${key}'⚠`;
+ }
+
+ return key;
+ }
+ }
+}
diff --git a/src/client/scripts/initialize-sw.ts b/src/client/scripts/initialize-sw.ts
new file mode 100644
index 0000000000..d6dbd5dbd4
--- /dev/null
+++ b/src/client/scripts/initialize-sw.ts
@@ -0,0 +1,68 @@
+import { instance } from '@/instance';
+import { $i } from '@/account';
+import { api } from '@/os';
+import { lang } from '@/config';
+
+export async function initializeSw() {
+ if (instance.swPublickey &&
+ ('serviceWorker' in navigator) &&
+ ('PushManager' in window) &&
+ $i && $i.token) {
+ navigator.serviceWorker.register(`/sw.js`);
+
+ navigator.serviceWorker.ready.then(registration => {
+ registration.active?.postMessage({
+ msg: 'initialize',
+ lang,
+ });
+ // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
+ registration.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: urlBase64ToUint8Array(instance.swPublickey)
+ }).then(subscription => {
+ function encode(buffer: ArrayBuffer | null) {
+ return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
+ }
+
+ // Register
+ api('sw/register', {
+ endpoint: subscription.endpoint,
+ auth: encode(subscription.getKey('auth')),
+ publickey: encode(subscription.getKey('p256dh'))
+ });
+ })
+ // When subscribe failed
+ .catch(async (err: Error) => {
+ // 通知が許可されていなかったとき
+ if (err.name === 'NotAllowedError') {
+ return;
+ }
+
+ // 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが
+ // 既に存在していることが原因でエラーになった可能性があるので、
+ // そのサブスクリプションを解除しておく
+ const subscription = await registration.pushManager.getSubscription();
+ if (subscription) subscription.unsubscribe();
+ });
+ });
+ }
+}
+
+/**
+ * Convert the URL safe base64 string to a Uint8Array
+ * @param base64String base64 string
+ */
+function urlBase64ToUint8Array(base64String: string): Uint8Array {
+ const padding = '='.repeat((4 - base64String.length % 4) % 4);
+ const base64 = (base64String + padding)
+ .replace(/-/g, '+')
+ .replace(/_/g, '/');
+
+ const rawData = window.atob(base64);
+ const outputArray = new Uint8Array(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+ return outputArray;
+}