refactor (client): move instance data to IndexDB (close #10939)

This commit is contained in:
naskya 2024-07-03 23:48:32 +09:00
parent 7af1ea891a
commit 6b03989c2e
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
50 changed files with 280 additions and 288 deletions

View file

@ -100,7 +100,7 @@ import * as os from "@/os";
import { MFM_TAGS } from "@/scripts/mfm-tags";
import { defaultStore } from "@/store";
import { addSkinTone, emojilist } from "@/scripts/emojilist";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
interface EmojiDef {
@ -141,7 +141,7 @@ for (const x of lib) {
emjdb.sort((a, b) => a.name.length - b.name.length);
// #region Construct Emoji DB
const customEmojis = instance.emojis;
const customEmojis = getInstanceInfo().emojis;
const emojiDefinitions: EmojiDef[] = [];
for (const x of customEmojis) {

View file

@ -64,7 +64,7 @@ import MkButton from "@/components/MkButton.vue";
import { host } from "@/config";
import { i18n } from "@/i18n";
import * as os from "@/os";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const show = ref(false);
@ -73,6 +73,7 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
const instance = getInstanceInfo();
const hostname =
instance.name?.length && instance.name?.length < 38 ? instance.name : host;

View file

@ -164,7 +164,7 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from "vue";
import { onMounted, ref, watch } from "vue";
import type { entities } from "firefish-js";
import { FocusTrap } from "focus-trap-vue";
import XSection from "@/components/MkEmojiPicker.section.vue";
@ -175,7 +175,7 @@ import Ripple from "@/components/MkRipple.vue";
import * as os from "@/os";
import { isTouchUsing } from "@/scripts/touch";
import { deviceKind } from "@/scripts/device-kind";
import { emojiCategories, instance } from "@/instance";
import { emojiCategories, getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
@ -235,7 +235,7 @@ const size = reactionPickerSize;
const width = reactionPickerWidth;
const height = reactionPickerHeight;
const customEmojiCategories = emojiCategories;
const customEmojis = instance.emojis;
const customEmojis = getInstanceInfo().emojis;
const q = ref<string | null>(null);
const searchResultCustom = ref<entities.CustomEmoji[]>([]);
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
@ -459,7 +459,6 @@ async function paste(event: ClipboardEvent) {
}
function done(query?: string | null): boolean {
// biome-ignore lint/style/noParameterAssign: assign it intentially
if (query == null) query = q.value;
if (query == null || typeof query !== "string") return false;

View file

@ -9,7 +9,7 @@
<template #header>{{ i18n.ts.forgotPassword }}</template>
<form
v-if="instance.enableEmail"
v-if="enableEmail"
class="bafeceda"
@submit.prevent="onSubmit"
>
@ -68,7 +68,7 @@ import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
import * as os from "@/os";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
const emit = defineEmits<{
@ -82,6 +82,8 @@ const username = ref("");
const email = ref("");
const processing = ref(false);
const { enableEmail } = getInstanceInfo();
async function onSubmit() {
processing.value = true;
await os.apiWithDialog("request-reset-password", {
@ -89,7 +91,7 @@ async function onSubmit() {
email: email.value,
});
emit("done");
dialog.value!.close();
dialog.value?.close();
}
</script>

View file

@ -19,7 +19,7 @@ import { ref } from "vue";
import type { entities } from "firefish-js";
import { instanceName, version } from "@/config";
import { instance as Instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
const props = defineProps<{
@ -28,9 +28,10 @@ const props = defineProps<{
const ticker = ref<HTMLElement | null>(null);
// FIXME: the following assumption is not necessarily correct
// if no instance data is given, this is for the local instance
const instance = props.instance ?? {
faviconUrl: Instance.iconUrl || "/favicon.ico",
faviconUrl: getInstanceInfo().iconUrl || "/favicon.ico",
name: instanceName,
themeColor: (
document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement

View file

@ -76,7 +76,7 @@ import MkButton from "@/components/MkButton.vue";
import bytes from "@/filters/bytes";
import number from "@/filters/number";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
const props = withDefaults(
defineProps<{
@ -109,14 +109,14 @@ const modal = ref<InstanceType<typeof MkModal> | null>(null);
const inputValue = ref(props.input.default ? props.input.default : null);
const remainingLength = computed(() => {
const maxCaptionLength = instance.maxCaptionTextLength ?? 512;
const maxCaptionLength = getInstanceInfo().maxCaptionTextLength ?? 512;
if (typeof inputValue.value !== "string") return maxCaptionLength;
return maxCaptionLength - length(inputValue.value);
});
function done(canceled: boolean, result?: string | null) {
emit("done", { canceled, result });
modal.value!.close();
modal.value?.close();
}
async function ok() {

View file

@ -285,7 +285,7 @@ import * as os from "@/os";
import { useStream } from "@/stream";
import { useTooltip } from "@/scripts/use-tooltip";
import { defaultStore } from "@/store";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const props = withDefaults(
@ -309,8 +309,9 @@ const hideFollowButton = defaultStore.state.hideFollowButtons;
const showEmojiReactions =
defaultStore.state.enableEmojiReactions ||
defaultStore.state.showEmojisInReactionNotifications;
const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
? instance.defaultReaction
const realDefaultReaction = getInstanceInfo().defaultReaction;
const defaultReaction = ["⭐", "👍", "❤️"].includes(realDefaultReaction)
? realDefaultReaction
: "⭐";
let readObserver: IntersectionObserver | undefined;

View file

@ -88,7 +88,7 @@ import * as os from "@/os";
import { useStream } from "@/stream";
import { useTooltip } from "@/scripts/use-tooltip";
import { defaultStore } from "@/store";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
import type {
NotificationFolded,
@ -116,8 +116,9 @@ const reactionRef = ref<InstanceType<typeof XReactionIcon> | null>(null);
const showEmojiReactions =
defaultStore.state.enableEmojiReactions ||
defaultStore.state.showEmojisInReactionNotifications;
const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
? instance.defaultReaction
const realDefaultReaction = getInstanceInfo().defaultReaction;
const defaultReaction = ["⭐", "👍", "❤️"].includes(realDefaultReaction)
? realDefaultReaction
: "⭐";
const users = computed(() => props.notification.users.slice(0, 5));
@ -178,7 +179,7 @@ useTooltip(reactionRef, (showing) => {
? n.reaction.replace(/^:(\w+):$/, ":$1@.:")
: n.reaction,
emojis: n.note.emojis,
targetElement: reactionRef.value!.$el,
targetElement: reactionRef.value?.$el,
},
{},
"closed",
@ -203,7 +204,7 @@ onMounted(() => {
readObserver.observe(elRef.value!);
connection = stream.useChannel("main");
connection.on("readAllNotifications", () => readObserver!.disconnect());
connection.on("readAllNotifications", () => readObserver?.disconnect());
});
onUnmounted(() => {

View file

@ -322,7 +322,7 @@ import { selectFiles } from "@/scripts/select-file";
import { defaultStore, notePostInterruptors, postFormActions } from "@/store";
import MkInfo from "@/components/MkInfo.vue";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
import { me } from "@/me";
import { uploadFile } from "@/scripts/upload";
@ -497,7 +497,7 @@ const textLength = computed((): number => {
});
const maxTextLength = computed((): number => {
return instance ? instance.maxNoteTextLength : 3000;
return getInstanceInfo().maxNoteTextLength ?? 3000;
});
const canPost = computed((): boolean => {

View file

@ -58,7 +58,7 @@ import { ref } from "vue";
import { getAccounts } from "@/account";
import { isSignedIn, me } from "@/me";
import MkButton from "@/components/MkButton.vue";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { api, apiWithDialog, promiseDialog } from "@/os";
import { i18n } from "@/i18n";
@ -76,6 +76,7 @@ defineProps<{
showOnlyToRegister?: boolean;
}>();
const { swPublickey } = getInstanceInfo();
// ServiceWorker registration
const registration = ref<ServiceWorkerRegistration | undefined>();
// If this browser supports push notification
@ -94,14 +95,14 @@ const pushRegistrationInServer = ref<
>();
function subscribe() {
if (!registration.value || !supported.value || !instance.swPublickey) return;
if (!registration.value || !supported.value || !swPublickey) return;
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
return promiseDialog(
registration.value.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
applicationServerKey: urlBase64ToUint8Array(swPublickey),
})
.then(
async (subscription) => {
@ -186,12 +187,7 @@ if (navigator.serviceWorker == null) {
pushSubscription.value =
await registration.value.pushManager.getSubscription();
if (
instance.swPublickey &&
"PushManager" in window &&
isSignedIn(me) &&
me.token
) {
if (swPublickey && "PushManager" in window && isSignedIn(me) && me.token) {
supported.value = true;
if (pushSubscription.value) {

View file

@ -288,7 +288,7 @@ import MkCaptcha from "@/components/MkCaptcha.vue";
import * as config from "@/config";
import * as os from "@/os";
import { signIn } from "@/account";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -306,6 +306,8 @@ const emit = defineEmits<{
(ev: "signupEmailPending"): void;
}>();
const instance = getInstanceInfo();
const host = toUnicode(config.host);
const hcaptcha = ref();

View file

@ -30,11 +30,11 @@
</g>
</svg>
<i
v-else-if="instance.defaultReaction === '👍'"
v-else-if="defaultReaction === '👍'"
:class="icon('ph-thumbs-up')"
></i>
<i
v-else-if="instance.defaultReaction === '❤️'"
v-else-if="defaultReaction === '❤️'"
:class="icon('ph-heart')"
></i>
<i v-else :class="icon('ph-star')"></i>
@ -48,19 +48,20 @@ import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const props = defineProps<{
note: entities.Note;
}>();
const { defaultReaction } = getInstanceInfo();
function star(ev?: MouseEvent): void {
pleaseLogin();
os.api("notes/reactions/create", {
noteId: props.note.id,
reaction:
defaultStore.state.woozyMode === true ? "🥴" : instance.defaultReaction,
reaction: defaultStore.state.woozyMode === true ? "🥴" : defaultReaction,
});
const el =
ev && ((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);

View file

@ -9,23 +9,23 @@
>
<span v-if="!reacted">
<i
v-if="instance.defaultReaction === '👍'"
v-if="defaultReaction === '👍'"
:class="icon('ph-thumbs-up')"
></i>
<i
v-else-if="instance.defaultReaction === '❤️'"
v-else-if="defaultReaction === '❤️'"
:class="icon('ph-heart')"
></i>
<i v-else :class="icon('ph-star')"></i>
</span>
<span v-else>
<i
v-if="instance.defaultReaction === '👍'"
v-if="defaultReaction === '👍'"
class="ph-thumbs-up ph-lg ph-fill"
:class="$style.yellow"
></i>
<i
v-else-if="instance.defaultReaction === '❤️'"
v-else-if="defaultReaction === '❤️'"
class="ph-heart ph-lg ph-fill"
:class="$style.red"
></i>
@ -45,7 +45,7 @@ import XDetails from "@/components/MkUsersTooltip.vue";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { useTooltip } from "@/scripts/use-tooltip";
import icon from "@/scripts/icon";
@ -57,13 +57,15 @@ const props = defineProps<{
const buttonRef = ref<HTMLElement>();
const { defaultReaction } = getInstanceInfo();
function toggleStar(ev?: MouseEvent): void {
pleaseLogin();
if (!props.reacted) {
os.api("notes/reactions/create", {
noteId: props.note.id,
reaction: instance.defaultReaction,
reaction: defaultReaction,
});
const el =
ev && ((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);

View file

@ -212,14 +212,19 @@ import FormSwitch from "@/components/form/switch.vue";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { isModerator } from "@/me";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const isLocalTimelineAvailable = !instance.disableLocalTimeline || isModerator;
const {
disableLocalTimeline,
disableRecommendedTimeline,
disableGlobalTimeline,
} = getInstanceInfo();
const isLocalTimelineAvailable = !disableLocalTimeline || isModerator;
const isRecommendedTimelineAvailable =
!instance.disableRecommendedTimeline || isModerator;
const isGlobalTimelineAvailable =
!instance.disableGlobalTimeline || isModerator;
!disableRecommendedTimeline || isModerator;
const isGlobalTimelineAvailable = !disableGlobalTimeline || isModerator;
const timelines = ["home"];
@ -252,13 +257,13 @@ const tutorial = computed({
},
});
const autoplayMfm = computed(
defaultStore.makeGetterSetter(
"animatedMfm",
(v) => !v,
(v) => !v,
),
);
// const autoplayMfm = computed(
// defaultStore.makeGetterSetter(
// "animatedMfm",
// (v) => !v,
// (v) => !v,
// ),
// );
const reduceAnimation = computed(
defaultStore.makeGetterSetter(
"animation",
@ -267,9 +272,9 @@ const reduceAnimation = computed(
),
);
function close(res) {
function close(_res) {
tutorial.value = -1;
dialog.value.close();
dialog.value?.close();
}
</script>

View file

@ -40,7 +40,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { host } from "@/config";
import MkButton from "@/components/MkButton.vue";
import { defaultStore } from "@/store";
@ -48,7 +48,9 @@ import * as os from "@/os";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
type Ad = (typeof instance)["ads"][number];
// TODO?: rename to community banner
const instanceAds = getInstanceInfo().ads;
type Ad = (typeof instanceAds)[number];
const props = defineProps<{
prefer: string[];
@ -65,7 +67,7 @@ const choseAd = (): Ad | Ad[] | null => {
return props.specify;
}
const allAds = instance.ads.map((ad) =>
const allAds = instanceAds.map((ad) =>
defaultStore.state.mutedAds.includes(ad.id)
? {
...ad,

View file

@ -26,7 +26,7 @@ import type { entities } from "firefish-js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { char2filePath } from "@/scripts/twemoji-base";
import { defaultStore } from "@/store";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
const props = defineProps<{
emoji: string;
@ -41,7 +41,7 @@ const char = computed(() => (isCustom.value ? null : props.emoji));
const useOsNativeEmojis = computed(
() => defaultStore.state.useOsNativeEmojis && !props.isReaction,
);
const ce = computed(() => props.customEmojis ?? instance.emojis ?? []);
const ce = computed(() => props.customEmojis ?? getInstanceInfo().emojis ?? []);
const customEmoji = computed(() =>
isCustom.value
? ce.value.find(
@ -55,7 +55,7 @@ const url = computed(() => {
} else {
return defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.value!.url)
: customEmoji.value!.url;
: customEmoji.value?.url;
}
});
const alt = computed(() =>

View file

@ -27,7 +27,7 @@ import { me } from "@/me";
import type { NoteTranslation, NoteType } from "@/types/note";
import { computed, ref, watch } from "vue";
import * as os from "@/os";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
const props = defineProps<{
note: NoteType;
@ -39,7 +39,7 @@ const translating = ref<boolean>();
const hasError = ref<boolean>();
const canTranslate = computed(
() =>
instance.translatorAvailable &&
getInstanceInfo().translatorAvailable &&
translation.value == null &&
translating.value !== true,
);

View file

@ -13,13 +13,13 @@ import "@phosphor-icons/web/fill";
import "@phosphor-icons/web/light";
import "@phosphor-icons/web/regular";
// #region account indexedDB migration
// #region IndexDB migrations
const accounts = localStorage.getItem("accounts");
if (accounts) {
set("accounts", JSON.parse(accounts));
localStorage.removeItem("accounts");
}
localStorage.removeItem("instance");
// #endregion
import {
@ -37,7 +37,7 @@ import components from "@/components";
import { lang, ui, version } from "@/config";
import directives from "@/directives";
import { i18n } from "@/i18n";
import { fetchInstance, instance } from "@/instance";
import { getInstanceInfo, initializeInstanceCache } from "@/instance";
import { isSignedIn, me } from "@/me";
import { alert, api, confirm, popup, post, toast } from "@/os";
import { deviceKind } from "@/scripts/device-kind";
@ -68,6 +68,7 @@ function checkForSplash() {
}
(async () => {
await initializeInstanceCache();
console.info(`Firefish v${version}`);
if (_DEV_) {
@ -177,14 +178,10 @@ function checkForSplash() {
}
// #endregion
const fetchInstanceMetaPromise = fetchInstance();
localStorage.setItem("v", getInstanceInfo().version);
fetchInstanceMetaPromise.then(() => {
localStorage.setItem("v", instance.version);
// Init service worker
initializeSw();
});
// Init service worker
initializeSw();
const app = createApp(
window.location.search === "?zen"
@ -341,21 +338,15 @@ function checkForSplash() {
};
// #endregion
fetchInstanceMetaPromise.then(() => {
if (defaultStore.state.themeInitial) {
if (instance.defaultLightTheme != null)
ColdDeviceStorage.set(
"lightTheme",
JSON.parse(instance.defaultLightTheme),
);
if (instance.defaultDarkTheme != null)
ColdDeviceStorage.set(
"darkTheme",
JSON.parse(instance.defaultDarkTheme),
);
defaultStore.set("themeInitial", false);
}
});
const { defaultLightTheme, defaultDarkTheme } = getInstanceInfo();
if (defaultStore.state.themeInitial) {
if (defaultLightTheme != null)
ColdDeviceStorage.set("lightTheme", JSON.parse(defaultLightTheme));
if (defaultDarkTheme != null)
ColdDeviceStorage.set("darkTheme", JSON.parse(defaultDarkTheme));
defaultStore.set("themeInitial", false);
}
watch(
defaultStore.reactiveState.useBlurEffectForModal,

View file

@ -1,30 +1,40 @@
import type { entities } from "firefish-js";
import { computed, reactive } from "vue";
import { computed } from "vue";
import { api } from "./os";
import { set, get } from "idb-keyval";
// TODO: 他のタブと永続化されたstateを同期
const instanceData = localStorage.getItem("instance");
// TODO: instanceをリアクティブにするかは再考の余地あり
// TODO: get("instance") requires top-level await
let instance: entities.DetailedInstanceMetadata;
export const instance: entities.DetailedInstanceMetadata = reactive(
instanceData
? JSON.parse(instanceData)
: {
// TODO: set default values
},
);
export function getInstanceInfo(): entities.DetailedInstanceMetadata {
return instance;
}
export async function fetchInstance() {
export async function initializeInstanceCache(): Promise<void> {
// Is the data stored in IndexDB?
const fromIdb = await get<string>("instance");
if (fromIdb != null) {
instance = JSON.parse(fromIdb);
}
// Call API
updateInstanceCache();
}
export async function updateInstanceCache(): Promise<void> {
const meta = await api("meta", {
detail: true,
});
// TODO: set default values
instance = {} as entities.DetailedInstanceMetadata;
for (const [k, v] of Object.entries(meta)) {
instance[k] = v;
}
localStorage.setItem("instance", JSON.stringify(instance));
set("instance", JSON.stringify(instance));
}
export const emojiCategories = computed(() => {

View file

@ -35,7 +35,7 @@
><MkEmoji
class="emoji"
:emoji="emoji.emoji"
:custom-emojis="instance.emojis"
:custom-emojis="instanceEmojis"
:is-reaction="false"
:normal="true"
:no-style="true"
@ -96,12 +96,13 @@ import { defaultReactions, defaultStore } from "@/store";
import * as os from "@/os";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
let easterEggReady = false;
const easterEggEmojis = ref([]);
const easterEggEngine = ref(null);
const containerEl = ref<HTMLElement>();
const instanceEmojis = getInstanceInfo().emojis;
function iconLoaded() {
const emojis =

View file

@ -6,12 +6,6 @@
><i :class="icon('ph-magnifying-glass')"></i
></template>
</MkInput>
<!-- たくさんあると邪魔
<div class="tags">
<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
</div>
-->
</div>
<MkFolder v-if="searchEmojis" class="emojis">
@ -54,7 +48,7 @@ import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue";
import MkFolder from "@/components/MkFolder.vue";
import MkTab from "@/components/MkTab.vue";
import { emojiCategories, emojiTags, instance } from "@/instance";
import { emojiCategories, emojiTags, getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import iconify from "@/scripts/icon";
@ -72,7 +66,7 @@ export default defineComponent({
return {
q: "",
customEmojiCategories: emojiCategories,
customEmojis: instance.emojis,
customEmojis: getInstanceInfo().emojis,
tags: emojiTags,
selectedTags: new Set(),
searchEmojis: null,

View file

@ -192,7 +192,7 @@ import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind";
import { isModerator } from "@/me";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
import "swiper/scss";
@ -207,6 +207,7 @@ withDefaults(
},
);
const instance = getInstanceInfo();
const stats = ref(null);
const instanceIcon = ref<HTMLImageElement>();
let iconClicks = 0;
@ -307,7 +308,7 @@ function onSlideChange() {
}
function syncSlide(index: number) {
swiperRef!.slideTo(index);
swiperRef?.slideTo(index);
}
</script>

View file

@ -81,7 +81,7 @@ import FormButton from "@/components/MkButton.vue";
import FormSuspense from "@/components/form/suspense.vue";
import FormSlot from "@/components/form/slot.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -89,7 +89,7 @@ const MkCaptcha = defineAsyncComponent(
() => import("@/components/MkCaptcha.vue"),
);
const provider = ref<any>(null);
const provider = ref<string | null>(null);
const hcaptchaSiteKey = ref<string | null>(null);
const hcaptchaSecretKey = ref<string | null>(null);
const recaptchaSiteKey = ref<string | null>(null);
@ -97,14 +97,14 @@ const recaptchaSecretKey = ref<string | null>(null);
async function init() {
const meta = await os.api("admin/meta");
hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
recaptchaSiteKey.value = meta.recaptchaSiteKey;
recaptchaSecretKey.value = meta.recaptchaSecretKey;
hcaptchaSiteKey.value = meta?.hcaptchaSiteKey;
hcaptchaSecretKey.value = meta?.hcaptchaSecretKey;
recaptchaSiteKey.value = meta?.recaptchaSiteKey;
recaptchaSecretKey.value = meta?.recaptchaSecretKey;
provider.value = meta.enableHcaptcha
provider.value = meta?.enableHcaptcha
? "hcaptcha"
: meta.enableRecaptcha
: meta?.enableRecaptcha
? "recaptcha"
: null;
}
@ -118,7 +118,7 @@ function save() {
recaptchaSiteKey: recaptchaSiteKey.value,
recaptchaSecretKey: recaptchaSecretKey.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}
</script>

View file

@ -100,13 +100,13 @@ import FormSuspense from "@/components/form/suspense.vue";
import FormSplit from "@/components/form/split.vue";
import FormSection from "@/components/form/section.vue";
import * as os from "@/os";
import { fetchInstance, instance } from "@/instance";
import { updateInstanceCache, getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
const enableEmail = ref(false);
const email: any = ref(null);
const email = ref<string | null>(null);
const smtpSecure = ref(false);
const smtpHost = ref("");
const smtpPort = ref(0);
@ -115,20 +115,22 @@ const smtpPass = ref("");
async function init() {
const meta = await os.api("admin/meta");
enableEmail.value = meta.enableEmail;
email.value = meta.email;
smtpSecure.value = meta.smtpSecure;
smtpHost.value = meta.smtpHost;
smtpPort.value = meta.smtpPort;
smtpUser.value = meta.smtpUser;
smtpPass.value = meta.smtpPass;
enableEmail.value = meta?.enableEmail;
email.value = meta?.email;
smtpSecure.value = meta?.smtpSecure;
smtpHost.value = meta?.smtpHost;
smtpPort.value = meta?.smtpPort;
smtpUser.value = meta?.smtpUser;
smtpPass.value = meta?.smtpPass;
}
const { maintainerEmail } = getInstanceInfo();
async function testEmail() {
const { canceled, result: destination } = await os.inputText({
title: i18n.ts.destination,
type: "email",
placeholder: instance.maintainerEmail,
placeholder: maintainerEmail,
});
if (canceled) return;
os.apiWithDialog("admin/send-email", {
@ -148,7 +150,7 @@ function save() {
smtpUser: smtpUser.value,
smtpPass: smtpPass.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -33,7 +33,7 @@ import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
import FormSuspense from "@/components/form/suspense.vue";
import FormSwitch from "@/components/form/switch.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -62,7 +62,7 @@ function save() {
},
};
os.apiWithDialog("admin/update-meta", experiments).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -32,7 +32,7 @@ import FormButton from "@/components/MkButton.vue";
import FormTextarea from "@/components/form/textarea.vue";
import FormSuspense from "@/components/form/suspense.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -41,14 +41,14 @@ const hiddenTags = ref("");
async function init() {
const meta = await os.api("admin/meta");
hiddenTags.value = meta.hiddenTags.join("\n");
hiddenTags.value = meta?.hiddenTags.join("\n");
}
function save() {
os.apiWithDialog("admin/update-meta", {
hiddenTags: hiddenTags.value.split("\n").map((h: string) => h.trim()) || [],
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -71,9 +71,9 @@ import {
import { i18n } from "@/i18n";
import MkSuperMenu from "@/components/MkSuperMenu.vue";
import MkInfo from "@/components/MkInfo.vue";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { version } from "@/config";
import { isAdmin, me } from "@/me";
import { isAdmin } from "@/me";
import * as os from "@/os";
import { lookupUser } from "@/scripts/lookup-user";
import { lookupFile } from "@/scripts/lookup-file";
@ -98,6 +98,8 @@ const indexInfo = {
provide("shouldOmitHeaderTitle", false);
const instance = getInstanceInfo();
const INFO = ref(indexInfo);
const childInfo = ref(null);
const narrow = ref(false);
@ -110,7 +112,7 @@ const noBotProtection =
const noEmailServer = !instance.enableEmail;
const thereIsUnresolvedAbuseReport = ref(false);
const updateAvailable = ref(false);
const currentPage = computed(() => router.currentRef.value.child);
const currentPage = computed(() => router.currentRef.value?.child);
os.api("admin/abuse-user-reports", {
state: "unresolved",
@ -312,7 +314,7 @@ onUnmounted(() => {
watch(router.currentRef, (to) => {
if (
to.route.path === "/admin" &&
to?.route.path === "/admin" &&
to.child?.route.name == null &&
!narrow.value
) {
@ -405,10 +407,6 @@ const lookup = (ev) => {
);
};
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(INFO.value);
defineExpose({

View file

@ -50,7 +50,7 @@ import FormTextarea from "@/components/form/textarea.vue";
import FormSuspense from "@/components/form/suspense.vue";
import MkTab from "@/components/MkTab.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -72,7 +72,7 @@ function save() {
blockedHosts: blockedHosts.value.split("\n").map((h) => h.trim()) || [],
silencedHosts: silencedHosts.value.split("\n").map((h) => h.trim()) || [],
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -156,7 +156,7 @@ import FormInput from "@/components/form/input.vue";
import FormSuspense from "@/components/form/suspense.vue";
import FormSplit from "@/components/form/split.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -177,19 +177,19 @@ const objectStorageS3ForcePathStyle = ref(true);
async function init() {
const meta = await os.api("admin/meta");
useObjectStorage.value = meta.useObjectStorage;
objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
objectStorageBucket.value = meta.objectStorageBucket;
objectStoragePrefix.value = meta.objectStoragePrefix;
objectStorageEndpoint.value = meta.objectStorageEndpoint;
objectStorageRegion.value = meta.objectStorageRegion;
objectStoragePort.value = meta.objectStoragePort;
objectStorageAccessKey.value = meta.objectStorageAccessKey;
objectStorageSecretKey.value = meta.objectStorageSecretKey;
objectStorageUseSSL.value = meta.objectStorageUseSSL;
objectStorageUseProxy.value = meta.objectStorageUseProxy;
objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
useObjectStorage.value = meta?.useObjectStorage;
objectStorageBaseUrl.value = meta?.objectStorageBaseUrl;
objectStorageBucket.value = meta?.objectStorageBucket;
objectStoragePrefix.value = meta?.objectStoragePrefix;
objectStorageEndpoint.value = meta?.objectStorageEndpoint;
objectStorageRegion.value = meta?.objectStorageRegion;
objectStoragePort.value = meta?.objectStoragePort;
objectStorageAccessKey.value = meta?.objectStorageAccessKey;
objectStorageSecretKey.value = meta?.objectStorageSecretKey;
objectStorageUseSSL.value = meta?.objectStorageUseSSL;
objectStorageUseProxy.value = meta?.objectStorageUseProxy;
objectStorageSetPublicRead.value = meta?.objectStorageSetPublicRead;
objectStorageS3ForcePathStyle.value = meta?.objectStorageS3ForcePathStyle;
}
function save() {
@ -208,7 +208,7 @@ function save() {
objectStorageSetPublicRead: objectStorageSetPublicRead.value,
objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -17,7 +17,7 @@ import { computed } from "vue";
import FormSuspense from "@/components/form/suspense.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -28,7 +28,7 @@ async function init() {
function save() {
os.apiWithDialog("admin/update-meta").then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -42,7 +42,7 @@ import MkButton from "@/components/MkButton.vue";
import MkInfo from "@/components/MkInfo.vue";
import FormSuspense from "@/components/form/suspense.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -52,7 +52,7 @@ const proxyAccountId = ref<any>(null);
async function init() {
const meta = await os.api("admin/meta");
proxyAccountId.value = meta.proxyAccountId;
proxyAccountId.value = meta?.proxyAccountId;
if (proxyAccountId.value) {
proxyAccount.value = await os.api("users/show", {
userId: proxyAccountId.value,
@ -72,7 +72,7 @@ function save() {
os.apiWithDialog("admin/update-meta", {
proxyAccountId: proxyAccountId.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}
@ -80,7 +80,7 @@ function del() {
os.apiWithDialog("admin/update-meta", {
proxyAccountId: null,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -151,7 +151,7 @@ import FormInput from "@/components/form/input.vue";
import FormTextarea from "@/components/form/textarea.vue";
import FormButton from "@/components/MkButton.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -168,15 +168,15 @@ const allowedHosts = ref("");
async function init() {
const meta = await os.api("admin/meta");
summalyProxy.value = meta.summalyProxy;
enableHcaptcha.value = meta.enableHcaptcha;
enableRecaptcha.value = meta.enableRecaptcha;
enableIpLogging.value = meta.enableIpLogging;
enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
summalyProxy.value = meta?.summalyProxy;
enableHcaptcha.value = meta?.enableHcaptcha;
enableRecaptcha.value = meta?.enableRecaptcha;
enableIpLogging.value = meta?.enableIpLogging;
enableActiveEmailValidation.value = meta?.enableActiveEmailValidation;
secureMode.value = meta.secureMode;
privateMode.value = meta.privateMode;
allowedHosts.value = meta.allowedHosts.join("\n");
secureMode.value = meta?.secureMode;
privateMode.value = meta?.privateMode;
allowedHosts.value = meta?.allowedHosts.join("\n");
}
function save() {
@ -185,7 +185,7 @@ function save() {
enableIpLogging: enableIpLogging.value,
enableActiveEmailValidation: enableActiveEmailValidation.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}
@ -195,7 +195,7 @@ function saveInstance() {
privateMode: privateMode.value,
allowedHosts: allowedHosts.value.split("\n"),
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -484,7 +484,7 @@ import FormSplit from "@/components/form/split.vue";
import FormSuspense from "@/components/form/suspense.vue";
import MkRadios from "@/components/form/radios.vue";
import * as os from "@/os";
import { fetchInstance } from "@/instance";
import { updateInstanceCache } from "@/instance";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -660,7 +660,7 @@ function save() {
enableServerMachineStats: enableServerMachineStats.value,
enableIdenticonGeneration: enableIdenticonGeneration.value,
}).then(() => {
fetchInstance();
updateInstanceCache();
});
}

View file

@ -41,7 +41,7 @@
</div>
<FormSwitch
v-if="!instance.markLocalFilesNsfwByDefault"
v-if="!markLocalFilesNsfwByDefault"
v-model="isSensitive"
>{{ i18n.ts.markAsSensitive }}</FormSwitch
>
@ -75,7 +75,7 @@ import { selectFiles } from "@/scripts/select-file";
import * as os from "@/os";
import { useRouter } from "@/router";
import { definePageMetadata } from "@/scripts/page-metadata";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -91,6 +91,8 @@ const description = ref(null);
const title = ref(null);
const isSensitive = ref(false);
const { markLocalFilesNsfwByDefault } = getInstanceInfo();
function selectFile(evt) {
selectFiles(evt.currentTarget ?? evt.target, null).then((selected) => {
files.value = files.value.concat(selected);
@ -118,7 +120,7 @@ async function save() {
fileIds: files.value.map((file) => file.id),
isSensitive: isSensitive.value,
});
router.push(`/gallery/${created.id}`);
router.push(`/gallery/${created?.id}`);
}
}

View file

@ -447,18 +447,20 @@ import { ref } from "vue";
import MkTextarea from "@/components/form/textarea.vue";
import { definePageMetadata } from "@/scripts/page-metadata";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
defineProps<{
popup?: boolean;
}>();
const sampleEmoji = getInstanceInfo().emojis.slice(0, 1);
const preview_mention = ref("@example");
const preview_hashtag = ref("#test");
const preview_link = ref(`[${i18n.ts._mfm.dummy}](https://firefish.dev)`);
const preview_emoji = ref(
instance.emojis.length ? `:${instance.emojis[0].name}:` : ":emojiname:",
sampleEmoji.length > 0 ? `:${sampleEmoji[0].name}:` : ":emojiname:",
);
const preview_bold = ref(`**${i18n.ts._mfm.dummy}**`);
const preview_small = ref(

View file

@ -48,7 +48,7 @@ import MkSuperMenu from "@/components/MkSuperMenu.vue";
import { signOut } from "@/account";
import { me } from "@/me";
import { unisonReload } from "@/scripts/unison-reload";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { useRouter } from "@/router";
import {
definePageMetadata,
@ -71,7 +71,7 @@ const router = useRouter();
const narrow = ref(false);
const NARROW_THRESHOLD = 600;
const currentPage = computed(() => router.currentRef.value.child);
const currentPage = computed(() => router.currentRef.value?.child);
const ro = new ResizeObserver((entries, observer) => {
if (entries.length === 0) return;
@ -282,7 +282,7 @@ onUnmounted(() => {
watch(router.currentRef, (to) => {
if (
to.route.name === "settings" &&
to?.route.name === "settings" &&
to.child?.route.name == null &&
!narrow.value
) {
@ -291,7 +291,8 @@ watch(router.currentRef, (to) => {
});
const emailNotConfigured = computed(
() => instance.enableEmail && (me.email == null || !me.emailVerified),
() =>
getInstanceInfo().enableEmail && (me?.email == null || !me.emailVerified),
);
provideMetadataReceiver((info) => {

View file

@ -157,7 +157,7 @@ import { selectFile } from "@/scripts/select-file";
import { isDeviceDarkmode } from "@/scripts/is-device-darkmode";
import { ColdDeviceStorage, defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { uniqueBy } from "@/scripts/array";
import { fetchThemes, getThemes } from "@/theme-store";
import { definePageMetadata } from "@/scripts/page-metadata";
@ -165,9 +165,10 @@ import icon from "@/scripts/icon";
const installedThemes = ref(getThemes());
const builtinThemes = getBuiltinThemesRef();
const { defaultDarkTheme, defaultLightTheme } = getInstanceInfo();
const instanceDarkTheme = computed(() =>
instance.defaultDarkTheme ? JSON5.parse(instance.defaultDarkTheme) : null,
defaultDarkTheme ? JSON5.parse(defaultDarkTheme) : null,
);
const installedDarkThemes = computed(() =>
installedThemes.value.filter((t) => t.base === "dark" || t.kind === "dark"),
@ -176,7 +177,7 @@ const builtinDarkThemes = computed(() =>
builtinThemes.value.filter((t) => t.base === "dark" || t.kind === "dark"),
);
const instanceLightTheme = computed(() =>
instance.defaultLightTheme ? JSON5.parse(instance.defaultLightTheme) : null,
defaultLightTheme ? JSON5.parse(defaultLightTheme) : null,
);
const installedLightThemes = computed(() =>
installedThemes.value.filter((t) => t.base === "light" || t.kind === "light"),

View file

@ -74,7 +74,7 @@ import XPostForm from "@/components/MkPostForm.vue";
import * as os from "@/os";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { isModerator, isSignedIn, me } from "@/me";
import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind";
@ -86,17 +86,22 @@ if (isSignedIn(me) && defaultStore.reactiveState.tutorial.value !== -1) {
os.popup(XTutorial, {}, {}, "closed");
}
const {
disableLocalTimeline,
enableGuestTimeline,
disableRecommendedTimeline,
disableGlobalTimeline,
} = getInstanceInfo();
const isHomeTimelineAvailable = isSignedIn(me);
const isLocalTimelineAvailable =
(!instance.disableLocalTimeline &&
(isSignedIn(me) || instance.enableGuestTimeline)) ||
(!disableLocalTimeline && (isSignedIn(me) || enableGuestTimeline)) ||
isModerator;
const isSocialTimelineAvailable = isLocalTimelineAvailable && isSignedIn(me);
const isRecommendedTimelineAvailable =
!instance.disableRecommendedTimeline && isSignedIn(me);
!disableRecommendedTimeline && isSignedIn(me);
const isGlobalTimelineAvailable =
(!instance.disableGlobalTimeline &&
(isSignedIn(me) || instance.enableGuestTimeline)) ||
(!disableGlobalTimeline && (isSignedIn(me) || enableGuestTimeline)) ||
isModerator;
const keymap = {
t: focus,
@ -201,7 +206,7 @@ function saveSrc(
}
function focus(): void {
tlComponent.value.focus();
tlComponent.value?.focus();
}
const headerActions = computed(() =>

View file

@ -316,7 +316,7 @@
type="number"
:placeholder="
i18n.t('defaultValueIs', {
value: instance.driveCapacityPerLocalUserMb,
value: defaultDriveCapacity,
})
"
@update:model-value="applyDriveCapacityOverride"
@ -364,7 +364,7 @@ import { userPage } from "@/filters/user";
import { definePageMetadata } from "@/scripts/page-metadata";
import { i18n } from "@/i18n";
import { isAdmin, isModerator } from "@/me";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const props = defineProps<{
@ -391,6 +391,8 @@ const filesPagination = {
})),
};
const defaultDriveCapacity = getInstanceInfo().driveCapacityPerLocalUserMb;
function createFetcher() {
if (isModerator) {
return () =>

View file

@ -17,8 +17,8 @@
<div class="main">
<img
:src="
instance.faviconUrl ||
instance.iconUrl ||
faviconUrl ||
iconUrl ||
'/favicon.ico'
"
alt=""
@ -105,7 +105,7 @@ import MkButton from "@/components/MkButton.vue";
import MkFeaturedPhotos from "@/components/MkFeaturedPhotos.vue";
import { instanceName } from "@/config";
import * as os from "@/os";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import { defaultReactions } from "@/store";
import icon from "@/scripts/icon";
@ -116,6 +116,8 @@ const tags = ref();
const onlineUsersCount = ref();
const instances = ref();
const { faviconUrl, iconUrl } = getInstanceInfo();
os.api("meta", { detail: true }).then((_meta) => {
meta.value = _meta;
});
@ -181,12 +183,12 @@ function showMenu(ev) {
os.pageWindow("/about-firefish");
},
},
instance.tosUrl
getInstanceInfo.tosUrl
? {
text: i18n.ts.tos,
icon: `${icon("ph-scroll")}`,
action: () => {
window.open(instance.tosUrl, "_blank");
window.open(getInstanceInfo.tosUrl, "_blank");
},
}
: null,

View file

@ -1,7 +1,7 @@
import XTutorial from "@/components/MkTutorialDialog.vue";
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
import { defaultStore } from "@/store";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { host } from "@/config";
import * as os from "@/os";
import { i18n } from "@/i18n";
@ -9,6 +9,7 @@ import icon from "@/scripts/icon";
import type { MenuItem } from "@/types/menu";
const instanceSpecificItems: MenuItem[] = [];
const instance = getInstanceInfo();
if (instance.tosUrl != null) {
instanceSpecificItems.push({

View file

@ -159,7 +159,7 @@ import { isAdmin, isModerator, me } from "@/me";
import { openHelpMenu_ } from "@/scripts/helpMenu";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { version } from "@/config";
import icon from "@/scripts/icon";
@ -189,13 +189,20 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
calcViewState();
});
const {
maintainerName,
maintainerEmail,
disableRegistration,
enableHcaptcha,
enableRecaptcha,
enableEmail,
} = getInstanceInfo();
const noMaintainerInformation =
isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
isEmpty(maintainerName) || isEmpty(maintainerEmail);
const noBotProtection =
!instance.disableRegistration &&
!instance.enableHcaptcha &&
!instance.enableRecaptcha;
const noEmailServer = !instance.enableEmail;
!disableRegistration && !enableHcaptcha && !enableRecaptcha;
const noEmailServer = !enableEmail;
const thereIsUnresolvedAbuseReport = ref(false);
const updateAvailable = ref(false);

View file

@ -51,7 +51,7 @@ import { removeColumn, updateColumn } from "./deck-store";
import XTimeline from "@/components/MkTimeline.vue";
import * as os from "@/os";
import { isModerator, isSignedIn, me } from "@/me";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -69,18 +69,23 @@ const disabled = ref(false);
const indicated = ref(false);
const columnActive = ref(true);
const {
disableLocalTimeline,
disableRecommendedTimeline,
disableGlobalTimeline,
} = getInstanceInfo();
onMounted(() => {
if (props.column.tl == null) {
setType();
} else if (isSignedIn(me)) {
disabled.value =
!isModerator &&
((instance.disableLocalTimeline &&
((disableLocalTimeline &&
["local", "social"].includes(props.column.tl)) ||
(instance.disableRecommendedTimeline &&
(disableRecommendedTimeline &&
["recommended"].includes(props.column.tl)) ||
(instance.disableGlobalTimeline &&
["global"].includes(props.column.tl)));
(disableGlobalTimeline && ["global"].includes(props.column.tl)));
}
});

View file

@ -3,7 +3,7 @@
<div
v-if="mainRouter.currentRoute?.name === 'index'"
class="banner"
:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
:style="{ backgroundImage: `url(${bannerUrl})` }"
>
<div>
<h1 v-if="meta">
@ -32,7 +32,7 @@
<div
v-else
class="banner-mini"
:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
:style="{ backgroundImage: `url(${bannerUrl})` }"
>
<div>
<h1 v-if="meta">
@ -86,7 +86,7 @@ import MkButton from "@/components/MkButton.vue";
import { ColdDeviceStorage, defaultStore } from "@/store";
import { mainRouter } from "@/router";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
const DESKTOP_THRESHOLD = 1100;
@ -111,6 +111,7 @@ export default defineComponent({
mainRouter,
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
i18n,
bannerUrl: getInstanceInfo().bannerUrl,
};
},
@ -122,7 +123,6 @@ export default defineComponent({
defaultStore.set("darkMode", !defaultStore.state.darkMode);
},
s: search,
"h|/": this.help,
};
},
},
@ -148,13 +148,6 @@ export default defineComponent({
},
methods: {
// @ThatOneCalculator: Are these methods even used?
// I can't find references to them anywhere else in the code...
// setParallax(el) {
// new simpleParallax(el);
// },
changePage(page) {
if (page == null) return;
@ -166,12 +159,6 @@ export default defineComponent({
top() {
window.scroll({ top: 0, behavior: "smooth" });
},
help() {
// TODO(thatonecalculator): popup with keybinds
// window.open('https://misskey-hub.net/docs/keyboard-shortcut.md', '_blank');
console.log("d = dark/light mode, s = search, p = post :3");
},
},
});
</script>

View file

@ -88,10 +88,9 @@ import XKanban from "./kanban.vue";
import { host, instanceName } from "@/config";
import { search } from "@/scripts/search";
import * as os from "@/os";
import { instance } from "@/instance";
import XSigninDialog from "@/components/MkSigninDialog.vue";
import XSignupDialog from "@/components/MkSignupDialog.vue";
import { ColdDeviceStorage, defaultStore } from "@/store";
import { defaultStore } from "@/store";
import { mainRouter } from "@/router";
import type { PageMetadata } from "@/scripts/page-metadata";
import { provideMetadataReceiver } from "@/scripts/page-metadata";
@ -110,30 +109,7 @@ provideMetadataReceiver((info) => {
}
});
const announcements = {
endpoint: "announcements",
limit: 10,
};
const isTimelineAvailable =
!instance.disableLocalTimeline ||
!instance.disableRecommendedTimeline ||
!instance.disableGlobalTimeline;
const showMenu = ref(false);
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
const narrow = ref(window.innerWidth < 1280);
const meta = ref();
const keymap = computed(() => {
return {
d: () => {
if (ColdDeviceStorage.get("syncDeviceDarkMode")) return;
defaultStore.set("darkMode", !defaultStore.state.darkMode);
},
s: search,
};
});
const root = computed(() => mainRouter.currentRoute.value.name === "index");
const root = computed(() => mainRouter.currentRoute.value?.name === "index");
os.api("meta", { detail: true }).then((res) => {
meta.value = res;

View file

@ -6,14 +6,6 @@
><i :class="icon('ph-house icon')"></i
>{{ i18n.ts.home }}</MkA
>
<!-- <MkA
v-if="isTimelineAvailable"
to="/timeline"
class="link"
active-class="active"
><i :class="icon('ph-chats-circle icon')"></i
>{{ i18n.ts.timeline }}</MkA
> -->
<MkA to="/explore" class="link" active-class="active"
><i :class="icon('ph-compass icon')"></i
>{{ i18n.ts.explore }}</MkA
@ -109,7 +101,6 @@ import { defineComponent } from "vue";
import XSigninDialog from "@/components/MkSigninDialog.vue";
import XSignupDialog from "@/components/MkSignupDialog.vue";
import * as os from "@/os";
import { instance } from "@/instance";
import { search } from "@/scripts/search";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -126,11 +117,6 @@ export default defineComponent({
narrow: null,
showMenu: false,
i18n,
isTimelineAvailable:
!instance.disableLocalTimeline ||
!instance.disableRecommendedTimeline ||
!instance.disableGlobalTimeline,
icon,
};
},
@ -162,6 +148,7 @@ export default defineComponent({
},
search,
icon,
},
});
</script>

View file

@ -4,7 +4,7 @@
:style="{
backgroundImage: transparent
? 'none'
: `url(${instance.backgroundImageUrl})`,
: `url(${backgroundImageUrl})`,
}"
>
<div class="back" :class="{ transparent }"></div>
@ -91,7 +91,7 @@ import XSigninDialog from "@/components/MkSigninDialog.vue";
import XSignupDialog from "@/components/MkSignupDialog.vue";
import MkButton from "@/components/MkButton.vue";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
export default defineComponent({
components: {
@ -129,7 +129,7 @@ export default defineComponent({
limit: 10,
},
i18n,
instance,
backgroundImageUrl: getInstanceInfo().backgroundImageUrl,
};
},

View file

@ -34,7 +34,7 @@ import type { Widget, WidgetComponentExpose } from "./widget";
import { useWidgetPropsManager } from "./widget";
import type { GetFormResultType } from "@/scripts/form";
import { host } from "@/config";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
const name = "serverInfo";
@ -47,6 +47,8 @@ const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
const { configure } = useWidgetPropsManager(name, widgetPropsDef, props, emit);
const instance = getInstanceInfo();
defineExpose<WidgetComponentExpose>({
name,
configure,

View file

@ -12,13 +12,13 @@
<i :class="icon('ph-sort-ascending')"></i></button
></template>
<div v-if="!instance.enableServerMachineStats" class="mkw-serverMetric">
<div v-if="!enableServerMachineStats" class="mkw-serverMetric">
<h3 style="text-align: center; color: var(--error)">
{{ i18n.ts.disabled }}
</h3>
</div>
<div
v-else-if="meta && instance.enableServerMachineStats"
v-else-if="meta && enableServerMachineStats"
class="mkw-serverMetric"
>
<XCpuMemory
@ -62,7 +62,7 @@ import type { GetFormResultType } from "@/scripts/form";
import * as os from "@/os";
import { useStream } from "@/stream";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getInstanceInfo } from "@/instance";
import icon from "@/scripts/icon";
const name = "serverMetric";
@ -96,6 +96,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(
);
const meta = ref(null);
const { enableServerMachineStats } = getInstanceInfo();
os.apiGet("server-info", {}).then((res) => {
meta.value = res;

View file

@ -405,6 +405,7 @@ export type DetailedInstanceMetadata = LiteInstanceMetadata & {
emailRequiredForSignup: boolean;
mascotImageUrl: string;
bannerUrl: string;
backgroundImageUrl: string;
errorImageUrl: string;
iconUrl: string | null;
maxCaptionTextLength: number;