Skip to content

Commit

Permalink
refactor: pass the Cybozu data via CustomeEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
ganta committed Dec 29, 2024
1 parent 317cb02 commit 474d650
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 58 deletions.
8 changes: 0 additions & 8 deletions public/js/initialization.js

This file was deleted.

7 changes: 7 additions & 0 deletions public/js/passCybozuData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(() => {
document.dispatchEvent(
new CustomEvent("cybozuDataPass", {
detail: JSON.stringify(cybozu.data),
}),
);
})();
2 changes: 1 addition & 1 deletion public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"permissions": ["storage"],
"web_accessible_resources": [
{
"resources": ["content.js", "assets/*", "js/initialization.js"],
"resources": ["content.js", "assets/*", "js/passCybozuData.js"],
"matches": ["<all_urls>"]
}
],
Expand Down
73 changes: 73 additions & 0 deletions src/apis/cybozu/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
CybozuDataInitializationTimeoutError,
UninitializedCybozuDataError,
} from "@/apis/cybozu/errors.ts";
import { type CybozuData, CybozuDataSchema } from "@/models/CybozuData";
import type { LoginUser } from "@/models/LoginUser.ts";

declare global {
// noinspection JSUnusedGlobalSymbols
interface DocumentEventMap {
cybozuDataPass: CustomEvent<CybozuData>;
}
}

const dataReceptionTimeoutMilliseconds = 1000;

let cybozuData: CybozuData | undefined;

export async function initializeCybozuData(): Promise<CybozuData> {
return new Promise((resolve, reject) => {
const timeoutTimer = setTimeout(() => {
document.removeEventListener("cybozuDataPass", handler);
reject(
new CybozuDataInitializationTimeoutError(
dataReceptionTimeoutMilliseconds,
),
);
}, dataReceptionTimeoutMilliseconds);

function handler(event: CustomEvent) {
clearTimeout(timeoutTimer);
const data = CybozuDataSchema.parse(JSON.parse(event.detail));
setCybozuData(data);
resolve(data);
}

document.addEventListener("cybozuDataPass", handler, {
once: true,
});

const scriptEl = document.createElement("script");
scriptEl.src = chrome.runtime.getURL("js/passCybozuData.js");
document.body.appendChild(scriptEl);
});
}

export function setCybozuData(data: CybozuData): void {
cybozuData = data;
}

export function clearCybozuData(): void {
cybozuData = undefined;
}

export function getLoginUser(): LoginUser {
return getCybozuData().LOGIN_USER;
}

export function getRequestToken(): string {
return getCybozuData().REQUEST_TOKEN;
}

export function getDisplayLocale(): string {
return getCybozuData().DISPLAY_LOCALE;
}

function getCybozuData(): CybozuData {
if (cybozuData === undefined) {
throw new UninitializedCybozuDataError();
}

return cybozuData;
}
17 changes: 17 additions & 0 deletions src/apis/cybozu/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export class CybozuDataInitializationTimeoutError extends Error {
constructor(timeoutMilliseconds: number) {
super(
`Could not receive the Cybozu data within ${timeoutMilliseconds} milliseconds`,
);
this.name = "CybozuDataInitializationTimeoutError";
Object.setPrototypeOf(this, CybozuDataInitializationTimeoutError.prototype);
}
}

export class UninitializedCybozuDataError extends Error {
constructor() {
super("Cybozu data is not initialized yet");
this.name = "UninitializedCybozuDataError";
Object.setPrototypeOf(this, UninitializedCybozuDataError.prototype);
}
}
21 changes: 6 additions & 15 deletions src/app/content.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@/styles/content.scss";

import { initializeCybozuData } from "@/apis/cybozu/api.ts";
import KintoneClient from "./kintone/kintone-client";
import MarktoneConfig from "./marktone-config";
import MarktoneHandler from "./marktone-handler";
Expand All @@ -14,23 +15,13 @@ function setMarktoneEnabled(enabled: boolean): void {
}
}

MarktoneConfig.loadEnabled(setMarktoneEnabled);
(async () => {
MarktoneConfig.loadEnabled(setMarktoneEnabled);
MarktoneConfig.onEnabledChanged(setMarktoneEnabled);

MarktoneConfig.onEnabledChanged(setMarktoneEnabled);
await initializeCybozuData();

// Pass the login user information to DOM.
// Because `window.kintone` cannot be referred directly from Chrome extension.
const initializationScript = document.createElement("script");
initializationScript.src = chrome.runtime.getURL("js/initialization.js");
initializationScript.onload = function () {
(this as HTMLScriptElement).remove();
};
(document.head || document.documentElement).appendChild(initializationScript);

const run = () => {
const kintoneClient = new KintoneClient();
const handler = new MarktoneHandler(kintoneClient);
handler.handle();
};

document.body.addEventListener("marktone-initialized", run, { once: true });
})();
38 changes: 5 additions & 33 deletions src/app/kintone/kintone-client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getDisplayLocale, getRequestToken } from "@/apis/cybozu/api.ts";
import {
type DirectoryEntity,
DirectoryEntityType,
Expand Down Expand Up @@ -86,21 +87,6 @@ class DirectoryEntityCollection {
}
}

interface LoginUser {
id: string;
code: string;
name: string;
email: string;
url: string;
employeeNumber: string;
phone: string;
mobilePhone: string;
extensionNumber: string;
timezone: string;
isGuest: boolean;
language: string;
}

export default class KintoneClient {
static defaultThumbnailWidth = 250;

Expand Down Expand Up @@ -134,31 +120,17 @@ export default class KintoneClient {

private static uploadBlobAPI = "/blob/upload.json";

private readonly loginUser: LoginUser;

private readonly spaceId: number | null;

private readonly requestToken: string;

constructor() {
this.loginUser = KintoneClient.getLoginUser();
this.spaceId = KintoneClient.getSpaceId();
this.requestToken = KintoneClient.getRequestToken();
}

static getLoginUser(): LoginUser {
return JSON.parse(document.body.dataset.loginUser as string) as LoginUser;
}

static getSpaceId(): number | null {
const match = window.location.hash.match(/\/space\/(\d+)\//);
return match ? Number.parseInt(match[1], 10) : null;
}

static getRequestToken(): string {
return document.body.dataset.requestToken || "";
}

static isSpacePage(): boolean {
return window.location.hash.startsWith("#/space/");
}
Expand Down Expand Up @@ -186,7 +158,7 @@ export default class KintoneClient {
): string {
const params = new URLSearchParams();
params.append("fileKey", fileKey);
params.append("_lc", KintoneClient.getLoginUser().language);
params.append("_lc", getDisplayLocale());
params.append("_ref", encodeURI(window.location.href));

if (additionalParams) {
Expand Down Expand Up @@ -315,7 +287,7 @@ export default class KintoneClient {
const params = new URLSearchParams();
params.append("checkThumbnail", "true");
params.append("w", KintoneClient.defaultThumbnailWidth.toString());
params.append("_lc", this.loginUser.language);
params.append("_lc", getDisplayLocale());
params.append("_ref", encodeURI(window.location.href));

const apiPath = `${KintoneClient.getAPIPathPrefix()}${
Expand All @@ -324,7 +296,7 @@ export default class KintoneClient {
const rawResponse = await fetch(apiPath, {
method: "POST",
headers: {
"X-Cybozu-RequestToken": this.requestToken,
"X-Cybozu-RequestToken": getRequestToken(),
},
body: formData,
});
Expand All @@ -333,7 +305,7 @@ export default class KintoneClient {

private async postToAPI<T>(path: string, requestBody: unknown): Promise<T> {
const params = new URLSearchParams();
params.append("_lc", this.loginUser.language);
params.append("_lc", getDisplayLocale());
params.append("_ref", encodeURI(window.location.href));

const apiPath = `${KintoneClient.getAPIPathPrefix()}${path}?${params.toString()}`;
Expand Down
3 changes: 2 additions & 1 deletion src/components/Marktone/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "@/models/DirectoryEntity.ts";

import "@webscopeio/react-textarea-autocomplete/style.css";
import { getLoginUser } from "@/apis/cybozu/api.ts";

const { useState, useEffect, useRef } = React;

Expand Down Expand Up @@ -87,7 +88,7 @@ const Marktone: React.FC<MarktoneProps> = ({
const convertReplyMentionsToText = (
replyMentions: ReplyMention[],
): string => {
const currentUser = KintoneClient.getLoginUser();
const currentUser = getLoginUser();
const normalizedMentions = replyMentions.filter((replyMention) => {
if (replyMention.type !== DirectoryEntityType.enum.User) return true;
return replyMention.code !== currentUser.code;
Expand Down
9 changes: 9 additions & 0 deletions src/models/CybozuData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { LoginUserSchema } from "@/models/LoginUser.ts";
import { z } from "zod";

export const CybozuDataSchema = z.object({
DISPLAY_LOCALE: z.string(),
LOGIN_USER: LoginUserSchema,
REQUEST_TOKEN: z.string(),
});
export type CybozuData = z.infer<typeof CybozuDataSchema>;
6 changes: 6 additions & 0 deletions src/models/LoginUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from "zod";

export const LoginUserSchema = z.object({
code: z.string(),
});
export type LoginUser = z.infer<typeof LoginUserSchema>;
Loading

0 comments on commit 474d650

Please sign in to comment.