Feat: メッセージの送受信 / New: ユーザーのiconプロパティ / New: logエンティティ・リポジトリ / Chg: コミュニティリポジトリのスキーマのiconをoptionalに / Del: 不要なimport / Fix: Vue起動前のindex.htmlの背景色をVueと同期 / Enhance: Service Workerを改善 / Fix: 最初に開いたページが動作しない問題 / Feat: 上部の通知モーダル / Feat: 閉じることができないエラーのモーダルに再読み込みボタンを追加 / Fix: はみ出す挙動などのCSSを修正
This commit is contained in:
@@ -1,37 +1,46 @@
|
||||
import TopNotice from "@/components/Modal/TopNotice.vue";
|
||||
import { createApp, h, ref, type Component } from "vue";
|
||||
|
||||
const layer = ref<number>(0);
|
||||
const layer = ref<number>(1);
|
||||
|
||||
export const createModal = <T extends Component>(data: {
|
||||
component: T,
|
||||
isTopNotice?: boolean;
|
||||
style?: Record<string, string>,
|
||||
props?: Record<string, ((...args: any[]) => any) | any>,
|
||||
onClose?: () => void
|
||||
onClose?: (...args: any) => any
|
||||
}) => {
|
||||
layer.value++
|
||||
const newContainer = document.createElement("div");
|
||||
for (const [key, value] of Object.entries({
|
||||
...data.style,
|
||||
transform: `translateZ(${layer.value})`,
|
||||
position: "fixed",
|
||||
inset: "0",
|
||||
display: "flex",
|
||||
"justify-content": "center",
|
||||
"align-items": "center",
|
||||
width: "100dvw",
|
||||
height: "100dvh",
|
||||
"background-color": "#00000080",
|
||||
})) {
|
||||
newContainer.style.setProperty(key, value);
|
||||
}
|
||||
layer.value++;
|
||||
|
||||
const container = document.querySelector(".modals-container")!
|
||||
.appendChild(newContainer);
|
||||
let container: HTMLDivElement;
|
||||
if (!data.isTopNotice) {
|
||||
const newContainer = document.createElement("div");
|
||||
for (const [key, value] of Object.entries({
|
||||
...data.style,
|
||||
"transform": `translateZ(${layer.value})`,
|
||||
"position": "fixed",
|
||||
"inset": "0",
|
||||
"display": "flex",
|
||||
"justify-content": "center",
|
||||
"align-items": "center",
|
||||
"width": "100dvw",
|
||||
"height": "100dvh",
|
||||
"background-color": "#00000080",
|
||||
})) {
|
||||
newContainer.style.setProperty(key, value);
|
||||
}
|
||||
|
||||
container = document.querySelector(".modals-container")!
|
||||
.appendChild(newContainer);
|
||||
} else {
|
||||
container = document.querySelector(".top-notice-container")!
|
||||
.appendChild(document.createElement("div"));
|
||||
}
|
||||
|
||||
let app: ReturnType<typeof createApp>;
|
||||
|
||||
const close = () => {
|
||||
data.onClose?.();
|
||||
const close = (closeData?: any) => {
|
||||
data.onClose?.(closeData);
|
||||
layer.value--;
|
||||
app.unmount();
|
||||
container.remove();
|
||||
@@ -49,4 +58,14 @@ export const createModal = <T extends Component>(data: {
|
||||
app.mount(container);
|
||||
|
||||
return close;
|
||||
}
|
||||
}
|
||||
|
||||
export const createTopNotice = (data?: {
|
||||
style?: Record<string, string>,
|
||||
props?: Record<string, ((...args: any[]) => any) | any>,
|
||||
onClose?: (...args: any) => any
|
||||
}) => createModal({
|
||||
...data,
|
||||
component: TopNotice,
|
||||
isTopNotice: true,
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
export function DateParse(date: string | Date, startDate: Date = new Date()) {
|
||||
const diffMs = startDate.getTime() - new Date(date).getTime();
|
||||
const diffSec = Math.abs(Math.floor(diffMs / 1000));
|
||||
const diffMin = Math.abs(Math.floor(diffSec / 60));
|
||||
const diffHour = Math.abs(Math.floor(diffMin / 60));
|
||||
const diffDay = Math.abs(Math.floor(diffHour / 24));
|
||||
const diffMonth = Math.abs(Math.floor(diffDay / 30));
|
||||
const diffYear = Math.abs(Math.floor(diffMonth / 12));
|
||||
|
||||
const diffStr = diffMs < 0
|
||||
? "後"
|
||||
: "前";
|
||||
|
||||
switch (true) {
|
||||
case diffSec < 60:
|
||||
return `${diffSec}秒${diffStr}`;
|
||||
case diffMin < 60:
|
||||
return `${diffMin}分${diffStr}`;
|
||||
case diffHour < 24:
|
||||
return `${diffHour}時間${diffStr}`;
|
||||
case diffDay < 30:
|
||||
return `${diffDay}日${diffStr}`;
|
||||
case diffMonth < 12:
|
||||
return `${diffMonth}ヶ月${diffStr}`;
|
||||
default:
|
||||
return `${diffYear}年${diffStr}`;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,63 @@
|
||||
const swSelf = globalThis as unknown as ServiceWorkerGlobalScope;
|
||||
|
||||
const resources = {
|
||||
"general": [
|
||||
"/assets/lynqchat.svg",
|
||||
],
|
||||
};
|
||||
|
||||
// @ts-expect-error
|
||||
const manifest = self.__WB_MANIFEST || [];
|
||||
manifest.forEach((entry: any) => {
|
||||
const url = typeof entry === "string"
|
||||
? entry
|
||||
: entry.url;
|
||||
resources.general.push(url);
|
||||
});
|
||||
|
||||
swSelf.addEventListener("install", (event) => {
|
||||
event.waitUntil(swSelf.skipWaiting());
|
||||
swSelf.skipWaiting();
|
||||
|
||||
event.waitUntil((async () => {
|
||||
await Promise.all(Object.entries(resources).map(async ([name, assets]) => {
|
||||
const cache = await caches.open(name);
|
||||
|
||||
for (const asset of assets) {
|
||||
try {
|
||||
await cache.add(asset);
|
||||
} catch (error) {
|
||||
console.error(`Failed to cache asset: ${asset}`, error);
|
||||
}
|
||||
}
|
||||
}));
|
||||
})());
|
||||
});
|
||||
|
||||
swSelf.addEventListener("activate", (event) => {
|
||||
event.waitUntil(swSelf.clients.claim());
|
||||
event.waitUntil((async () => {
|
||||
await swSelf.clients.claim();
|
||||
})());
|
||||
});
|
||||
|
||||
swSelf.addEventListener("fetch", (event) => {
|
||||
const request = event.request;
|
||||
|
||||
if (request.url.indexOf("http") !== 0)
|
||||
if (request.method !== "GET")
|
||||
return;
|
||||
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.origin !== location.origin)
|
||||
return;
|
||||
|
||||
event.respondWith((async () => {
|
||||
const cached = await caches.match(request);
|
||||
if (cached)
|
||||
return cached;
|
||||
|
||||
try {
|
||||
const res = await fetch(request);
|
||||
return res;
|
||||
} catch (err) {
|
||||
return new Response("Network error", {
|
||||
status: 504,
|
||||
});
|
||||
return await fetch(request);
|
||||
} catch (error) {
|
||||
return new Response("Network error", { status: 504 });
|
||||
}
|
||||
})());
|
||||
});
|
||||
Reference in New Issue
Block a user