Merge branch 'develop' of https://codeberg.org/calckey/calckey into note-improvements
This commit is contained in:
commit
6639d7d42c
|
@ -1400,6 +1400,7 @@ _profile:
|
||||||
metadataContent: "Content"
|
metadataContent: "Content"
|
||||||
changeAvatar: "Change avatar"
|
changeAvatar: "Change avatar"
|
||||||
changeBanner: "Change banner"
|
changeBanner: "Change banner"
|
||||||
|
locationDescription: "If entered properly, this will display your local time to other users."
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "All posts"
|
allNotes: "All posts"
|
||||||
followingList: "Followed users"
|
followingList: "Followed users"
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "calckey",
|
"name": "calckey",
|
||||||
"version": "13.2.0-dev10",
|
"version": "13.2.0-dev11",
|
||||||
"codename": "aqua",
|
"codename": "aqua",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://codeberg.org/calckey/calckey.git"
|
"url": "https://codeberg.org/calckey/calckey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@7.27.0",
|
"packageManager": "pnpm@7.27.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",
|
"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",
|
||||||
|
|
|
@ -122,7 +122,6 @@ export async function createNote(
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
||||||
|
|
||||||
logger.info(`Creating the Note: ${note.id}`);
|
logger.info(`Creating the Note: ${note.id}`);
|
||||||
|
|
||||||
// Skip if note is made before 2007 (1yr before Fedi was created)
|
// Skip if note is made before 2007 (1yr before Fedi was created)
|
||||||
|
|
|
@ -54,7 +54,7 @@ export function apiMastodonCompatible(router: Router): void {
|
||||||
// displayed without being logged in
|
// displayed without being logged in
|
||||||
try {
|
try {
|
||||||
const data = await client.getInstance();
|
const data = await client.getInstance();
|
||||||
ctx.body = getInstance(data.data);
|
ctx.body = await getInstance(data.data);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
|
|
|
@ -98,20 +98,23 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
"/v1/accounts/:id(^.*\\d.*$)",
|
||||||
const accessTokens = ctx.headers.authorization;
|
async (ctx) => {
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
try {
|
const accessTokens = ctx.headers.authorization;
|
||||||
const data = await client.getAccount(ctx.params.id);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
ctx.body = data.data;
|
try {
|
||||||
} catch (e: any) {
|
const data = await client.getAccount(ctx.params.id);
|
||||||
console.error(e);
|
ctx.body = data.data;
|
||||||
console.error(e.response.data);
|
} catch (e: any) {
|
||||||
ctx.status = 401;
|
console.error(e);
|
||||||
ctx.body = e.response.data;
|
console.error(e.response.data);
|
||||||
}
|
ctx.status = 401;
|
||||||
});
|
ctx.body = e.response.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
router.get<{ Params: { id: string } }>(
|
router.get<{ Params: { id: string } }>(
|
||||||
"/v1/accounts/:id/statuses",
|
"/v1/accounts/:id/statuses",
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
|
@ -304,11 +307,12 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
let users;
|
let users;
|
||||||
try {
|
try {
|
||||||
const idsRaw = ctx.request.body ? ["id[]"] : null;
|
// TODO: this should be body
|
||||||
|
const idsRaw = ctx.request.query ? ctx.request.query["id[]"] : null;
|
||||||
const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw;
|
const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw;
|
||||||
users = ids;
|
users = ids;
|
||||||
relationshopModel.id = idsRaw?.toString() || "1";
|
relationshopModel.id = idsRaw?.toString() || "1";
|
||||||
if (!(idsRaw && ids)) {
|
if (!idsRaw) {
|
||||||
ctx.body = [relationshopModel];
|
ctx.body = [relationshopModel];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -316,9 +320,11 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
console.error(e.response.data);
|
let data = e.response.data;
|
||||||
|
data.users = users;
|
||||||
|
console.error(data);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get("/v1/bookmarks", async (ctx) => {
|
router.get("/v1/bookmarks", async (ctx) => {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { Entity } from "@calckey/megalodon";
|
import { Entity } from "@calckey/megalodon";
|
||||||
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
|
|
||||||
// TODO: add calckey features
|
// TODO: add calckey features
|
||||||
export function getInstance(response: Entity.Instance) {
|
export async function getInstance(response: Entity.Instance) {
|
||||||
|
const meta = await fetchMeta(true);
|
||||||
return {
|
return {
|
||||||
uri: response.uri,
|
uri: response.uri,
|
||||||
title: response.title || "",
|
title: response.title || "",
|
||||||
|
@ -11,8 +14,8 @@ export function getInstance(response: Entity.Instance) {
|
||||||
urls: response.urls,
|
urls: response.urls,
|
||||||
stats: response.stats,
|
stats: response.stats,
|
||||||
thumbnail: response.thumbnail || "",
|
thumbnail: response.thumbnail || "",
|
||||||
languages: ["en", "de", "ja"],
|
languages: meta.langs,
|
||||||
registrations: response.registrations,
|
registrations: !meta.disableRegistration || response.registrations,
|
||||||
approval_required: !response.registrations,
|
approval_required: !response.registrations,
|
||||||
invites_enabled: response.registrations,
|
invites_enabled: response.registrations,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@ -77,17 +80,17 @@ export function getInstance(response: Entity.Instance) {
|
||||||
bot: true,
|
bot: true,
|
||||||
discoverable: false,
|
discoverable: false,
|
||||||
group: false,
|
group: false,
|
||||||
created_at: "1971-01-01T00:00:00.000Z",
|
created_at: Math.floor(new Date().getTime() / 1000),
|
||||||
note: "",
|
note: "Please refer to the original instance for the actual admin contact.",
|
||||||
url: "https://http.cat/404",
|
url: "/",
|
||||||
avatar: "https://http.cat/404",
|
avatar: "/static-assets/badges/info.png",
|
||||||
avatar_static: "https://http.cat/404",
|
avatar_static: "/static-assets/badges/info.png",
|
||||||
header: "https://http.cat/404",
|
header: "https://http.cat/404",
|
||||||
header_static: "https://http.cat/404",
|
header_static: "https://http.cat/404",
|
||||||
followers_count: -1,
|
followers_count: -1,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
statuses_count: 0,
|
statuses_count: 0,
|
||||||
last_status_at: "1971-01-01T00:00:00.000Z",
|
last_status_at: Math.floor(new Date().getTime() / 1000),
|
||||||
noindex: true,
|
noindex: true,
|
||||||
emojis: [],
|
emojis: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
|
|
|
@ -404,7 +404,7 @@ async function getFirstReaction(
|
||||||
) {
|
) {
|
||||||
const accessTokenArr = accessTokens?.split(" ") ?? [null];
|
const accessTokenArr = accessTokens?.split(" ") ?? [null];
|
||||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||||
let react = "👍";
|
let react = "⭐";
|
||||||
try {
|
try {
|
||||||
const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, {
|
const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, {
|
||||||
scope: ["client", "base"],
|
scope: ["client", "base"],
|
||||||
|
@ -412,7 +412,7 @@ async function getFirstReaction(
|
||||||
i: accessToken,
|
i: accessToken,
|
||||||
});
|
});
|
||||||
const reactRaw = api.data;
|
const reactRaw = api.data;
|
||||||
react = Array.isArray(reactRaw) ? api.data[0] : "👍";
|
react = Array.isArray(reactRaw) ? api.data[0] : "⭐";
|
||||||
console.log(api.data);
|
console.log(api.data);
|
||||||
return react;
|
return react;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -426,16 +426,16 @@ export function statusModel(
|
||||||
emojis: MastodonEntity.Emoji[],
|
emojis: MastodonEntity.Emoji[],
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
const now = "1970-01-02T00:00:00.000Z";
|
const now = Math.floor(new Date().getTime() / 1000);
|
||||||
return {
|
return {
|
||||||
id: "9atm5frjhb",
|
id: "9atm5frjhb",
|
||||||
uri: "https://http.cat/404", // ""
|
uri: "https://http.cat/404", // ""
|
||||||
url: "https://http.cat/404", // "",
|
url: "https://http.cat/404", // "",
|
||||||
account: {
|
account: {
|
||||||
id: "9arzuvv0sw",
|
id: "9arzuvv0sw",
|
||||||
username: "ReactionBot",
|
username: "Reactions",
|
||||||
acct: "ReactionBot",
|
acct: "Reactions",
|
||||||
display_name: "ReactionsToThisPost",
|
display_name: "Reactions to this post",
|
||||||
locked: false,
|
locked: false,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
followers_count: 0,
|
followers_count: 0,
|
||||||
|
@ -443,8 +443,8 @@ export function statusModel(
|
||||||
statuses_count: 0,
|
statuses_count: 0,
|
||||||
note: "",
|
note: "",
|
||||||
url: "https://http.cat/404",
|
url: "https://http.cat/404",
|
||||||
avatar: "https://http.cat/404",
|
avatar: "/static-assets/badges/info.png",
|
||||||
avatar_static: "https://http.cat/404",
|
avatar_static: "/static-assets/badges/info.png",
|
||||||
header: "https://http.cat/404", // ""
|
header: "https://http.cat/404", // ""
|
||||||
header_static: "https://http.cat/404", // ""
|
header_static: "https://http.cat/404", // ""
|
||||||
emojis: [],
|
emojis: [],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
const account = JSON.parse(localStorage.getItem('account'));
|
const account = JSON.parse(localStorage.getItem("account"));
|
||||||
const i = account.token;
|
const i = account.token;
|
||||||
|
|
||||||
const api = (endpoint, data = {}) => {
|
const api = (endpoint, data = {}) => {
|
||||||
|
@ -10,42 +10,44 @@ window.onload = async () => {
|
||||||
if (i) data.i = i;
|
if (i) data.i = i;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
fetch(endpoint.indexOf("://") > -1 ? endpoint : `/api/${endpoint}`, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
credentials: 'omit',
|
credentials: "omit",
|
||||||
cache: 'no-cache'
|
cache: "no-cache",
|
||||||
}).then(async (res) => {
|
})
|
||||||
const body = res.status === 204 ? null : await res.json();
|
.then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resolve(body);
|
resolve(body);
|
||||||
} else if (res.status === 204) {
|
} else if (res.status === 204) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject(body.error);
|
reject(body.error);
|
||||||
}
|
}
|
||||||
}).catch(reject);
|
})
|
||||||
|
.catch(reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
const content = document.getElementById('content');
|
const content = document.getElementById("content");
|
||||||
|
|
||||||
document.getElementById('ls').addEventListener('click', () => {
|
document.getElementById("ls").addEventListener("click", () => {
|
||||||
content.innerHTML = '';
|
content.innerHTML = "";
|
||||||
|
|
||||||
const lsEditor = document.createElement('div');
|
const lsEditor = document.createElement("div");
|
||||||
lsEditor.id = 'lsEditor';
|
lsEditor.id = "lsEditor";
|
||||||
|
|
||||||
const adder = document.createElement('div');
|
const adder = document.createElement("div");
|
||||||
adder.classList.add('adder');
|
adder.classList.add("adder");
|
||||||
const addKeyInput = document.createElement('input');
|
const addKeyInput = document.createElement("input");
|
||||||
const addValueTextarea = document.createElement('textarea');
|
const addValueTextarea = document.createElement("textarea");
|
||||||
const addButton = document.createElement('button');
|
const addButton = document.createElement("button");
|
||||||
addButton.textContent = 'Add';
|
addButton.textContent = "Add";
|
||||||
addButton.addEventListener('click', () => {
|
addButton.addEventListener("click", () => {
|
||||||
localStorage.setItem(addKeyInput.value, addValueTextarea.value);
|
localStorage.setItem(addKeyInput.value, addValueTextarea.value);
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
|
@ -57,21 +59,21 @@ window.onload = async () => {
|
||||||
|
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const k = localStorage.key(i);
|
const k = localStorage.key(i);
|
||||||
const record = document.createElement('div');
|
const record = document.createElement("div");
|
||||||
record.classList.add('record');
|
record.classList.add("record");
|
||||||
const header = document.createElement('header');
|
const header = document.createElement("header");
|
||||||
header.textContent = k;
|
header.textContent = k;
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
textarea.textContent = localStorage.getItem(k);
|
textarea.textContent = localStorage.getItem(k);
|
||||||
const saveButton = document.createElement('button');
|
const saveButton = document.createElement("button");
|
||||||
saveButton.textContent = 'Save';
|
saveButton.textContent = "Save";
|
||||||
saveButton.addEventListener('click', () => {
|
saveButton.addEventListener("click", () => {
|
||||||
localStorage.setItem(k, textarea.value);
|
localStorage.setItem(k, textarea.value);
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
const removeButton = document.createElement('button');
|
const removeButton = document.createElement("button");
|
||||||
removeButton.textContent = 'Remove';
|
removeButton.textContent = "Remove";
|
||||||
removeButton.addEventListener('click', () => {
|
removeButton.addEventListener("click", () => {
|
||||||
localStorage.removeItem(k);
|
localStorage.removeItem(k);
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,120 +9,122 @@
|
||||||
* 注: webpackは介さないため、このファイルではrequireやimportは使えません。
|
* 注: webpackは介さないため、このファイルではrequireやimportは使えません。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
|
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
|
||||||
(async () => {
|
(async () => {
|
||||||
window.onerror = (e) => {
|
window.onerror = (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
renderError('SOMETHING_HAPPENED', e);
|
renderError("SOMETHING_HAPPENED", e);
|
||||||
};
|
};
|
||||||
window.onunhandledrejection = (e) => {
|
window.onunhandledrejection = (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
|
renderError("SOMETHING_HAPPENED_IN_PROMISE", e);
|
||||||
};
|
};
|
||||||
|
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
const v = localStorage.getItem('v') || VERSION;
|
const v = localStorage.getItem("v") || VERSION;
|
||||||
|
|
||||||
const supportedLangs = LANGS;
|
const supportedLangs = LANGS;
|
||||||
let lang = localStorage.getItem('lang');
|
let lang = localStorage.getItem("lang");
|
||||||
if (lang == null || !supportedLangs.includes(lang)) {
|
if (lang == null || !supportedLangs.includes(lang)) {
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
if (supportedLangs.includes(navigator.language)) {
|
||||||
lang = navigator.language;
|
lang = navigator.language;
|
||||||
} else {
|
} else {
|
||||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
lang = supportedLangs.find((x) => x.split("-")[0] === navigator.language);
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
if (lang == null) lang = 'en-US';
|
if (lang == null) lang = "en-US";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(`/assets/locales/${lang}.${v}.json`);
|
const res = await fetch(`/assets/locales/${lang}.${v}.json`);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
localStorage.setItem('lang', lang);
|
localStorage.setItem("lang", lang);
|
||||||
localStorage.setItem('locale', await res.text());
|
localStorage.setItem("locale", await res.text());
|
||||||
localStorage.setItem('localeVersion', v);
|
localStorage.setItem("localeVersion", v);
|
||||||
} else {
|
} else {
|
||||||
await checkUpdate();
|
await checkUpdate();
|
||||||
renderError('LOCALE_FETCH');
|
renderError("LOCALE_FETCH");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Script
|
//#region Script
|
||||||
function importAppScript() {
|
function importAppScript() {
|
||||||
import(`/assets/${CLIENT_ENTRY}`)
|
import(`/assets/${CLIENT_ENTRY}`).catch(async (e) => {
|
||||||
.catch(async e => {
|
await checkUpdate();
|
||||||
await checkUpdate();
|
console.error(e);
|
||||||
console.error(e);
|
renderError("APP_IMPORT", e);
|
||||||
renderError('APP_IMPORT', e);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある
|
// タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある
|
||||||
if (document.readyState !== 'loading') {
|
if (document.readyState !== "loading") {
|
||||||
importAppScript();
|
importAppScript();
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
importAppScript();
|
importAppScript();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Theme
|
//#region Theme
|
||||||
const theme = localStorage.getItem('theme');
|
const theme = localStorage.getItem("theme");
|
||||||
if (theme) {
|
if (theme) {
|
||||||
for (const [k, v] of Object.entries(JSON.parse(theme))) {
|
for (const [k, v] of Object.entries(JSON.parse(theme))) {
|
||||||
document.documentElement.style.setProperty(`--${k}`, v.toString());
|
document.documentElement.style.setProperty(`--${k}`, v.toString());
|
||||||
|
|
||||||
// HTMLの theme-color 適用
|
// HTMLの theme-color 適用
|
||||||
if (k === 'htmlThemeColor') {
|
if (k === "htmlThemeColor") {
|
||||||
for (const tag of document.head.children) {
|
for (const tag of document.head.children) {
|
||||||
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
if (
|
||||||
tag.setAttribute('content', v);
|
tag.tagName === "META" &&
|
||||||
|
tag.getAttribute("name") === "theme-color"
|
||||||
|
) {
|
||||||
|
tag.setAttribute("content", v);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const colorSchema = localStorage.getItem('colorSchema');
|
const colorSchema = localStorage.getItem("colorSchema");
|
||||||
if (colorSchema) {
|
if (colorSchema) {
|
||||||
document.documentElement.style.setProperty('color-schema', colorSchema);
|
document.documentElement.style.setProperty("color-schema", colorSchema);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
const fontSize = localStorage.getItem('fontSize');
|
const fontSize = localStorage.getItem("fontSize");
|
||||||
if (fontSize) {
|
if (fontSize) {
|
||||||
document.documentElement.classList.add('f-' + fontSize);
|
document.documentElement.classList.add("f-" + fontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useSystemFont = localStorage.getItem('useSystemFont');
|
const useSystemFont = localStorage.getItem("useSystemFont");
|
||||||
if (useSystemFont) {
|
if (useSystemFont) {
|
||||||
document.documentElement.classList.add('useSystemFont');
|
document.documentElement.classList.add("useSystemFont");
|
||||||
}
|
}
|
||||||
|
|
||||||
const wallpaper = localStorage.getItem('wallpaper');
|
const wallpaper = localStorage.getItem("wallpaper");
|
||||||
if (wallpaper) {
|
if (wallpaper) {
|
||||||
document.documentElement.style.backgroundImage = `url(${wallpaper})`;
|
document.documentElement.style.backgroundImage = `url(${wallpaper})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const customCss = localStorage.getItem('customCss');
|
const customCss = localStorage.getItem("customCss");
|
||||||
if (customCss && customCss.length > 0) {
|
if (customCss && customCss.length > 0) {
|
||||||
const style = document.createElement('style');
|
const style = document.createElement("style");
|
||||||
style.innerHTML = customCss;
|
style.innerHTML = customCss;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addStyle(styleText) {
|
async function addStyle(styleText) {
|
||||||
let css = document.createElement('style');
|
let css = document.createElement("style");
|
||||||
css.appendChild(document.createTextNode(styleText));
|
css.appendChild(document.createTextNode(styleText));
|
||||||
document.head.appendChild(css);
|
document.head.appendChild(css);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderError(code, details) {
|
function renderError(code, details) {
|
||||||
let errorsElement = document.getElementById('errors');
|
let errorsElement = document.getElementById("errors");
|
||||||
|
|
||||||
if (!errorsElement) {
|
if (!errorsElement) {
|
||||||
document.body.innerHTML = `
|
document.body.innerHTML = `
|
||||||
|
@ -158,9 +160,9 @@
|
||||||
<br>
|
<br>
|
||||||
<div id="errors"></div>
|
<div id="errors"></div>
|
||||||
`;
|
`;
|
||||||
errorsElement = document.getElementById('errors');
|
errorsElement = document.getElementById("errors");
|
||||||
}
|
}
|
||||||
const detailsElement = document.createElement('details');
|
const detailsElement = document.createElement("details");
|
||||||
detailsElement.innerHTML = `
|
detailsElement.innerHTML = `
|
||||||
<br>
|
<br>
|
||||||
<summary>
|
<summary>
|
||||||
|
@ -278,25 +280,25 @@
|
||||||
details {
|
details {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
`)
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkUpdate() {
|
async function checkUpdate() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/meta', {
|
const res = await fetch("/api/meta", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
cache: 'no-cache'
|
cache: "no-cache",
|
||||||
});
|
});
|
||||||
|
|
||||||
const meta = await res.json();
|
const meta = await res.json();
|
||||||
|
|
||||||
if (meta.version != v) {
|
if (meta.version != v) {
|
||||||
localStorage.setItem('v', meta.version);
|
localStorage.setItem("v", meta.version);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
renderError('UPDATE_CHECK', e);
|
renderError("UPDATE_CHECK", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,9 +306,9 @@
|
||||||
function refresh() {
|
function refresh() {
|
||||||
// Clear cache (service worker)
|
// Clear cache (service worker)
|
||||||
try {
|
try {
|
||||||
navigator.serviceWorker.controller.postMessage('clear');
|
navigator.serviceWorker.controller.postMessage("clear");
|
||||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||||
registrations.forEach(registration => registration.unregister());
|
registrations.forEach((registration) => registration.unregister());
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
const account = JSON.parse(localStorage.getItem('account'));
|
const account = JSON.parse(localStorage.getItem("account"));
|
||||||
const i = account.token;
|
const i = account.token;
|
||||||
|
|
||||||
const api = (endpoint, data = {}) => {
|
const api = (endpoint, data = {}) => {
|
||||||
|
@ -10,42 +10,44 @@ window.onload = async () => {
|
||||||
if (i) data.i = i;
|
if (i) data.i = i;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
fetch(endpoint.indexOf("://") > -1 ? endpoint : `/api/${endpoint}`, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
credentials: 'omit',
|
credentials: "omit",
|
||||||
cache: 'no-cache'
|
cache: "no-cache",
|
||||||
}).then(async (res) => {
|
})
|
||||||
const body = res.status === 204 ? null : await res.json();
|
.then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resolve(body);
|
resolve(body);
|
||||||
} else if (res.status === 204) {
|
} else if (res.status === 204) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject(body.error);
|
reject(body.error);
|
||||||
}
|
}
|
||||||
}).catch(reject);
|
})
|
||||||
|
.catch(reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('submit').addEventListener('click', () => {
|
document.getElementById("submit").addEventListener("click", () => {
|
||||||
api('notes/create', {
|
api("notes/create", {
|
||||||
text: document.getElementById('text').value
|
text: document.getElementById("text").value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
api('notes/timeline').then(notes => {
|
api("notes/timeline").then((notes) => {
|
||||||
const tl = document.getElementById('tl');
|
const tl = document.getElementById("tl");
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement("div");
|
||||||
const name = document.createElement('header');
|
const name = document.createElement("header");
|
||||||
name.textContent = `${note.user.name} @${note.user.username}`;
|
name.textContent = `${note.user.name} @${note.user.username}`;
|
||||||
const text = document.createElement('div');
|
const text = document.createElement("div");
|
||||||
text.textContent = `${note.text}`;
|
text.textContent = `${note.text}`;
|
||||||
el.appendChild(name);
|
el.appendChild(name);
|
||||||
el.appendChild(text);
|
el.appendChild(text);
|
||||||
|
|
|
@ -23,7 +23,7 @@ block og
|
||||||
meta(property='og:description' content= summary)
|
meta(property='og:description' content= summary)
|
||||||
meta(property='og:url' content= url)
|
meta(property='og:url' content= url)
|
||||||
meta(property='og:image' content= imageUrl)
|
meta(property='og:image' content= imageUrl)
|
||||||
if isImage
|
if isImage && !note.files[0].isSensitive
|
||||||
meta(property='og:image:width' content=note.files[0].properties.width)
|
meta(property='og:image:width' content=note.files[0].properties.width)
|
||||||
meta(property='og:image:height' content=note.files[0].properties.height)
|
meta(property='og:image:height' content=note.files[0].properties.height)
|
||||||
meta(property='og:image:type' content=note.files[0].type)
|
meta(property='og:image:type' content=note.files[0].type)
|
||||||
|
|
|
@ -6,13 +6,9 @@
|
||||||
"build": "pnpm vite build",
|
"build": "pnpm vite build",
|
||||||
"lint": "pnpm rome check \"src/**/*.{ts,vue}\""
|
"lint": "pnpm rome check \"src/**/*.{ts,vue}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"@khmyznikov/pwa-install": "^0.2.0",
|
|
||||||
"chartjs-chart-matrix": "^2.0.1",
|
|
||||||
"gsap": "^3.11.4"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@discordapp/twemoji": "14.0.2",
|
"@discordapp/twemoji": "14.0.2",
|
||||||
|
"@khmyznikov/pwa-install": "^0.2.0",
|
||||||
"@rollup/plugin-alias": "3.1.9",
|
"@rollup/plugin-alias": "3.1.9",
|
||||||
"@rollup/plugin-json": "4.1.0",
|
"@rollup/plugin-json": "4.1.0",
|
||||||
"@rollup/pluginutils": "^4.2.1",
|
"@rollup/pluginutils": "^4.2.1",
|
||||||
|
@ -40,6 +36,8 @@
|
||||||
"chartjs-adapter-date-fns": "2.0.1",
|
"chartjs-adapter-date-fns": "2.0.1",
|
||||||
"chartjs-plugin-gradient": "0.5.1",
|
"chartjs-plugin-gradient": "0.5.1",
|
||||||
"chartjs-plugin-zoom": "1.2.1",
|
"chartjs-plugin-zoom": "1.2.1",
|
||||||
|
"chartjs-chart-matrix": "^2.0.1",
|
||||||
|
"city-timezones": "^1.2.1",
|
||||||
"compare-versions": "5.0.3",
|
"compare-versions": "5.0.3",
|
||||||
"cropperjs": "2.0.0-beta.2",
|
"cropperjs": "2.0.0-beta.2",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
|
@ -47,6 +45,7 @@
|
||||||
"date-fns": "2.29.3",
|
"date-fns": "2.29.3",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eventemitter3": "4.0.7",
|
"eventemitter3": "4.0.7",
|
||||||
|
"gsap": "^3.11.4",
|
||||||
"idb-keyval": "6.2.0",
|
"idb-keyval": "6.2.0",
|
||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :prefer-type="'dialog'" :z-priority="'high'" @click="done(true)" @closed="emit('closed')">
|
<MkModal ref="modal" :prefer-type="'dialog'" :z-priority="'high'" @click="done(true)" @closed="emit('closed')">
|
||||||
<div class="mk-dialog">
|
<div :class="$style.root">
|
||||||
<div v-if="icon" class="icon">
|
<div v-if="icon" :class="$style.icon">
|
||||||
<i :class="icon"></i>
|
<i :class="icon"></i>
|
||||||
</div>
|
|
||||||
<div v-else-if="!input && !select" class="icon" :class="type">
|
|
||||||
<i v-if="type === 'success'" class="ph-check-bold ph-lg"></i>
|
|
||||||
<i v-else-if="type === 'error'" class="ph-circle-wavy-warning-bold ph-lg"></i>
|
|
||||||
<i v-else-if="type === 'warning'" class="ph-warning-bold ph-lg"></i>
|
|
||||||
<i v-else-if="type === 'info'" class="ph-info-bold ph-lg"></i>
|
|
||||||
<i v-else-if="type === 'question'" class="ph-question-bold ph-lg"></i>
|
|
||||||
<i v-else-if="type === 'waiting'" class="ph-circle-notch-bold ph-lg fa-pulse"></i>
|
|
||||||
</div>
|
|
||||||
<header v-if="title"><Mfm :text="title"/></header>
|
|
||||||
<div v-if="text" class="body"><Mfm :text="text"/></div>
|
|
||||||
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" @keydown="onInputKeydown">
|
|
||||||
<template v-if="input.type === 'password'" #prefix><i class="ph-lock-bold ph-lg"></i></template>
|
|
||||||
</MkInput>
|
|
||||||
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
|
||||||
<template v-if="select.items">
|
|
||||||
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label">
|
|
||||||
<option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option>
|
|
||||||
</optgroup>
|
|
||||||
</template>
|
|
||||||
</MkSelect>
|
|
||||||
<div v-if="(showOkButton || showCancelButton) && !actions" class="buttons">
|
|
||||||
<div v-if="!isYesNo">
|
|
||||||
<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt }}</MkButton>
|
|
||||||
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else-if="!input && !select" :class="[$style.icon, $style['type_' + type]]">
|
||||||
<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ i18n.ts.yes }}</MkButton>
|
<i v-if="type === 'success'" :class="$style.iconInner" class="ph-check-bold ph-lg"></i>
|
||||||
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.no }}</MkButton>
|
<i v-else-if="type === 'error'" :class="$style.iconInner" class="ph-circle-wavy-warning-bold ph-lg"></i>
|
||||||
|
<i v-else-if="type === 'warning'" :class="$style.iconInner" class="ph-warning-bold ph-lg"></i>
|
||||||
|
<i v-else-if="type === 'info'" :class="$style.iconInner" class="ph-info-bold ph-lg"></i>
|
||||||
|
<i v-else-if="type === 'question'" :class="$style.iconInner" class="ph-circle-question-bold ph-lg"></i>
|
||||||
|
<MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/>
|
||||||
|
</div>
|
||||||
|
<header v-if="title" :class="$style.title"><Mfm :text="title"/></header>
|
||||||
|
<div v-if="text" :class="$style.text"><Mfm :text="text"/></div>
|
||||||
|
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" @keydown="onInputKeydown">
|
||||||
|
<template v-if="input.type === 'password'" #prefix><i class="ph-password-bold ph-lg"></i></template>
|
||||||
|
</MkInput>
|
||||||
|
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
||||||
|
<template v-if="select.items">
|
||||||
|
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label">
|
||||||
|
<option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option>
|
||||||
|
</optgroup>
|
||||||
|
</template>
|
||||||
|
</MkSelect>
|
||||||
|
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
||||||
|
<div v-if="!isYesNo">
|
||||||
|
<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt }}</MkButton>
|
||||||
|
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ i18n.ts.yes }}</MkButton>
|
||||||
|
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.no }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="actions" :class="$style.buttons">
|
||||||
|
<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); modal?.close(); }">{{ action.text }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="actions" class="buttons">
|
</MkModal>
|
||||||
<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkModal>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/form/input.vue';
|
import MkInput from '@/components/form/input.vue';
|
||||||
|
@ -88,12 +88,16 @@ const props = withDefaults(defineProps<{
|
||||||
showOkButton?: boolean;
|
showOkButton?: boolean;
|
||||||
showCancelButton?: boolean;
|
showCancelButton?: boolean;
|
||||||
isYesNo?: boolean;
|
isYesNo?: boolean;
|
||||||
|
|
||||||
cancelableByBgClick?: boolean;
|
cancelableByBgClick?: boolean;
|
||||||
|
okText?: string;
|
||||||
|
cancelText?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
showOkButton: true,
|
showOkButton: true,
|
||||||
showCancelButton: false,
|
showCancelButton: false,
|
||||||
isYesNo: false,
|
isYesNo: false,
|
||||||
|
|
||||||
cancelableByBgClick: true,
|
cancelableByBgClick: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,7 +106,7 @@ const emit = defineEmits<{
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const modal = ref<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const inputValue = ref(props.input?.default || null);
|
const inputValue = ref(props.input?.default || null);
|
||||||
const selectedValue = ref(props.select?.default || null);
|
const selectedValue = ref(props.select?.default || null);
|
||||||
|
@ -151,9 +155,10 @@ onBeforeUnmount(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.mk-dialog {
|
.root {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin: auto;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
|
@ -161,56 +166,56 @@ onBeforeUnmount(() => {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
> .icon {
|
.icon {
|
||||||
font-size: 32px;
|
font-size: 24px;
|
||||||
|
|
||||||
&.info {
|
& + .title {
|
||||||
color: #55c4dd;
|
margin-top: 8px;
|
||||||
}
|
|
||||||
|
|
||||||
&.success {
|
|
||||||
color: var(--success);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.error {
|
|
||||||
color: var(--error);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.warning {
|
|
||||||
color: var(--warn);
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
& + header {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> header {
|
|
||||||
margin: 0 0 8px 0;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
|
|
||||||
& + .body {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
margin: 16px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .buttons {
|
|
||||||
margin-top: 16px;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconInner {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type_info {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type_success {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type_error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type_warning {
|
||||||
|
color: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
& + .text {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin: 16px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="onOpened">
|
<Transition
|
||||||
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
:name="transitionName"
|
||||||
<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
|
:enter-active-class="$style['transition_' + transitionName + '_enterActive']"
|
||||||
<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick">
|
:leave-active-class="$style['transition_' + transitionName + '_leaveActive']"
|
||||||
<slot :max-height="maxHeight" :type="type"></slot>
|
:enter-from-class="$style['transition_' + transitionName + '_enterFrom']"
|
||||||
|
:leave-to-class="$style['transition_' + transitionName + '_leaveTo']"
|
||||||
|
:duration="transitionDuration" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="onOpened"
|
||||||
|
>
|
||||||
|
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog' || type === 'dialog:top', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
||||||
|
<div class="_modalBg data-cy-bg" :class="[$style.bg, { [$style.bgTransparent]: isEnableBgTransparent, 'data-cy-transparent': isEnableBgTransparent }]" :style="{ zIndex }" @click="onBgClick" @mousedown="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
|
||||||
|
<div ref="content" :class="[$style.content, { [$style.fixed]: fixed, top: type === 'dialog:top' }]" :style="{ zIndex }" @click.self="onBgClick">
|
||||||
|
<slot :max-height="maxHeight" :type="type"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
</transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -61,9 +68,10 @@ let maxHeight = $ref<number>();
|
||||||
let fixed = $ref(false);
|
let fixed = $ref(false);
|
||||||
let transformOrigin = $ref('center');
|
let transformOrigin = $ref('center');
|
||||||
let showing = $ref(true);
|
let showing = $ref(true);
|
||||||
let content = $ref<HTMLElement>();
|
let content = $shallowRef<HTMLElement>();
|
||||||
const zIndex = os.claimZIndex(props.zPriority);
|
const zIndex = os.claimZIndex(props.zPriority);
|
||||||
const type = $computed(() => {
|
let useSendAnime = $ref(false);
|
||||||
|
const type = $computed<ModalTypes>(() => {
|
||||||
if (props.preferType === 'auto') {
|
if (props.preferType === 'auto') {
|
||||||
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
||||||
return 'drawer';
|
return 'drawer';
|
||||||
|
@ -74,19 +82,47 @@ const type = $computed(() => {
|
||||||
return props.preferType!;
|
return props.preferType!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup'));
|
||||||
|
let transitionName = $computed((() =>
|
||||||
|
defaultStore.state.animation
|
||||||
|
? useSendAnime
|
||||||
|
? 'send'
|
||||||
|
: type === 'drawer'
|
||||||
|
? 'modal-drawer'
|
||||||
|
: type === 'popup'
|
||||||
|
? 'modal-popup'
|
||||||
|
: 'modal'
|
||||||
|
: ''
|
||||||
|
));
|
||||||
|
let transitionDuration = $computed((() =>
|
||||||
|
transitionName === 'send'
|
||||||
|
? 400
|
||||||
|
: transitionName === 'modal-popup'
|
||||||
|
? 100
|
||||||
|
: transitionName === 'modal'
|
||||||
|
? 200
|
||||||
|
: transitionName === 'modal-drawer'
|
||||||
|
? 200
|
||||||
|
: 0
|
||||||
|
));
|
||||||
|
|
||||||
let contentClicking = false;
|
let contentClicking = false;
|
||||||
|
|
||||||
const close = () => {
|
function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||||
|
if (opts.useSendAnimation) {
|
||||||
|
useSendAnime = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
if (props.src) props.src.style.pointerEvents = 'auto';
|
if (props.src) props.src.style.pointerEvents = 'auto';
|
||||||
showing = false;
|
showing = false;
|
||||||
emit('close');
|
emit('close');
|
||||||
};
|
}
|
||||||
|
|
||||||
const onBgClick = () => {
|
function onBgClick() {
|
||||||
if (contentClicking) return;
|
if (contentClicking) return;
|
||||||
emit('click');
|
emit('click');
|
||||||
};
|
}
|
||||||
|
|
||||||
if (type === 'drawer') {
|
if (type === 'drawer') {
|
||||||
maxHeight = window.innerHeight / 1.5;
|
maxHeight = window.innerHeight / 1.5;
|
||||||
|
@ -158,21 +194,21 @@ const align = () => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
if (left + width - window.scrollX > window.innerWidth) {
|
||||||
left = window.innerWidth - width + window.pageXOffset - 1;
|
left = window.innerWidth - width + window.scrollX - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset);
|
const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset);
|
||||||
const upperSpace = (srcRect.top - MARGIN);
|
const upperSpace = (srcRect.top - MARGIN);
|
||||||
|
|
||||||
// 画面から縦にはみ出る場合
|
// 画面から縦にはみ出る場合
|
||||||
if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) {
|
if (top + height - window.scrollY > (window.innerHeight - MARGIN)) {
|
||||||
if (props.noOverlap && props.anchor.x === 'center') {
|
if (props.noOverlap && props.anchor.x === 'center') {
|
||||||
if (underSpace >= (upperSpace / 3)) {
|
if (underSpace >= (upperSpace / 3)) {
|
||||||
maxHeight = underSpace;
|
maxHeight = underSpace;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = upperSpace;
|
maxHeight = upperSpace;
|
||||||
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
|
top = window.scrollY + ((upperSpace + MARGIN) - height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
|
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
|
||||||
|
@ -230,6 +266,7 @@ const onOpened = () => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
watch(() => props.src, async () => {
|
watch(() => props.src, async () => {
|
||||||
if (props.src) {
|
if (props.src) {
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
props.src.style.pointerEvents = 'none';
|
props.src.style.pointerEvents = 'none';
|
||||||
}
|
}
|
||||||
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null);
|
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null);
|
||||||
|
@ -251,8 +288,33 @@ defineExpose({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.modal-enter-active, .modal-leave-active {
|
.transition_send_enterActive,
|
||||||
|
.transition_send_leaveActive {
|
||||||
|
> .bg {
|
||||||
|
transition: opacity 0.3s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
transform: translateY(0px);
|
||||||
|
transition: opacity 0.3s ease-in, transform 0.3s cubic-bezier(.5,-0.5,1,.5) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transition_send_enterFrom,
|
||||||
|
.transition_send_leaveTo {
|
||||||
|
> .bg {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-300px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transition_modal_enterActive,
|
||||||
|
.transition_modal_leaveActive {
|
||||||
> .bg {
|
> .bg {
|
||||||
transition: opacity 0.2s !important;
|
transition: opacity 0.2s !important;
|
||||||
}
|
}
|
||||||
|
@ -262,7 +324,8 @@ defineExpose({
|
||||||
transition: opacity 0.2s, transform 0.2s !important;
|
transition: opacity 0.2s, transform 0.2s !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-enter-from, .modal-leave-to {
|
.transition_modal_enterFrom,
|
||||||
|
.transition_modal_leaveTo {
|
||||||
> .bg {
|
> .bg {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -275,7 +338,8 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-popup-enter-active, .modal-popup-leave-active {
|
.transition_modal-popup_enterActive,
|
||||||
|
.transition_modal-popup_leaveActive {
|
||||||
> .bg {
|
> .bg {
|
||||||
transition: opacity 0.2s !important;
|
transition: opacity 0.2s !important;
|
||||||
}
|
}
|
||||||
|
@ -285,7 +349,8 @@ defineExpose({
|
||||||
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
|
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-popup-enter-from, .modal-popup-leave-to {
|
.transition_modal-popup_enterFrom,
|
||||||
|
.transition_modal-popup_leaveTo {
|
||||||
> .bg {
|
> .bg {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +363,7 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-drawer-enter-active {
|
.transition_modal-drawer_enterActive {
|
||||||
> .bg {
|
> .bg {
|
||||||
transition: opacity 0.2s !important;
|
transition: opacity 0.2s !important;
|
||||||
}
|
}
|
||||||
|
@ -307,7 +372,7 @@ defineExpose({
|
||||||
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-drawer-leave-active {
|
.transition_modal-drawer_leaveActive {
|
||||||
> .bg {
|
> .bg {
|
||||||
transition: opacity 0.2s !important;
|
transition: opacity 0.2s !important;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +381,8 @@ defineExpose({
|
||||||
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-drawer-enter-from, .modal-drawer-leave-to {
|
.transition_modal-drawer_enterFrom,
|
||||||
|
.transition_modal-drawer_leaveTo {
|
||||||
> .bg {
|
> .bg {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -327,15 +393,7 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.qzhlnise {
|
.root {
|
||||||
> .bg {
|
|
||||||
&.transparent {
|
|
||||||
background: transparent;
|
|
||||||
-webkit-backdrop-filter: none;
|
|
||||||
backdrop-filter: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dialog {
|
&.dialog {
|
||||||
> .content {
|
> .content {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -366,6 +424,7 @@ defineExpose({
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,6 +458,13 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
&.bgTransparent {
|
||||||
|
background: transparent;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :prefer-type="'dialog'" @click="onBgClick" @closed="$emit('closed')">
|
<MkModal ref="modal" :prefer-type="'dialog'" @click="onBgClick" @closed="$emit('closed')">
|
||||||
<div ref="rootEl" class="ebkgoccj _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
|
<div ref="rootEl" class="ebkgoccj" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
|
||||||
<div ref="headerEl" class="header">
|
<div ref="headerEl" class="header">
|
||||||
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="ph-x-bold ph-lg"></i></button>
|
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="ph-x-bold ph-lg"></i></button>
|
||||||
<span class="title">
|
<span class="title">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</span>
|
</span>
|
||||||
<button v-if="!withOkButton" class="_button" @click="$emit('close')"><i class="ph-x-bold ph-lg"></i></button>
|
<button v-if="!withOkButton" class="_button" @click="$emit('close')"><i class="ph-x-bold ph-lg"></i></button>
|
||||||
<button v-if="withOkButton" class="_button" :disabled="okButtonDisabled" @click="$emit('ok')"><i class="ph-check-bold ph-lg"></i></button>
|
<button v-if="withOkButton" class="_button" :disabled="okButtonDisabled" @click="$emit('ok')"><i class="ph-check-bold ph-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<slot :width="bodyWidth" :height="bodyHeight"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
</MkModal>
|
||||||
<slot :width="bodyWidth" :height="bodyHeight"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkModal>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -41,9 +41,9 @@ const emit = defineEmits<{
|
||||||
(event: 'ok'): void;
|
(event: 'ok'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let rootEl = $ref<HTMLElement>();
|
let rootEl = $shallowRef<HTMLElement>();
|
||||||
let headerEl = $ref<HTMLElement>();
|
let headerEl = $shallowRef<HTMLElement>();
|
||||||
let bodyWidth = $ref(0);
|
let bodyWidth = $ref(0);
|
||||||
let bodyHeight = $ref(0);
|
let bodyHeight = $ref(0);
|
||||||
|
|
||||||
|
@ -85,12 +85,13 @@ defineExpose({
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.ebkgoccj {
|
.ebkgoccj {
|
||||||
|
margin: auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
contain: content;
|
contain: content;
|
||||||
|
container-type: inline-size;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
--root-margin: 24px;
|
--root-margin: 24px;
|
||||||
|
|
||||||
|
@ -145,3 +146,4 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,19 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :prefer-type="'dialog:top'" @click="$refs.modal.close()" @closed="$emit('closed')">
|
<MkModal ref="modal" :prefer-type="'dialog'" @click="modal.close()" @closed="onModalClosed()">
|
||||||
<MkPostForm v-bind="$attrs" @posted="$refs.modal.close()" @cancel="$refs.modal.close()" @esc="$refs.modal.close()"/>
|
<MkPostForm ref="form" style="margin: 0 auto auto auto;" v-bind="props" autofocus freeze-after-posted @posted="onPosted" @cancel="modal.close()" @esc="modal.close()"/>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { } from 'vue';
|
||||||
|
import * as misskey from 'calckey-js';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkPostForm from '@/components/MkPostForm.vue';
|
import MkPostForm from '@/components/MkPostForm.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
reply?: misskey.entities.Note;
|
||||||
MkModal,
|
renote?: misskey.entities.Note;
|
||||||
MkPostForm,
|
channel?: any; // TODO
|
||||||
},
|
mention?: misskey.entities.User;
|
||||||
emits: ['closed'],
|
specified?: misskey.entities.User;
|
||||||
});
|
initialText?: string;
|
||||||
|
initialVisibility?: typeof misskey.noteVisibilities;
|
||||||
|
initialFiles?: misskey.entities.DriveFile[];
|
||||||
|
initialLocalOnly?: boolean;
|
||||||
|
initialVisibleUsers?: misskey.entities.User[];
|
||||||
|
initialNote?: misskey.entities.Note;
|
||||||
|
instant?: boolean;
|
||||||
|
fixed?: boolean;
|
||||||
|
autofocus?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'closed'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
|
function onPosted() {
|
||||||
|
modal.close({
|
||||||
|
useSendAnimation: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onModalClosed() {
|
||||||
|
emit('closed');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :z-priority="'middle'" @click="modal.close()" @closed="$emit('closed')">
|
<MkModal ref="modal" :z-priority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||||
<div class="ewlycnyt">
|
<div :class="$style.root">
|
||||||
<div class="title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
|
<div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
|
||||||
<div class="version">✨ {{ version }} 🚀</div>
|
<div :class="$style.version">✨ {{ version }} 🚀</div>
|
||||||
<div v-if="newRelease" class="releaseNotes">
|
<div v-if="newRelease" :class="$style.releaseNotes">
|
||||||
<Mfm :text="data.notes"/>
|
<Mfm :text="data.notes"/>
|
||||||
<div v-if="data.screenshots.length > 0" style="max-width: 500">
|
<div v-if="data.screenshots.length > 0" style="max-width: 500">
|
||||||
<img v-for="i in data.screenshots" :key="i" :src="i"/>
|
<img v-for="i in data.screenshots" :key="i" :src="i" alt="screenshot"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton class="gotIt" primary full @click="modal.close()">{{ i18n.ts.gotIt }}</MkButton>
|
<MkButton :class="$style.gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkSparkle from '@/components/MkSparkle.vue';
|
import MkSparkle from '@/components/MkSparkle.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
@ -22,7 +23,7 @@ import { version } from '@/config';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
|
||||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
let newRelease = $ref(false);
|
let newRelease = $ref(false);
|
||||||
let data = $ref(Object);
|
let data = $ref(Object);
|
||||||
|
@ -31,6 +32,7 @@ os.api('release').then(res => {
|
||||||
data = res;
|
data = res;
|
||||||
newRelease = (version === data?.version);
|
newRelease = (version === data?.version);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Version: ${version}`)
|
console.log(`Version: ${version}`)
|
||||||
console.log(`Data version: ${data.version}`)
|
console.log(`Data version: ${data.version}`)
|
||||||
console.log(newRelease)
|
console.log(newRelease)
|
||||||
|
@ -38,8 +40,9 @@ console.log(data);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.ewlycnyt {
|
.root {
|
||||||
|
margin: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
|
@ -48,24 +51,23 @@ console.log(data);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
> .title {
|
.title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .version {
|
.version {
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .gotIt {
|
.gotIt {
|
||||||
margin: 8px 0 0 0;
|
margin: 8px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .releaseNotes {
|
.releaseNotes {
|
||||||
|
> img {
|
||||||
> img {
|
border-radius: 10px;
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :prefer-type="'dialog'" :z-priority="'high'" @click="success ? done() : () => {}" @closed="emit('closed')">
|
<MkModal ref="modal" :prefer-type="'dialog'" :z-priority="'high'" @click="success ? done() : () => {}" @closed="emit('closed')">
|
||||||
<div class="iuyakobc" :class="{ iconOnly: (text == null) || success }">
|
<div :class="[$style.root, { [$style.iconOnly]: (text == null) || success }]">
|
||||||
<i v-if="success" class="ph-check-bold ph-lg icon success"></i>
|
<i v-if="success" :class="[$style.icon, $style.success]" class="ph-check-bold ph-lg"></i>
|
||||||
<i v-else class="ph-circle-notch-bold ph-lg fa-pulse icon waiting"></i>
|
<MkLoading v-else :class="[$style.icon, $style.waiting]" :em="true"/>
|
||||||
<div v-if="text && !success" class="text">{{ text }}<MkEllipsis/></div>
|
<div v-if="text && !success" :class="$style.text">{{ text }}<MkEllipsis/></div>
|
||||||
</div>
|
</div>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, ref } from 'vue';
|
import { watch, shallowRef } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
|
|
||||||
const modal = ref<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
@ -27,7 +27,7 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
function done() {
|
function done() {
|
||||||
emit('done');
|
emit('done');
|
||||||
modal.value.close();
|
modal.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.showing, () => {
|
watch(() => props.showing, () => {
|
||||||
|
@ -35,8 +35,9 @@ watch(() => props.showing, () => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.iuyakobc {
|
.root {
|
||||||
|
margin: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -53,21 +54,21 @@ watch(() => props.showing, () => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .icon {
|
.icon {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
|
||||||
|
|
||||||
&.waiting {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .text {
|
&.waiting {
|
||||||
margin-top: 16px;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -15,10 +15,12 @@ const props = withDefaults(defineProps<{
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
colored?: boolean;
|
colored?: boolean;
|
||||||
mini?: boolean;
|
mini?: boolean;
|
||||||
|
em?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
inline: false,
|
inline: false,
|
||||||
colored: true,
|
colored: true,
|
||||||
mini: false,
|
mini: false,
|
||||||
|
em: false,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -70,6 +72,12 @@ const props = withDefaults(defineProps<{
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
--size: 32px;
|
--size: 32px;
|
||||||
}
|
}
|
||||||
|
&.em {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0;
|
||||||
|
--size: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|
|
@ -141,32 +141,32 @@ const calcBg = () => {
|
||||||
let ro: ResizeObserver | null;
|
let ro: ResizeObserver | null;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
calcBg();
|
calcBg();
|
||||||
globalEvents.on('themeChanged', calcBg);
|
globalEvents.on('themeChanged', calcBg);
|
||||||
|
|
||||||
const updateTabHighlight = () => {
|
watch(() => [props.tab, props.tabs], () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const tabEl = tabRefs[props.tab];
|
const tabEl = tabRefs[props.tab];
|
||||||
if (tabEl && tabHighlightEl) {
|
if (tabEl && tabHighlightEl) {
|
||||||
const tabSizeX = tabEl.scrollWidth + 20;
|
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||||
tabEl.style.setProperty('--width', `${tabSizeX}px`);
|
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
||||||
|
const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding
|
||||||
const parentRect = tabsEl.getBoundingClientRect();
|
tabEl.style = `--width: ${tabSizeX}px`;
|
||||||
const rect = tabEl.getBoundingClientRect();
|
setTimeout(() => {
|
||||||
const left = (rect.left - parentRect.left + tabsEl.scrollLeft);
|
const parentRect = tabsEl.getBoundingClientRect();
|
||||||
tabHighlightEl.style.width = `${tabSizeX}px`;
|
const rect = tabEl.getBoundingClientRect();
|
||||||
tabHighlightEl.style.transform = `translateX(${left}px)`;
|
const left = (rect.left - parentRect.left + tabsEl?.scrollLeft);
|
||||||
tabsEl.scrollTo({left: left - 60, behavior: "smooth"});
|
tabHighlightEl.style.width = tabSizeX + 'px';
|
||||||
}
|
tabHighlightEl.style.transform = `translateX(${left}px)`;
|
||||||
});
|
window.requestAnimationFrame(() => {
|
||||||
};
|
tabsEl?.scrollTo({left: left - 60, behavior: "smooth"});
|
||||||
|
})
|
||||||
const updateTab = () => {
|
}, 200);
|
||||||
emit('update:tab', props.tab);
|
}
|
||||||
};
|
});
|
||||||
|
}, {
|
||||||
watch(() => [props.tab, props.tabs], updateTabHighlight, { immediate: true });
|
immediate: true,
|
||||||
watch(() => props.tab, updateTab);
|
});
|
||||||
|
|
||||||
if (el && el.parentElement) {
|
if (el && el.parentElement) {
|
||||||
narrow.value = el.parentElement.offsetWidth < 500;
|
narrow.value = el.parentElement.offsetWidth < 500;
|
||||||
|
|
|
@ -251,6 +251,7 @@ import { getAccountFromId } from "@/scripts/get-account-from-id";
|
||||||
|
|
||||||
// クライアントが更新されたか?
|
// クライアントが更新されたか?
|
||||||
const lastVersion = localStorage.getItem("lastVersion");
|
const lastVersion = localStorage.getItem("lastVersion");
|
||||||
|
|
||||||
if (lastVersion !== version) {
|
if (lastVersion !== version) {
|
||||||
localStorage.setItem("lastVersion", version);
|
localStorage.setItem("lastVersion", version);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import * as Misskey from "calckey-js";
|
||||||
import { apiUrl, url } from "@/config";
|
import { apiUrl, url } from "@/config";
|
||||||
import MkPostFormDialog from "@/components/MkPostFormDialog.vue";
|
import MkPostFormDialog from "@/components/MkPostFormDialog.vue";
|
||||||
import MkWaitingDialog from "@/components/MkWaitingDialog.vue";
|
import MkWaitingDialog from "@/components/MkWaitingDialog.vue";
|
||||||
|
import MkToast from "@/components/MkToast.vue";
|
||||||
|
import MkDialog from "@/components/MkDialog.vue";
|
||||||
import { MenuItem } from "@/types/menu";
|
import { MenuItem } from "@/types/menu";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
|
|
||||||
|
@ -247,7 +249,7 @@ export function modalPageWindow(path: string) {
|
||||||
|
|
||||||
export function toast(message: string) {
|
export function toast(message: string) {
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(() => import("@/components/MkToast.vue")),
|
MkToast,
|
||||||
{
|
{
|
||||||
message,
|
message,
|
||||||
},
|
},
|
||||||
|
@ -263,7 +265,7 @@ export function alert(props: {
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(() => import("@/components/MkDialog.vue")),
|
MkDialog,
|
||||||
props,
|
props,
|
||||||
{
|
{
|
||||||
done: (result) => {
|
done: (result) => {
|
||||||
|
@ -279,10 +281,12 @@ export function confirm(props: {
|
||||||
type: "error" | "info" | "success" | "warning" | "waiting" | "question";
|
type: "error" | "info" | "success" | "warning" | "waiting" | "question";
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
|
okText?: string;
|
||||||
|
cancelText?: string;
|
||||||
}): Promise<{ canceled: boolean }> {
|
}): Promise<{ canceled: boolean }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(() => import("@/components/MkDialog.vue")),
|
MkDialog,
|
||||||
{
|
{
|
||||||
...props,
|
...props,
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
<template>
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<div class="_formRoot">
|
|
||||||
<FormSwitch v-model="enableTwitterIntegration" class="_formBlock">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<template v-if="enableTwitterIntegration">
|
|
||||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
|
|
||||||
|
|
||||||
<FormInput v-model="twitterConsumerKey" class="_formBlock">
|
|
||||||
<template #prefix><i class="ph-key-bold ph-lg"></i></template>
|
|
||||||
<template #label>Consumer Key</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormInput v-model="twitterConsumerSecret" class="_formBlock">
|
|
||||||
<template #prefix><i class="ph-key-bold ph-lg"></i></template>
|
|
||||||
<template #label>Consumer Secret</template>
|
|
||||||
</FormInput>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<FormButton primary class="_formBlock" @click="save"><i class="ph-floppy-disk-back-bold ph-lg"></i> {{ i18n.ts.save }}</FormButton>
|
|
||||||
</div>
|
|
||||||
</FormSuspense>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import FormSwitch from '@/components/form/switch.vue';
|
|
||||||
import FormInput from '@/components/form/input.vue';
|
|
||||||
import FormButton from '@/components/MkButton.vue';
|
|
||||||
import FormInfo from '@/components/MkInfo.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { fetchInstance } from '@/instance';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
|
|
||||||
let uri: string = $ref('');
|
|
||||||
let enableTwitterIntegration: boolean = $ref(false);
|
|
||||||
let twitterConsumerKey: string | null = $ref(null);
|
|
||||||
let twitterConsumerSecret: string | null = $ref(null);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const meta = await os.api('admin/meta');
|
|
||||||
uri = meta.uri;
|
|
||||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
|
||||||
twitterConsumerKey = meta.twitterConsumerKey;
|
|
||||||
twitterConsumerSecret = meta.twitterConsumerSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
enableTwitterIntegration,
|
|
||||||
twitterConsumerKey,
|
|
||||||
twitterConsumerSecret,
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -3,12 +3,6 @@
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormFolder class="_formBlock">
|
|
||||||
<template #icon><i class="ph-twitter-logo-bold ph-lg"></i></template>
|
|
||||||
<template #label>Twitter</template>
|
|
||||||
<template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
|
|
||||||
<XTwitter/>
|
|
||||||
</FormFolder>
|
|
||||||
<FormFolder class="_formBlock">
|
<FormFolder class="_formBlock">
|
||||||
<template #icon><i class="ph-github-logo-bold ph-lg"></i></template>
|
<template #icon><i class="ph-github-logo-bold ph-lg"></i></template>
|
||||||
<template #label>GitHub</template>
|
<template #label>GitHub</template>
|
||||||
|
@ -28,7 +22,6 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { } from 'vue';
|
||||||
import XTwitter from './integrations.twitter.vue';
|
|
||||||
import XGithub from './integrations.github.vue';
|
import XGithub from './integrations.github.vue';
|
||||||
import XDiscord from './integrations.discord.vue';
|
import XDiscord from './integrations.discord.vue';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
|
|
|
@ -147,14 +147,14 @@ onMounted(async () => {
|
||||||
|
|
||||||
&.sub {
|
&.sub {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #907aa955;
|
background: #907aa922;
|
||||||
color: #c4a7e7;
|
color: #c4a7e7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.pub {
|
&.pub {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #56949f55;
|
background: #56949f22;
|
||||||
color: #9ccfd8;
|
color: #9ccfd8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,35 +106,35 @@ onMounted(async () => {
|
||||||
|
|
||||||
&.users {
|
&.users {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #56949f55;
|
background: #56949f22;
|
||||||
color: #9ccfd8;
|
color: #9ccfd8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.notes {
|
&.notes {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #28698355;
|
background: #28698322;
|
||||||
color: #31748f;
|
color: #31748f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.instances {
|
&.instances {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #d7827e55;
|
background: #d7827e22;
|
||||||
color: #ebbcba;
|
color: #ebbcba;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.emojis {
|
&.emojis {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #ea9d3455;
|
background: #ea9d3422;
|
||||||
color: #f6c177;
|
color: #f6c177;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.online {
|
&.online {
|
||||||
> .icon {
|
> .icon {
|
||||||
background: #907aa955;
|
background: #907aa922;
|
||||||
color: #c4a7e7;
|
color: #c4a7e7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="_formRoot">
|
<div class="_formRoot">
|
||||||
<FormSection v-if="instance.enableTwitterIntegration">
|
|
||||||
<template #label><i class="ph-twitter-logo-bold ph-lg"></i> Twitter</template>
|
|
||||||
<p v-if="integrations.twitter">{{ i18n.ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
|
|
||||||
<MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ i18n.ts.disconnectService }}</MkButton>
|
|
||||||
<MkButton v-else primary @click="connectTwitter">{{ i18n.ts.connectService }}</MkButton>
|
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<FormSection v-if="instance.enableDiscordIntegration">
|
<FormSection v-if="instance.enableDiscordIntegration">
|
||||||
<template #label><i class="ph-discord-logo-bold ph-lg"></i> Discord</template>
|
<template #label><i class="ph-discord-logo-bold ph-lg"></i> Discord</template>
|
||||||
<p v-if="integrations.discord">{{ i18n.ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
<p v-if="integrations.discord">{{ i18n.ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
<FormInput v-model="profile.location" manual-save class="_formBlock">
|
<FormInput v-model="profile.location" manual-save class="_formBlock">
|
||||||
<template #label>{{ i18n.ts.location }}</template>
|
<template #label>{{ i18n.ts.location }}</template>
|
||||||
<template #prefix><i class="ph-map-pin-bold ph-lg"></i></template>
|
<template #prefix><i class="ph-map-pin-bold ph-lg"></i></template>
|
||||||
|
<template #caption>{{ i18n.ts._profile.locationDescription }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock">
|
<FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock">
|
||||||
|
@ -76,7 +77,6 @@ import FormSelect from '@/components/form/select.vue';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import FormFolder from '@/components/form/folder.vue';
|
import FormFolder from '@/components/form/folder.vue';
|
||||||
import FormSlot from '@/components/form/slot.vue';
|
import FormSlot from '@/components/form/slot.vue';
|
||||||
import { host } from '@/config';
|
|
||||||
import { selectFile } from '@/scripts/select-file';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
<div class="fields system">
|
<div class="fields system">
|
||||||
<dl v-if="user.location" class="field">
|
<dl v-if="user.location" class="field">
|
||||||
<dt class="name"><i class="ph-map-pin-bold ph-lg ph-fw ph-lg"></i> {{ i18n.ts.location }}</dt>
|
<dt class="name"><i class="ph-map-pin-bold ph-lg ph-fw ph-lg"></i> {{ i18n.ts.location }}</dt>
|
||||||
<dd class="value">{{ user.location }}</dd>
|
<dd class="value">{{ user.location }}{{ timeForThem }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl v-if="user.birthday" class="field">
|
<dl v-if="user.birthday" class="field">
|
||||||
<dt class="name"><i class="ph-cake-bold ph-lg ph-fw ph-lg"></i> {{ i18n.ts.birthday }}</dt>
|
<dt class="name"><i class="ph-cake-bold ph-lg ph-fw ph-lg"></i> {{ i18n.ts.birthday }}</dt>
|
||||||
|
@ -117,27 +117,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue';
|
import { defineAsyncComponent, onMounted, onUnmounted } from 'vue';
|
||||||
import calcAge from 's-age';
|
import calcAge from 's-age';
|
||||||
|
import cityTimezones from 'city-timezones';
|
||||||
import XUserTimeline from './index.timeline.vue';
|
import XUserTimeline from './index.timeline.vue';
|
||||||
import type * as misskey from 'calckey-js';
|
import type * as misskey from 'calckey-js';
|
||||||
import XNote from '@/components/MkNote.vue';
|
import XNote from '@/components/MkNote.vue';
|
||||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||||
import MkContainer from '@/components/MkContainer.vue';
|
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
|
||||||
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
|
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
|
||||||
import MkTab from '@/components/MkTab.vue';
|
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkMoved from '@/components/MkMoved.vue';
|
import MkMoved from '@/components/MkMoved.vue';
|
||||||
import { getScrollPosition } from '@/scripts/scroll';
|
import { getScrollPosition } from '@/scripts/scroll';
|
||||||
import { getUserMenu } from '@/scripts/get-user-menu';
|
import { getUserMenu } from '@/scripts/get-user-menu';
|
||||||
import number from '@/filters/number';
|
import number from '@/filters/number';
|
||||||
import { userPage, acct as getAcct } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { useRouter } from '@/router';
|
import { useRouter } from '@/router';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { host } from '@/config';
|
|
||||||
|
|
||||||
const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
|
const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
|
||||||
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
|
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
|
||||||
|
@ -166,6 +163,14 @@ const age = $computed(() => {
|
||||||
return calcAge(props.user.birthday);
|
return calcAge(props.user.birthday);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const timeForThem = $computed(() => {
|
||||||
|
const tzInfo = cityTimezones.lookupViaCity(props.user.location!.replace(/\s.*/,''));
|
||||||
|
if (tzInfo.length == 0) return "";
|
||||||
|
const tz = tzInfo[0].timezone;
|
||||||
|
const theirTime = new Date().toLocaleString("en-US", { timeZone: tz, hour12: true })
|
||||||
|
return ` (${theirTime.split(",")[1].trim().split(":")[0]} ${theirTime.split(" ")[1].slice(-2)})`
|
||||||
|
})
|
||||||
|
|
||||||
function menu(ev) {
|
function menu(ev) {
|
||||||
os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target);
|
os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,6 +420,7 @@ importers:
|
||||||
chartjs-chart-matrix: ^2.0.1
|
chartjs-chart-matrix: ^2.0.1
|
||||||
chartjs-plugin-gradient: 0.5.1
|
chartjs-plugin-gradient: 0.5.1
|
||||||
chartjs-plugin-zoom: 1.2.1
|
chartjs-plugin-zoom: 1.2.1
|
||||||
|
city-timezones: ^1.2.1
|
||||||
compare-versions: 5.0.3
|
compare-versions: 5.0.3
|
||||||
cropperjs: 2.0.0-beta.2
|
cropperjs: 2.0.0-beta.2
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
|
@ -464,12 +465,9 @@ importers:
|
||||||
vue-plyr: ^7.0.0
|
vue-plyr: ^7.0.0
|
||||||
vue-prism-editor: 2.0.0-alpha.2
|
vue-prism-editor: 2.0.0-alpha.2
|
||||||
vuedraggable: 4.1.0
|
vuedraggable: 4.1.0
|
||||||
dependencies:
|
|
||||||
'@khmyznikov/pwa-install': 0.2.0
|
|
||||||
chartjs-chart-matrix: 2.0.1_chart.js@4.1.1
|
|
||||||
gsap: 3.11.4
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@discordapp/twemoji': 14.0.2
|
'@discordapp/twemoji': 14.0.2
|
||||||
|
'@khmyznikov/pwa-install': 0.2.0
|
||||||
'@rollup/plugin-alias': 3.1.9_rollup@3.9.1
|
'@rollup/plugin-alias': 3.1.9_rollup@3.9.1
|
||||||
'@rollup/plugin-json': 4.1.0_rollup@3.9.1
|
'@rollup/plugin-json': 4.1.0_rollup@3.9.1
|
||||||
'@rollup/pluginutils': 4.2.1
|
'@rollup/pluginutils': 4.2.1
|
||||||
|
@ -495,8 +493,10 @@ importers:
|
||||||
calckey-js: 0.0.22
|
calckey-js: 0.0.22
|
||||||
chart.js: 4.1.1
|
chart.js: 4.1.1
|
||||||
chartjs-adapter-date-fns: 2.0.1_chart.js@4.1.1
|
chartjs-adapter-date-fns: 2.0.1_chart.js@4.1.1
|
||||||
|
chartjs-chart-matrix: 2.0.1_chart.js@4.1.1
|
||||||
chartjs-plugin-gradient: 0.5.1_chart.js@4.1.1
|
chartjs-plugin-gradient: 0.5.1_chart.js@4.1.1
|
||||||
chartjs-plugin-zoom: 1.2.1_chart.js@4.1.1
|
chartjs-plugin-zoom: 1.2.1_chart.js@4.1.1
|
||||||
|
city-timezones: 1.2.1
|
||||||
compare-versions: 5.0.3
|
compare-versions: 5.0.3
|
||||||
cropperjs: 2.0.0-beta.2
|
cropperjs: 2.0.0-beta.2
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
|
@ -504,6 +504,7 @@ importers:
|
||||||
date-fns: 2.29.3
|
date-fns: 2.29.3
|
||||||
escape-regexp: 0.0.1
|
escape-regexp: 0.0.1
|
||||||
eventemitter3: 4.0.7
|
eventemitter3: 4.0.7
|
||||||
|
gsap: 3.11.4
|
||||||
idb-keyval: 6.2.0
|
idb-keyval: 6.2.0
|
||||||
insert-text-at-cursor: 0.3.0
|
insert-text-at-cursor: 0.3.0
|
||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
|
@ -1237,7 +1238,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lit/localize': 0.11.4
|
'@lit/localize': 0.11.4
|
||||||
lit: 2.6.1
|
lit: 2.6.1
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@koa/cors/3.4.3:
|
/@koa/cors/3.4.3:
|
||||||
resolution: {integrity: sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw==}
|
resolution: {integrity: sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw==}
|
||||||
|
@ -1270,23 +1271,24 @@ packages:
|
||||||
|
|
||||||
/@kurkle/color/0.3.2:
|
/@kurkle/color/0.3.2:
|
||||||
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
|
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@lit-labs/ssr-dom-shim/1.0.0:
|
/@lit-labs/ssr-dom-shim/1.0.0:
|
||||||
resolution: {integrity: sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==}
|
resolution: {integrity: sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==}
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@lit/localize/0.11.4:
|
/@lit/localize/0.11.4:
|
||||||
resolution: {integrity: sha512-RRIwIX2tAm3+DuEndoXSJrFjGrAK5cb5IXo5K6jcJ6sbgD829B8rSqHC5MaKVUmXTVLIR1bk5IZOZDf9wFereA==}
|
resolution: {integrity: sha512-RRIwIX2tAm3+DuEndoXSJrFjGrAK5cb5IXo5K6jcJ6sbgD829B8rSqHC5MaKVUmXTVLIR1bk5IZOZDf9wFereA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lit/reactive-element': 1.6.1
|
'@lit/reactive-element': 1.6.1
|
||||||
lit: 2.6.1
|
lit: 2.6.1
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@lit/reactive-element/1.6.1:
|
/@lit/reactive-element/1.6.1:
|
||||||
resolution: {integrity: sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==}
|
resolution: {integrity: sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lit-labs/ssr-dom-shim': 1.0.0
|
'@lit-labs/ssr-dom-shim': 1.0.0
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@mapbox/node-pre-gyp/1.0.9:
|
/@mapbox/node-pre-gyp/1.0.9:
|
||||||
resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
|
resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
|
||||||
|
@ -2540,7 +2542,7 @@ packages:
|
||||||
|
|
||||||
/@types/trusted-types/2.0.2:
|
/@types/trusted-types/2.0.2:
|
||||||
resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==}
|
resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==}
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/@types/undertaker-registry/1.0.1:
|
/@types/undertaker-registry/1.0.1:
|
||||||
resolution: {integrity: sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==}
|
resolution: {integrity: sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==}
|
||||||
|
@ -3390,7 +3392,7 @@ packages:
|
||||||
/axios/0.25.0_debug@4.3.4:
|
/axios/0.25.0_debug@4.3.4:
|
||||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2_debug@4.3.4
|
follow-redirects: 1.15.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -4010,6 +4012,7 @@ packages:
|
||||||
engines: {pnpm: ^7.0.0}
|
engines: {pnpm: ^7.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@kurkle/color': 0.3.2
|
'@kurkle/color': 0.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/chartjs-adapter-date-fns/2.0.1_chart.js@4.1.1:
|
/chartjs-adapter-date-fns/2.0.1_chart.js@4.1.1:
|
||||||
resolution: {integrity: sha512-v3WV9rdnQ05ce3A0ZCjzUekJCAbfm6+3HqSoeY2BIkdMYZoYr/4T+ril1tZyDl869lz6xdNVMXejUFT9YKpw4A==}
|
resolution: {integrity: sha512-v3WV9rdnQ05ce3A0ZCjzUekJCAbfm6+3HqSoeY2BIkdMYZoYr/4T+ril1tZyDl869lz6xdNVMXejUFT9YKpw4A==}
|
||||||
|
@ -4025,7 +4028,7 @@ packages:
|
||||||
chart.js: '>=3.0.0'
|
chart.js: '>=3.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
chart.js: 4.1.1
|
chart.js: 4.1.1
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/chartjs-plugin-gradient/0.5.1_chart.js@4.1.1:
|
/chartjs-plugin-gradient/0.5.1_chart.js@4.1.1:
|
||||||
resolution: {integrity: sha512-vhwlYGZWan4MGZZ4Wj64Y4aIql1uCPCU1JcggLWn3cgYEv4G7pXp1YgM4XH5ugmyn6BVCgQqAhiJ2h6hppzHmQ==}
|
resolution: {integrity: sha512-vhwlYGZWan4MGZZ4Wj64Y4aIql1uCPCU1JcggLWn3cgYEv4G7pXp1YgM4XH5ugmyn6BVCgQqAhiJ2h6hppzHmQ==}
|
||||||
|
@ -4104,6 +4107,12 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/city-timezones/1.2.1:
|
||||||
|
resolution: {integrity: sha512-hruuB611QFoUFMsan7xd9B2VPMrA8XC716O/999WW34kmaJUT1hxKF2W8TSXAWkhSqgvbu70DjcDv7/wpM6vow==}
|
||||||
|
dependencies:
|
||||||
|
lodash: 4.17.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
/clap/1.2.3:
|
/clap/1.2.3:
|
||||||
resolution: {integrity: sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==}
|
resolution: {integrity: sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -6382,19 +6391,6 @@ packages:
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
dev: false
|
|
||||||
|
|
||||||
/follow-redirects/1.15.2_debug@4.3.4:
|
|
||||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
|
||||||
engines: {node: '>=4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
debug: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
debug:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
debug: 4.3.4
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/for-each/0.3.3:
|
/for-each/0.3.3:
|
||||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||||
|
@ -6918,7 +6914,7 @@ packages:
|
||||||
|
|
||||||
/gsap/3.11.4:
|
/gsap/3.11.4:
|
||||||
resolution: {integrity: sha512-McHhEguHyExMMnjqKA8G+7TvxmlKQGMbjgwAilnFe1e4id7V/tFveRQ2YMZhTYu0oxHGWvbPltdVYQOu3z1SCA==}
|
resolution: {integrity: sha512-McHhEguHyExMMnjqKA8G+7TvxmlKQGMbjgwAilnFe1e4id7V/tFveRQ2YMZhTYu0oxHGWvbPltdVYQOu3z1SCA==}
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/gulp-cli/2.3.0:
|
/gulp-cli/2.3.0:
|
||||||
resolution: {integrity: sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==}
|
resolution: {integrity: sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==}
|
||||||
|
@ -8596,13 +8592,13 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lit/reactive-element': 1.6.1
|
'@lit/reactive-element': 1.6.1
|
||||||
lit-html: 2.6.1
|
lit-html: 2.6.1
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/lit-html/2.6.1:
|
/lit-html/2.6.1:
|
||||||
resolution: {integrity: sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==}
|
resolution: {integrity: sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/trusted-types': 2.0.2
|
'@types/trusted-types': 2.0.2
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/lit/2.6.1:
|
/lit/2.6.1:
|
||||||
resolution: {integrity: sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==}
|
resolution: {integrity: sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==}
|
||||||
|
@ -8610,7 +8606,7 @@ packages:
|
||||||
'@lit/reactive-element': 1.6.1
|
'@lit/reactive-element': 1.6.1
|
||||||
lit-element: 3.2.2
|
lit-element: 3.2.2
|
||||||
lit-html: 2.6.1
|
lit-html: 2.6.1
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/load-json-file/1.1.0:
|
/load-json-file/1.1.0:
|
||||||
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
|
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
|
||||||
|
|
Loading…
Reference in New Issue