Merge branch 'refactor/no-reactivity' into 'develop'

refactor: ♻️ No Vue Reactivity

See merge request firefish/firefish!10560
This commit is contained in:
Kainoa Kanter 2023-08-12 00:59:31 +00:00
commit 3d8fb7b76f
247 changed files with 3327 additions and 3021 deletions

View File

@ -65,6 +65,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkSwitch from "@/components/form/switch.vue"; import MkSwitch from "@/components/form/switch.vue";
import MkKeyValue from "@/components/MkKeyValue.vue"; import MkKeyValue from "@/components/MkKeyValue.vue";
@ -79,11 +81,11 @@ const emit = defineEmits<{
(ev: "resolved", reportId: string): void; (ev: "resolved", reportId: string): void;
}>(); }>();
const forward = $ref(props.report.forwarded); const forward = ref(props.report.forwarded);
function resolve() { function resolve() {
os.apiWithDialog("admin/resolve-abuse-user-report", { os.apiWithDialog("admin/resolve-abuse-user-report", {
forward, forward: forward.value,
reportId: props.report.id, reportId: props.report.id,
}).then(() => { }).then(() => {
emit("resolved", props.report.id); emit("resolved", props.report.id);

View File

@ -108,7 +108,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from "vue"; import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import { globalEvents } from "@/events.js"; import { globalEvents } from "@/events.js";
@ -167,19 +167,19 @@ const texts = computed(() => {
}); });
let enabled = true, let enabled = true,
majorGraduationColor = $ref<string>(), majorGraduationColor = ref<string>(),
// let minorGraduationColor = $ref<string>(); // let minorGraduationColor = $ref<string>();
sHandColor = $ref<string>(), sHandColor = ref<string>(),
mHandColor = $ref<string>(), mHandColor = ref<string>(),
hHandColor = $ref<string>(), hHandColor = ref<string>(),
nowColor = $ref<string>(), nowColor = ref<string>(),
h = $ref<number>(0), h = ref<number>(0),
m = $ref<number>(0), m = ref<number>(0),
s = $ref<number>(0), s = ref<number>(0),
hAngle = $ref<number>(0), hAngle = ref<number>(0),
mAngle = $ref<number>(0), mAngle = ref<number>(0),
sAngle = $ref<number>(0), sAngle = ref<number>(0),
disableSAnimate = $ref(false), disableSAnimate = ref(false),
sOneRound = false; sOneRound = false;
function tick() { function tick() {
@ -187,29 +187,31 @@ function tick() {
now.setMinutes( now.setMinutes(
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset), now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
); );
s = now.getSeconds(); s.value = now.getSeconds();
m = now.getMinutes(); m.value = now.getMinutes();
h = now.getHours(); h.value = now.getHours();
hAngle = hAngle.value =
(Math.PI * ((h % (props.twentyfour ? 24 : 12)) + (m + s / 60) / 60)) / (Math.PI *
((h.value % (props.twentyfour ? 24 : 12)) +
(m.value + s.value / 60) / 60)) /
(props.twentyfour ? 12 : 6); (props.twentyfour ? 12 : 6);
mAngle = (Math.PI * (m + s / 60)) / 30; mAngle.value = (Math.PI * (m.value + s.value / 60)) / 30;
if (sOneRound) { if (sOneRound) {
// (59->0) // (59->0)
sAngle = (Math.PI * 60) / 30; sAngle.value = (Math.PI * 60) / 30;
window.setTimeout(() => { window.setTimeout(() => {
disableSAnimate = true; disableSAnimate.value = true;
window.setTimeout(() => { window.setTimeout(() => {
sAngle = 0; sAngle.value = 0;
window.setTimeout(() => { window.setTimeout(() => {
disableSAnimate = false; disableSAnimate.value = false;
}, 100); }, 100);
}, 100); }, 100);
}, 700); }, 700);
} else { } else {
sAngle = (Math.PI * s) / 30; sAngle.value = (Math.PI * s.value) / 30;
} }
sOneRound = s === 59; sOneRound = s.value === 59;
} }
tick(); tick();
@ -220,16 +222,16 @@ function calcColors() {
const accent = tinycolor( const accent = tinycolor(
computedStyle.getPropertyValue("--accent"), computedStyle.getPropertyValue("--accent"),
).toHexString(); ).toHexString();
majorGraduationColor = dark majorGraduationColor.value = dark
? "rgba(255, 255, 255, 0.3)" ? "rgba(255, 255, 255, 0.3)"
: "rgba(0, 0, 0, 0.3)"; : "rgba(0, 0, 0, 0.3)";
// minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; // minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
sHandColor = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)"; sHandColor.value = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
mHandColor = tinycolor( mHandColor.value = tinycolor(
computedStyle.getPropertyValue("--fg"), computedStyle.getPropertyValue("--fg"),
).toHexString(); ).toHexString();
hHandColor = accent; hHandColor.value = accent;
nowColor = accent; nowColor.value = accent;
} }
calcColors(); calcColors();

View File

@ -27,7 +27,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from "vue"; import { nextTick, onMounted, ref } from "vue";
const props = defineProps<{ const props = defineProps<{
type?: "button" | "submit" | "reset"; type?: "button" | "submit" | "reset";
@ -49,13 +49,13 @@ const emit = defineEmits<{
(ev: "click", payload: MouseEvent): void; (ev: "click", payload: MouseEvent): void;
}>(); }>();
const el = $ref<HTMLElement | null>(null); const el = ref<HTMLElement | null>(null);
const ripples = $ref<HTMLElement | null>(null); const ripples = ref<HTMLElement | null>(null);
onMounted(() => { onMounted(() => {
if (props.autofocus) { if (props.autofocus) {
nextTick(() => { nextTick(() => {
el!.focus(); el.value!.focus();
}); });
} }
}); });
@ -81,7 +81,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px"; ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px";
ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px"; ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px";
ripples!.appendChild(ripple); ripples.value!.appendChild(ripple);
const circleCenterX = evt.clientX - rect.left; const circleCenterX = evt.clientX - rect.left;
const circleCenterY = evt.clientY - rect.top; const circleCenterY = evt.clientY - rect.top;
@ -101,7 +101,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.opacity = "0"; ripple.style.opacity = "0";
}, 1000); }, 1000);
window.setTimeout(() => { window.setTimeout(() => {
if (ripples) ripples.removeChild(ripple); if (ripples.value) ripples.value.removeChild(ripple);
}, 2000); }, 2000);
} }
</script> </script>

View File

@ -11,6 +11,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue"; import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -20,7 +22,7 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -12,7 +12,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted } from "vue"; import { onBeforeUnmount, onMounted, ref } from "vue";
import MkMenu from "@/components/MkMenu.vue"; import MkMenu from "@/components/MkMenu.vue";
import type { MenuItem } from "@/types/menu"; import type { MenuItem } from "@/types/menu";
import contains from "@/scripts/contains"; import contains from "@/scripts/contains";
@ -27,16 +27,16 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
const rootEl = $ref<HTMLDivElement>(); const rootEl = ref<HTMLDivElement>();
const zIndex = $ref<number>(os.claimZIndex("high")); const zIndex = ref<number>(os.claimZIndex("high"));
onMounted(() => { onMounted(() => {
let left = props.ev.pageX + 1, // + 1 let left = props.ev.pageX + 1, // + 1
top = props.ev.pageY + 1; // + 1 top = props.ev.pageY + 1; // + 1
const width = rootEl.offsetWidth; const width = rootEl.value.offsetWidth;
const height = rootEl.offsetHeight; const height = rootEl.value.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) { if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset; left = window.innerWidth - width + window.pageXOffset;
@ -54,8 +54,8 @@ onMounted(() => {
left = 0; left = 0;
} }
rootEl.style.top = `${top}px`; rootEl.value.style.top = `${top}px`;
rootEl.style.left = `${left}px`; rootEl.value.style.left = `${left}px`;
document.body.addEventListener("mousedown", onMousedown); document.body.addEventListener("mousedown", onMousedown);
}); });
@ -65,7 +65,8 @@ onBeforeUnmount(() => {
}); });
function onMousedown(evt: Event) { function onMousedown(evt: Event) {
if (!contains(rootEl, evt.target) && rootEl !== evt.target) emit("closed"); if (!contains(rootEl.value, evt.target) && rootEl.value !== evt.target)
emit("closed");
} }
</script> </script>

View File

@ -36,7 +36,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import Cropper from "cropperjs"; import Cropper from "cropperjs";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
@ -62,10 +62,10 @@ const props = defineProps<{
const imgUrl = `${url}/proxy/image.webp?${query({ const imgUrl = `${url}/proxy/image.webp?${query({
url: props.file.url, url: props.file.url,
})}`; })}`;
const dialogEl = $ref<InstanceType<typeof XModalWindow>>(); const dialogEl = ref<InstanceType<typeof XModalWindow>>();
const imgEl = $ref<HTMLImageElement>(); const imgEl = ref<HTMLImageElement>();
let cropper: Cropper | null = null, let cropper: Cropper | null = null,
loading = $ref(true); loading = ref(true);
const ok = async () => { const ok = async () => {
const promise = new Promise<misskey.entities.DriveFile>(async (res) => { const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
@ -96,16 +96,16 @@ const ok = async () => {
const f = await promise; const f = await promise;
emit("ok", f); emit("ok", f);
dialogEl.close(); dialogEl.value.close();
}; };
const cancel = () => { const cancel = () => {
emit("cancel"); emit("cancel");
dialogEl.close(); dialogEl.value.close();
}; };
const onImageLoad = () => { const onImageLoad = () => {
loading = false; loading.value = false;
if (cropper) { if (cropper) {
cropper.getCropperImage()!.$center("contain"); cropper.getCropperImage()!.$center("contain");
@ -114,7 +114,7 @@ const onImageLoad = () => {
}; };
onMounted(() => { onMounted(() => {
cropper = new Cropper(imgEl, {}); cropper = new Cropper(imgEl.value, {});
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);

View File

@ -199,7 +199,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue"; import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from "vue";
import * as Acct from "firefish-js/built/acct"; import * as Acct from "firefish-js/built/acct";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -281,17 +281,15 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
const inputValue = ref<string | number | null>(props.input?.default ?? null); const inputValue = ref<string | number | null>(props.input?.default ?? null);
const selectedValue = ref(props.select?.default ?? null); const selectedValue = ref(props.select?.default ?? null);
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">( let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
null, const okButtonDisabled = computed<boolean>(() => {
);
const okButtonDisabled = $computed<boolean>(() => {
if (props.input) { if (props.input) {
if (props.input.minLength) { if (props.input.minLength) {
if ( if (
(inputValue.value || inputValue.value === "") && (inputValue.value || inputValue.value === "") &&
(inputValue.value as string).length < props.input.minLength (inputValue.value as string).length < props.input.minLength
) { ) {
disabledReason = "charactersBelow"; disabledReason.value = "charactersBelow";
return true; return true;
} }
} }
@ -300,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
inputValue.value && inputValue.value &&
(inputValue.value as string).length > props.input.maxLength (inputValue.value as string).length > props.input.maxLength
) { ) {
disabledReason = "charactersExceeded"; disabledReason.value = "charactersExceeded";
return true; return true;
} }
} }

View File

@ -61,7 +61,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from "vue"; import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import type * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
import { stream } from "@/stream"; import { stream } from "@/stream";
@ -88,13 +88,13 @@ const props = withDefaults(
const isBlocking = computed(() => props.user.isBlocking); const isBlocking = computed(() => props.user.isBlocking);
let state = $ref(i18n.ts.processing); let state = ref(i18n.ts.processing);
let isFollowing = $ref(props.user.isFollowing); let isFollowing = ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = $ref( let hasPendingFollowRequestFromYou = ref(
props.user.hasPendingFollowRequestFromYou, props.user.hasPendingFollowRequestFromYou,
); );
let wait = $ref(false); let wait = ref(false);
const connection = stream.useChannel("main"); const connection = stream.useChannel("main");
if (props.user.isFollowing == null) { if (props.user.isFollowing == null) {
@ -105,13 +105,14 @@ if (props.user.isFollowing == null) {
function onFollowChange(user: Misskey.entities.UserDetailed) { function onFollowChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) { if (user.id === props.user.id) {
isFollowing = user.isFollowing; isFollowing.value = user.isFollowing;
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; hasPendingFollowRequestFromYou.value =
user.hasPendingFollowRequestFromYou;
} }
} }
async function onClick() { async function onClick() {
wait = true; wait.value = true;
try { try {
if (isBlocking.value) { if (isBlocking.value) {
@ -130,7 +131,7 @@ async function onClick() {
}); });
} }
emit("refresh"); emit("refresh");
} else if (isFollowing) { } else if (isFollowing.value) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: "warning", type: "warning",
text: i18n.t("unfollowConfirm", { text: i18n.t("unfollowConfirm", {
@ -144,22 +145,22 @@ async function onClick() {
userId: props.user.id, userId: props.user.id,
}); });
} else { } else {
if (hasPendingFollowRequestFromYou) { if (hasPendingFollowRequestFromYou.value) {
await os.api("following/requests/cancel", { await os.api("following/requests/cancel", {
userId: props.user.id, userId: props.user.id,
}); });
hasPendingFollowRequestFromYou = false; hasPendingFollowRequestFromYou.value = false;
} else { } else {
await os.api("following/create", { await os.api("following/create", {
userId: props.user.id, userId: props.user.id,
}); });
hasPendingFollowRequestFromYou = true; hasPendingFollowRequestFromYou.value = true;
} }
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally { } finally {
wait = false; wait.value = false;
} }
} }

View File

@ -62,6 +62,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -75,20 +77,20 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let dialog: InstanceType<typeof XModalWindow> = $ref(); let dialog: InstanceType<typeof XModalWindow> = ref();
let username = $ref(""); let username = ref("");
let email = $ref(""); let email = ref("");
let processing = $ref(false); let processing = ref(false);
async function onSubmit() { async function onSubmit() {
processing = true; processing.value = true;
await os.apiWithDialog("request-reset-password", { await os.apiWithDialog("request-reset-password", {
username, username: username.value,
email, email: email.value,
}); });
emit("done"); emit("done");
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -8,7 +8,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick, watch } from "vue"; import { onMounted, nextTick, watch, shallowRef, ref } from "vue";
import { Chart } from "chart.js"; import { Chart } from "chart.js";
import * as os from "@/os"; import * as os from "@/os";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -23,11 +23,11 @@ const props = defineProps<{
src: string; src: string;
}>(); }>();
const rootEl = $shallowRef<HTMLDivElement>(null); const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = $shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
let fetching = $ref(true); let fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({ const { handler: externalTooltipHandler } = useChartTooltip({
position: "middle", position: "middle",
@ -43,8 +43,8 @@ async function renderChart() {
chartInstance.destroy(); chartInstance.destroy();
} }
const wide = rootEl.offsetWidth > 700; const wide = rootEl.value.offsetWidth > 700;
const narrow = rootEl.offsetWidth < 400; const narrow = rootEl.value.offsetWidth < 400;
const weeks = wide ? 50 : narrow ? 10 : 25; const weeks = wide ? 50 : narrow ? 10 : 25;
const chartLimit = 7 * weeks; const chartLimit = 7 * weeks;
@ -113,7 +113,7 @@ async function renderChart() {
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote); values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
} }
fetching = false; fetching.value = false;
await nextTick(); await nextTick();
@ -131,7 +131,7 @@ async function renderChart() {
const marginEachCell = 4; const marginEachCell = 4;
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: "matrix", type: "matrix",
data: { data: {
datasets: [ datasets: [
@ -247,7 +247,7 @@ async function renderChart() {
watch( watch(
() => props.src, () => props.src,
() => { () => {
fetching = true; fetching.value = true;
renderChart(); renderChart();
}, },
); );

View File

@ -26,6 +26,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import bytes from "@/filters/bytes"; import bytes from "@/filters/bytes";
@ -43,7 +45,7 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
const modal = $ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -24,7 +24,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import { decodeBlurHash } from "fast-blurhash"; import { decodeBlurHash } from "fast-blurhash";
const props = withDefaults( const props = withDefaults(
@ -48,20 +48,20 @@ const props = withDefaults(
}, },
); );
const canvas = $ref<HTMLCanvasElement>(); const canvas = ref<HTMLCanvasElement>();
let loaded = $ref(false); let loaded = ref(false);
function draw() { function draw() {
if (props.hash == null || canvas == null) return; if (props.hash == null || canvas.value == null) return;
const pixels = decodeBlurHash(props.hash, props.size, props.size); const pixels = decodeBlurHash(props.hash, props.size, props.size);
const ctx = canvas.getContext("2d"); const ctx = canvas.value.getContext("2d");
const imageData = ctx!.createImageData(props.size, props.size); const imageData = ctx!.createImageData(props.size, props.size);
imageData.data.set(pixels); imageData.data.set(pixels);
ctx!.putImageData(imageData, 0, 0); ctx!.putImageData(imageData, 0, 0);
} }
function onLoad() { function onLoad() {
loaded = true; loaded.value = true;
} }
onMounted(() => { onMounted(() => {

View File

@ -24,6 +24,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import * as firefish from "firefish-js"; import * as firefish from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue"; import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -33,7 +35,7 @@ const props = defineProps<{
instance: firefish.entities.Instance; instance: firefish.entities.Instance;
}>(); }>();
let chartValues = $ref<number[] | null>(null); let chartValues = ref<number[] | null>(null);
os.apiGet("charts/instance", { os.apiGet("charts/instance", {
host: props.instance.host, host: props.instance.host,
@ -42,7 +44,7 @@ os.apiGet("charts/instance", {
}).then((res) => { }).then((res) => {
// //
res.requests.received.splice(0, 1); res.requests.received.splice(0, 1);
chartValues = res.requests.received; chartValues.value = res.requests.received;
}); });
function getInstanceIcon(instance): string { function getInstanceIcon(instance): string {

View File

@ -56,6 +56,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -68,28 +70,28 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let hostname = $ref(""); let hostname = ref("");
let instances: Instance[] = $ref([]); let instances: Instance[] = ref([]);
let selected: Instance | null = $ref(null); let selected: Instance | null = ref(null);
let dialogEl = $ref<InstanceType<typeof XModalWindow>>(); let dialogEl = ref<InstanceType<typeof XModalWindow>>();
let searchOrderLatch = 0; let searchOrderLatch = 0;
const search = () => { const search = () => {
if (hostname === "") { if (hostname.value === "") {
instances = []; instances.value = [];
return; return;
} }
const searchId = ++searchOrderLatch; const searchId = ++searchOrderLatch;
os.api("federation/instances", { os.api("federation/instances", {
host: hostname, host: hostname.value,
limit: 10, limit: 10,
blocked: false, blocked: false,
suspended: false, suspended: false,
sort: "+pubSub", sort: "+pubSub",
}).then((_instances) => { }).then((_instances) => {
if (searchId !== searchOrderLatch) return; if (searchId !== searchOrderLatch) return;
instances = _instances.map( instances.value = _instances.map(
(x) => (x) =>
({ ({
id: x.id, id: x.id,
@ -101,14 +103,14 @@ const search = () => {
}; };
const ok = () => { const ok = () => {
if (selected == null) return; if (selected.value == null) return;
emit("ok", selected); emit("ok", selected.value);
dialogEl?.close(); dialogEl.value?.close();
}; };
const cancel = () => { const cancel = () => {
emit("cancel"); emit("cancel");
dialogEl?.close(); dialogEl.value?.close();
}; };
</script> </script>

View File

@ -102,7 +102,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref, shallowRef } from "vue";
import { Chart } from "chart.js"; import { Chart } from "chart.js";
import MkSelect from "@/components/form/select.vue"; import MkSelect from "@/components/form/select.vue";
import MkChart from "@/components/MkChart.vue"; import MkChart from "@/components/MkChart.vue";
@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
initChart(); initChart();
const chartLimit = 500; const chartLimit = 500;
let chartSpan = $ref<"hour" | "day">("hour"); let chartSpan = ref<"hour" | "day">("hour");
let chartSrc = $ref("active-users"); let chartSrc = ref("active-users");
let heatmapSrc = $ref("active-users"); let heatmapSrc = ref("active-users");
let subDoughnutEl = $shallowRef<HTMLCanvasElement>(); let subDoughnutEl = shallowRef<HTMLCanvasElement>();
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>(); let pubDoughnutEl = shallowRef<HTMLCanvasElement>();
const { handler: externalTooltipHandler1 } = useChartTooltip({ const { handler: externalTooltipHandler1 } = useChartTooltip({
position: "middle", position: "middle",
@ -189,7 +189,7 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => { onMounted(() => {
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => { os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
createDoughnut( createDoughnut(
subDoughnutEl, subDoughnutEl.value,
externalTooltipHandler1, externalTooltipHandler1,
fedStats.topSubInstances fedStats.topSubInstances
.map((x) => ({ .map((x) => ({
@ -210,7 +210,7 @@ onMounted(() => {
); );
createDoughnut( createDoughnut(
pubDoughnutEl, pubDoughnutEl.value,
externalTooltipHandler2, externalTooltipHandler2,
fedStats.topPubInstances fedStats.topPubInstances
.map((x) => ({ .map((x) => ({

View File

@ -11,6 +11,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import { instanceName } from "@/config"; import { instanceName } from "@/config";
import { instance as Instance } from "@/instance"; import { instance as Instance } from "@/instance";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy"; import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
@ -24,7 +26,7 @@ const props = defineProps<{
}; };
}>(); }>();
let ticker = $ref<HTMLElement | null>(null); let ticker = ref<HTMLElement | null>(null);
// if no instance data is given, this is for the local instance // if no instance data is given, this is for the local instance
const instance = props.instance ?? { const instance = props.instance ?? {

View File

@ -62,6 +62,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import { navbarItemDef } from "@/navbar"; import { navbarItemDef } from "@/navbar";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -89,7 +91,7 @@ const preferedModalType =
? "drawer" ? "drawer"
: "dialog"; : "dialog";
const modal = $ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
const menu = defaultStore.state.menu; const menu = defaultStore.state.menu;
@ -107,7 +109,7 @@ const items = Object.keys(navbarItemDef)
})); }));
function close() { function close() {
modal.close(); modal.value.close();
} }
</script> </script>

View File

@ -18,7 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from "vue"; import { defineAsyncComponent, ref } from "vue";
import { url as local } from "@/config"; import { url as local } from "@/config";
import { useTooltip } from "@/scripts/use-tooltip"; import { useTooltip } from "@/scripts/use-tooltip";
import * as os from "@/os"; import * as os from "@/os";
@ -35,9 +35,9 @@ const self = props.url.startsWith(local);
const attr = self ? "to" : "href"; const attr = self ? "to" : "href";
const target = self ? null : "_blank"; const target = self ? null : "_blank";
const el = $ref(); const el = ref();
useTooltip($$(el), (showing) => { useTooltip(el, (showing) => {
os.popup( os.popup(
defineAsyncComponent( defineAsyncComponent(
() => import("@/components/MkUrlPreviewPopup.vue"), () => import("@/components/MkUrlPreviewPopup.vue"),
@ -45,7 +45,7 @@ useTooltip($$(el), (showing) => {
{ {
showing, showing,
url: props.url, url: props.url,
source: el, source: el.value,
}, },
{}, {},
"closed", "closed",

View File

@ -104,7 +104,7 @@ const props = defineProps<{
raw?: boolean; raw?: boolean;
}>(); }>();
let hide = $ref(true); let hide = ref(true);
const plyr = ref(); const plyr = ref();
@ -145,7 +145,7 @@ function captionPopup() {
watch( watch(
() => props.media, () => props.media,
() => { () => {
hide = hide.value =
defaultStore.state.nsfw === "force" defaultStore.state.nsfw === "force"
? true ? true
: props.media.isSensitive && : props.media.isSensitive &&

View File

@ -56,7 +56,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import VuePlyr from "vue-plyr"; import VuePlyr from "vue-plyr";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { ColdDeviceStorage } from "@/store"; import { ColdDeviceStorage } from "@/store";
@ -70,15 +70,17 @@ const props = withDefaults(
{}, {},
); );
const audioEl = $ref<HTMLAudioElement | null>(); const audioEl = ref<HTMLAudioElement | null>();
let hide = $ref(true); let hide = ref(true);
function volumechange() { function volumechange() {
if (audioEl) ColdDeviceStorage.set("mediaVolume", audioEl.volume); if (audioEl.value)
ColdDeviceStorage.set("mediaVolume", audioEl.value.volume);
} }
onMounted(() => { onMounted(() => {
if (audioEl) audioEl.volume = ColdDeviceStorage.get("mediaVolume"); if (audioEl.value)
audioEl.value.volume = ColdDeviceStorage.get("mediaVolume");
}); });
</script> </script>

View File

@ -233,13 +233,13 @@ const emit = defineEmits<{
(ev: "close", actioned?: boolean): void; (ev: "close", actioned?: boolean): void;
}>(); }>();
let itemsEl = $ref<HTMLDivElement>(); let itemsEl = ref<HTMLDivElement>();
let items2: InnerMenuItem[] = $ref([]); let items2: InnerMenuItem[] = ref([]);
let child = $ref<InstanceType<typeof XChild>>(); let child = ref<InstanceType<typeof XChild>>();
let childShowingItem = $ref<MenuItem | null>(); let childShowingItem = ref<MenuItem | null>();
watch( watch(
() => props.items, () => props.items,
@ -255,24 +255,24 @@ watch(
// if item is Promise // if item is Promise
items[i] = { type: "pending" }; items[i] = { type: "pending" };
item.then((actualItem) => { item.then((actualItem) => {
items2[i] = actualItem; items2.value[i] = actualItem;
}); });
} }
} }
items2 = items as InnerMenuItem[]; items2.value = items as InnerMenuItem[];
}, },
{ {
immediate: true, immediate: true,
}, },
); );
let childMenu = $ref<MenuItem[] | null>(); let childMenu = ref<MenuItem[] | null>();
let childTarget = $ref<HTMLElement | null>(); let childTarget = ref<HTMLElement | null>();
function closeChild() { function closeChild() {
childMenu = null; childMenu.value = null;
childShowingItem = null; childShowingItem.value = null;
} }
function childActioned() { function childActioned() {
@ -282,11 +282,12 @@ function childActioned() {
function onGlobalMousedown(event: MouseEvent) { function onGlobalMousedown(event: MouseEvent) {
if ( if (
childTarget && childTarget.value &&
(event.target === childTarget || childTarget.contains(event.target)) (event.target === childTarget.value ||
childTarget.value.contains(event.target))
) )
return; return;
if (child && child.checkHit(event)) return; if (child.value && child.value.checkHit(event)) return;
closeChild(); closeChild();
} }
@ -305,9 +306,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
os.popupMenu(item.children, ev.currentTarget ?? ev.target); os.popupMenu(item.children, ev.currentTarget ?? ev.target);
close(); close();
} else { } else {
childTarget = ev.currentTarget ?? ev.target; childTarget.value = ev.currentTarget ?? ev.target;
childMenu = item.children; childMenu.value = item.children;
childShowingItem = item; childShowingItem.value = item;
} }
} }

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from "vue"; import { watch, ref } from "vue";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import { useInterval } from "@/scripts/use-interval"; import { useInterval } from "@/scripts/use-interval";
@ -37,10 +37,10 @@ const props = defineProps<{
const viewBoxX = 50; const viewBoxX = 50;
const viewBoxY = 50; const viewBoxY = 50;
const gradientId = uuid(); const gradientId = uuid();
let polylinePoints = $ref(""); let polylinePoints = ref("");
let polygonPoints = $ref(""); let polygonPoints = ref("");
let headX = $ref<number | null>(null); let headX = ref<number | null>(null);
let headY = $ref<number | null>(null); let headY = ref<number | null>(null);
const accent = tinycolor( const accent = tinycolor(
getComputedStyle(document.documentElement).getPropertyValue("--accent"), getComputedStyle(document.documentElement).getPropertyValue("--accent"),
); );
@ -55,12 +55,14 @@ function draw(): void {
(1 - n / peak) * viewBoxY, (1 - n / peak) * viewBoxY,
]); ]);
polylinePoints = _polylinePoints.map((xy) => `${xy[0]},${xy[1]}`).join(" "); polylinePoints.value = _polylinePoints
.map((xy) => `${xy[0]},${xy[1]}`)
.join(" ");
polygonPoints = `0,${viewBoxY} ${polylinePoints} ${viewBoxX},${viewBoxY}`; polygonPoints.value = `0,${viewBoxY} ${polylinePoints.value} ${viewBoxX},${viewBoxY}`;
headX = _polylinePoints[_polylinePoints.length - 1][0]; headX.value = _polylinePoints[_polylinePoints.length - 1][0];
headY = _polylinePoints[_polylinePoints.length - 1][1]; headY.value = _polylinePoints[_polylinePoints.length - 1][1];
} }
watch(() => props.src, draw, { immediate: true }); watch(() => props.src, draw, { immediate: true });

View File

@ -77,7 +77,16 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, watch, provide, onUnmounted } from "vue"; import {
nextTick,
onMounted,
watch,
provide,
onUnmounted,
ref,
shallowRef,
computed,
} from "vue";
import * as os from "@/os"; import * as os from "@/os";
import { isTouchUsing } from "@/scripts/touch"; import { isTouchUsing } from "@/scripts/touch";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -130,14 +139,14 @@ const emit = defineEmits<{
provide("modal", true); provide("modal", true);
let maxHeight = $ref<number>(); 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 = $shallowRef<HTMLElement>(); let content = shallowRef<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
let useSendAnime = $ref(false); let useSendAnime = ref(false);
const type = $computed<ModalTypes>(() => { const type = computed<ModalTypes>(() => {
if (props.preferType === "auto") { if (props.preferType === "auto") {
if ( if (
!defaultStore.state.disableDrawer && !defaultStore.state.disableDrawer &&
@ -152,28 +161,28 @@ const type = $computed<ModalTypes>(() => {
return props.preferType!; return props.preferType!;
} }
}); });
const isEnableBgTransparent = $computed( const isEnableBgTransparent = computed(
() => props.transparentBg && type === "popup", () => props.transparentBg && type.value === "popup",
); );
let transitionName = $computed(() => let transitionName = computed(() =>
defaultStore.state.animation defaultStore.state.animation
? useSendAnime ? useSendAnime.value
? "send" ? "send"
: type === "drawer" : type.value === "drawer"
? "modal-drawer" ? "modal-drawer"
: type === "popup" : type.value === "popup"
? "modal-popup" ? "modal-popup"
: "modal" : "modal"
: "", : "",
); );
let transitionDuration = $computed(() => let transitionDuration = computed(() =>
transitionName === "send" transitionName.value === "send"
? 400 ? 400
: transitionName === "modal-popup" : transitionName.value === "modal-popup"
? 100 ? 100
: transitionName === "modal" : transitionName.value === "modal"
? 200 ? 200
: transitionName === "modal-drawer" : transitionName.value === "modal-drawer"
? 200 ? 200
: 0, : 0,
); );
@ -187,12 +196,12 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
// history.forward(); // history.forward();
// } // }
if (opts.useSendAnimation) { if (opts.useSendAnimation) {
useSendAnime = true; useSendAnime.value = true;
} }
// eslint-disable-next-line vue/no-mutating-props // 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.value = false;
emit("close"); emit("close");
if (!props.noReturnFocus) { if (!props.noReturnFocus) {
focusedElement.focus(); focusedElement.focus();
@ -204,8 +213,8 @@ function onBgClick() {
emit("click"); emit("click");
} }
if (type === "drawer") { if (type.value === "drawer") {
maxHeight = window.innerHeight / 1.5; maxHeight.value = window.innerHeight / 1.5;
} }
const keymap = { const keymap = {
@ -216,21 +225,21 @@ const MARGIN = 16;
const align = () => { const align = () => {
if (props.src == null) return; if (props.src == null) return;
if (type === "drawer") return; if (type.value === "drawer") return;
if (type === "dialog") return; if (type.value === "dialog") return;
if (content == null) return; if (content.value == null) return;
const srcRect = props.src.getBoundingClientRect(); const srcRect = props.src.getBoundingClientRect();
const width = content!.offsetWidth; const width = content.value!.offsetWidth;
const height = content!.offsetHeight; const height = content.value!.offsetHeight;
let left; let left;
let top; let top;
const x = srcRect.left + (fixed ? 0 : window.pageXOffset); const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed ? 0 : window.pageYOffset); const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
if (props.anchor.x === "center") { if (props.anchor.x === "center") {
left = x + props.src.offsetWidth / 2 - width / 2; left = x + props.src.offsetWidth / 2 - width / 2;
@ -248,7 +257,7 @@ const align = () => {
top = y + props.src.offsetHeight; top = y + props.src.offsetHeight;
} }
if (fixed) { if (fixed.value) {
// //
if (left + width > window.innerWidth) { if (left + width > window.innerWidth) {
left = window.innerWidth - width; left = window.innerWidth - width;
@ -261,16 +270,16 @@ const align = () => {
if (top + height > window.innerHeight - MARGIN) { if (top + height > 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.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = upperSpace + MARGIN - height; top = upperSpace + MARGIN - height;
} }
} else { } else {
top = window.innerHeight - MARGIN - height; top = window.innerHeight - MARGIN - height;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} else { } else {
// //
@ -286,9 +295,9 @@ const align = () => {
if (top + height - window.scrollY > 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.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = window.scrollY + (upperSpace + MARGIN - height); top = window.scrollY + (upperSpace + MARGIN - height);
} }
} else { } else {
@ -300,7 +309,7 @@ const align = () => {
1; 1;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} }
@ -317,36 +326,43 @@ const align = () => {
if ( if (
top >= top >=
srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset) srcRect.top +
props.src.offsetHeight +
(fixed.value ? 0 : window.pageYOffset)
) { ) {
transformOriginY = "top"; transformOriginY = "top";
} else if (top + height <= srcRect.top + (fixed ? 0 : window.pageYOffset)) { } else if (
top + height <=
srcRect.top + (fixed.value ? 0 : window.pageYOffset)
) {
transformOriginY = "bottom"; transformOriginY = "bottom";
} }
if ( if (
left >= left >=
srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset) srcRect.left +
props.src.offsetWidth +
(fixed.value ? 0 : window.pageXOffset)
) { ) {
transformOriginX = "left"; transformOriginX = "left";
} else if ( } else if (
left + width <= left + width <=
srcRect.left + (fixed ? 0 : window.pageXOffset) srcRect.left + (fixed.value ? 0 : window.pageXOffset)
) { ) {
transformOriginX = "right"; transformOriginX = "right";
} }
transformOrigin = `${transformOriginX} ${transformOriginY}`; transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
content.style.left = left + "px"; content.value.style.left = left + "px";
content.style.top = top + "px"; content.value.style.top = top + "px";
}; };
const onOpened = () => { const onOpened = () => {
emit("opened"); emit("opened");
// //
const el = content!.children[0]; const el = content.value!.children[0];
el.addEventListener( el.addEventListener(
"mousedown", "mousedown",
(ev) => { (ev) => {
@ -378,7 +394,8 @@ onMounted(() => {
// eslint-disable-next-line vue/no-mutating-props // 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.value =
type.value === "drawer" || getFixedContainer(props.src) != null;
await nextTick(); await nextTick();
@ -390,7 +407,7 @@ onMounted(() => {
nextTick(() => { nextTick(() => {
new ResizeObserver((entries, observer) => { new ResizeObserver((entries, observer) => {
align(); align();
}).observe(content!); }).observe(content.value!);
}); });
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -52,7 +52,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, provide } from "vue"; import { ComputedRef, provide, ref, computed } from "vue";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import { popout as _popout } from "@/scripts/popout"; import { popout as _popout } from "@/scripts/popout";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
@ -76,27 +76,27 @@ const router = new Router(routes, props.initialPath);
router.addListener("push", (ctx) => {}); router.addListener("push", (ctx) => {});
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let rootEl = $ref(); let rootEl = ref();
let modal = $ref<InstanceType<typeof MkModal>>(); let modal = ref<InstanceType<typeof MkModal>>();
let path = $ref(props.initialPath); let path = ref(props.initialPath);
let width = $ref(860); let width = ref(860);
let height = $ref(660); let height = ref(660);
const history = []; const history = [];
provide("router", router); provide("router", router);
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
pageMetadata = info; pageMetadata.value = info;
}); });
provide("shouldOmitHeaderTitle", true); provide("shouldOmitHeaderTitle", true);
provide("shouldHeaderThin", true); provide("shouldHeaderThin", true);
const pageUrl = $computed(() => url + path); const pageUrl = computed(() => url + path.value);
const contextmenu = $computed(() => { const contextmenu = computed(() => {
return [ return [
{ {
type: "label", type: "label",
text: path, text: path.value,
}, },
{ {
icon: "ph-arrows-out-simple ph-bold ph-lg", icon: "ph-arrows-out-simple ph-bold ph-lg",
@ -113,15 +113,15 @@ const contextmenu = $computed(() => {
icon: "ph-arrow-square-out ph-bold ph-lg", icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(pageUrl, "_blank"); window.open(pageUrl.value, "_blank");
modal.close(); modal.value.close();
}, },
}, },
{ {
icon: "ph-link-simple ph-bold ph-lg", icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink, text: i18n.ts.copyLink,
action: () => { action: () => {
copyToClipboard(pageUrl); copyToClipboard(pageUrl.value);
}, },
}, },
]; ];
@ -137,17 +137,17 @@ function back() {
} }
function expand() { function expand() {
mainRouter.push(path); mainRouter.push(path.value);
modal.close(); modal.value.close();
} }
function popout() { function popout() {
_popout(path, rootEl); _popout(path.value, rootEl.value);
modal.close(); modal.value.close();
} }
function onContextmenu(ev: MouseEvent) { function onContextmenu(ev: MouseEvent) {
os.contextMenu(contextmenu, ev); os.contextMenu(contextmenu.value, ev);
} }
</script> </script>

View File

@ -62,6 +62,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from "vue";
import { FocusTrap } from "focus-trap-vue"; import { FocusTrap } from "focus-trap-vue";
import MkModal from "./MkModal.vue"; import MkModal from "./MkModal.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -90,12 +92,12 @@ const emit = defineEmits<{
(event: "ok"): void; (event: "ok"): void;
}>(); }>();
let modal = $shallowRef<InstanceType<typeof MkModal>>(); let modal = shallowRef<InstanceType<typeof MkModal>>();
let rootEl = $shallowRef<HTMLElement>(); let rootEl = shallowRef<HTMLElement>();
let headerEl = $shallowRef<HTMLElement>(); let headerEl = shallowRef<HTMLElement>();
const close = (ev) => { const close = (ev) => {
modal?.close(ev); modal.value?.close(ev);
}; };
const onBgClick = () => { const onBgClick = () => {

View File

@ -297,7 +297,7 @@ const props = defineProps<{
const inChannel = inject("inChannel", null); const inChannel = inject("inChannel", null);
let note = $ref(deepClone(props.note)); let note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -312,19 +312,19 @@ const softMuteReasonI18nSrc = (what?: string) => {
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result = deepClone(note); let result = deepClone(note.value);
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result); result = await interruptor.handler(result);
} }
note = result; note.value = result;
}); });
} }
const isRenote = const isRenote =
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null; note.value.poll == null;
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const footerEl = ref<HTMLElement>(); const footerEl = ref<HTMLElement>();
@ -333,13 +333,15 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement>(); const renoteTime = ref<HTMLElement>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = $computed(() => let appearNote = computed(() =>
isRenote ? (note.renote as misskey.entities.Note) : note, isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
); );
const isMyRenote = $i && $i.id === note.userId; const isMyRenote = $i && $i.id === note.value.userId;
const showContent = ref(false); const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords)); const muted = ref(
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
);
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
const enableEmojiReactions = defaultStore.state.enableEmojiReactions; const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
@ -358,7 +360,7 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: appearNote,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
@ -366,7 +368,7 @@ function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post( os.post(
{ {
reply: appearNote, reply: appearNote.value,
animation: !viaKeyboard, animation: !viaKeyboard,
}, },
() => { () => {
@ -382,7 +384,7 @@ function react(viaKeyboard = false): void {
reactButton.value, reactButton.value,
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, reaction: reaction,
}); });
}, },
@ -425,21 +427,24 @@ function onContextmenu(ev: MouseEvent): void {
[ [
{ {
type: "label", type: "label",
text: notePage(appearNote), text: notePage(appearNote.value),
}, },
{ {
icon: "ph-browser ph-bold ph-lg", icon: "ph-browser ph-bold ph-lg",
text: i18n.ts.openInWindow, text: i18n.ts.openInWindow,
action: () => { action: () => {
os.pageWindow(notePage(appearNote)); os.pageWindow(notePage(appearNote.value));
}, },
}, },
notePage(appearNote) != location.pathname notePage(appearNote.value) != location.pathname
? { ? {
icon: "ph-arrows-out-simple ph-bold ph-lg", icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
action: () => { action: () => {
router.push(notePage(appearNote), "forcePage"); router.push(
notePage(appearNote.value),
"forcePage",
);
}, },
} }
: undefined, : undefined,
@ -448,22 +453,25 @@ function onContextmenu(ev: MouseEvent): void {
type: "a", type: "a",
icon: "ph-arrow-square-out ph-bold ph-lg", icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
href: notePage(appearNote), href: notePage(appearNote.value),
target: "_blank", target: "_blank",
}, },
{ {
icon: "ph-link-simple ph-bold ph-lg", icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink, text: i18n.ts.copyLink,
action: () => { action: () => {
copyToClipboard(`${url}${notePage(appearNote)}`); copyToClipboard(`${url}${notePage(appearNote.value)}`);
}, },
}, },
appearNote.user.host != null appearNote.value.user.host != null
? { ? {
type: "a", type: "a",
icon: "ph-arrow-square-up-right ph-bold ph-lg", icon: "ph-arrow-square-up-right ph-bold ph-lg",
text: i18n.ts.showOnRemote, text: i18n.ts.showOnRemote,
href: appearNote.url ?? appearNote.uri ?? "", href:
appearNote.value.url ??
appearNote.value.uri ??
"",
target: "_blank", target: "_blank",
} }
: undefined, : undefined,
@ -476,7 +484,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void { function menu(viaKeyboard = false): void {
os.popupMenu( os.popupMenu(
getNoteMenu({ getNoteMenu({
note: note, note: note.value,
translating, translating,
translation, translation,
menuButton, menuButton,
@ -500,7 +508,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true, danger: true,
action: () => { action: () => {
os.api("notes/delete", { os.api("notes/delete", {
noteId: note.id, noteId: note.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
}, },
@ -541,13 +549,13 @@ function noteClick(e) {
) { ) {
e.stopPropagation(); e.stopPropagation();
} else { } else {
router.push(notePage(appearNote)); router.push(notePage(appearNote.value));
} }
} }
function readPromo() { function readPromo() {
os.api("promo/read", { os.api("promo/read", {
noteId: appearNote.id, noteId: appearNote.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
} }
@ -559,28 +567,30 @@ function setPostExpanded(val: boolean) {
} }
const accessibleLabel = computed(() => { const accessibleLabel = computed(() => {
let label = `${appearNote.user.username}; `; let label = `${appearNote.value.user.username}; `;
if (appearNote.renote) { if (appearNote.value.renote) {
label += `${i18n.t("renoted")} ${appearNote.renote.user.username}; `; label += `${i18n.t("renoted")} ${
if (appearNote.renote.cw) { appearNote.value.renote.user.username
label += `${i18n.t("cw")}: ${appearNote.renote.cw}; `; }; `;
if (appearNote.value.renote.cw) {
label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
if (postIsExpanded.value) { if (postIsExpanded.value) {
label += `${appearNote.renote.text}; `; label += `${appearNote.value.renote.text}; `;
} }
} else { } else {
label += `${appearNote.renote.text}; `; label += `${appearNote.value.renote.text}; `;
} }
} else { } else {
if (appearNote.cw) { if (appearNote.value.cw) {
label += `${i18n.t("cw")}: ${appearNote.cw}; `; label += `${i18n.t("cw")}: ${appearNote.value.cw}; `;
if (postIsExpanded.value) { if (postIsExpanded.value) {
label += `${appearNote.text}; `; label += `${appearNote.value.text}; `;
} }
} else { } else {
label += `${appearNote.text}; `; label += `${appearNote.value.text}; `;
} }
} }
const date = new Date(appearNote.createdAt); const date = new Date(appearNote.value.createdAt);
label += `${date.toLocaleTimeString()}`; label += `${date.toLocaleTimeString()}`;
return label; return label;
}); });

View File

@ -177,9 +177,9 @@ const props = defineProps<{
pinned?: boolean; pinned?: boolean;
}>(); }>();
let tab = $ref("replies"); let tab = ref("replies");
let note = $ref(deepClone(props.note)); let note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -194,30 +194,32 @@ const softMuteReasonI18nSrc = (what?: string) => {
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result = deepClone(note); let result = deepClone(note.value);
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result); result = await interruptor.handler(result);
} }
note = result; note.value = result;
}); });
} }
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const noteEl = $ref(); const noteEl = ref();
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
const showContent = ref(false); const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords)); const muted = ref(
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
);
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
let conversation = $ref<null | misskey.entities.Note[]>([]); let conversation = ref<null | misskey.entities.Note[]>([]);
const replies = ref<misskey.entities.Note[]>([]); const replies = ref<misskey.entities.Note[]>([]);
let directReplies = $ref<null | misskey.entities.Note[]>([]); let directReplies = ref<null | misskey.entities.Note[]>([]);
let directQuotes = $ref<null | misskey.entities.Note[]>([]); let directQuotes = ref<null | misskey.entities.Note[]>([]);
let clips = $ref(); let clips = ref();
let renotes = $ref(); let renotes = ref();
let isScrolling; let isScrolling;
const reactionsCount = Object.values(props.note.reactions).reduce( const reactionsCount = Object.values(props.note.reactions).reduce(
@ -236,14 +238,14 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(note), note: note,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: note, reply: note.value,
animation: !viaKeyboard, animation: !viaKeyboard,
}).then(() => { }).then(() => {
focus(); focus();
@ -257,7 +259,7 @@ function react(viaKeyboard = false): void {
reactButton.value, reactButton.value,
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: note.id, noteId: note.value.id,
reaction: reaction, reaction: reaction,
}); });
}, },
@ -291,7 +293,7 @@ function onContextmenu(ev: MouseEvent): void {
} else { } else {
os.contextMenu( os.contextMenu(
getNoteMenu({ getNoteMenu({
note: note, note: note.value,
translating, translating,
translation, translation,
menuButton, menuButton,
@ -305,7 +307,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void { function menu(viaKeyboard = false): void {
os.popupMenu( os.popupMenu(
getNoteMenu({ getNoteMenu({
note: note, note: note.value,
translating, translating,
translation, translation,
menuButton, menuButton,
@ -319,48 +321,50 @@ function menu(viaKeyboard = false): void {
} }
function focus() { function focus() {
noteEl.focus(); noteEl.value.focus();
} }
function blur() { function blur() {
noteEl.blur(); noteEl.value.blur();
} }
directReplies = null; directReplies.value = null;
os.api("notes/children", { os.api("notes/children", {
noteId: note.id, noteId: note.value.id,
limit: 30, limit: 30,
depth: 12, depth: 12,
}).then((res) => { }).then((res) => {
res = res.reduce((acc, resNote) => { res = res.reduce((acc, resNote) => {
if (resNote.userId == note.userId) { if (resNote.userId == note.value.userId) {
return [...acc, resNote]; return [...acc, resNote];
} }
return [resNote, ...acc]; return [resNote, ...acc];
}, []); }, []);
replies.value = res; replies.value = res;
directReplies = res directReplies.value = res
.filter((resNote) => resNote.replyId === note.id) .filter((resNote) => resNote.replyId === note.value.id)
.reverse(); .reverse();
directQuotes = res.filter((resNote) => resNote.renoteId === note.id); directQuotes.value = res.filter(
(resNote) => resNote.renoteId === note.value.id,
);
}); });
conversation = null; conversation.value = null;
if (note.replyId) { if (note.value.replyId) {
os.api("notes/conversation", { os.api("notes/conversation", {
noteId: note.replyId, noteId: note.value.replyId,
limit: 30, limit: 30,
}).then((res) => { }).then((res) => {
conversation = res.reverse(); conversation.value = res.reverse();
focus(); focus();
}); });
} }
clips = null; clips.value = null;
os.api("notes/clips", { os.api("notes/clips", {
noteId: note.id, noteId: note.value.id,
}).then((res) => { }).then((res) => {
clips = res; clips.value = res;
}); });
// const pagination = { // const pagination = {
@ -371,14 +375,14 @@ os.api("notes/clips", {
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>(); // const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
renotes = null; renotes.value = null;
function loadTab() { function loadTab() {
if (tab === "renotes" && !renotes) { if (tab.value === "renotes" && !renotes.value) {
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: note.id, noteId: note.value.id,
limit: 100, limit: 100,
}).then((res) => { }).then((res) => {
renotes = res; renotes.value = res;
}); });
} }
} }
@ -387,7 +391,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
const { type, id, body } = noteData; const { type, id, body } = noteData;
let found = -1; let found = -1;
if (id === note.id) { if (id === note.value.id) {
found = 0; found = 0;
} else { } else {
for (let i = 0; i < replies.value.length; i++) { for (let i = 0; i < replies.value.length; i++) {
@ -412,7 +416,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
replies.value.splice(found, 0, replyNote); replies.value.splice(found, 0, replyNote);
if (found === 0) { if (found === 0) {
directReplies.push(replyNote); directReplies.value.push(replyNote);
} }
break; break;
@ -433,12 +437,12 @@ document.addEventListener("wheel", () => {
onMounted(() => { onMounted(() => {
stream.on("noteUpdated", onNoteUpdated); stream.on("noteUpdated", onNoteUpdated);
isScrolling = false; isScrolling = false;
noteEl.scrollIntoView(); noteEl.value.scrollIntoView();
}); });
onUpdated(() => { onUpdated(() => {
if (!isScrolling) { if (!isScrolling) {
noteEl.scrollIntoView(); noteEl.value.scrollIntoView();
if (location.hash) { if (location.hash) {
location.replace(location.hash); // Jump to highlighted reply location.replace(location.hash); // Jump to highlighted reply
} }

View File

@ -47,6 +47,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -61,11 +63,12 @@ const props = defineProps<{
pinned?: boolean; pinned?: boolean;
}>(); }>();
let note = $ref(props.note); let note = ref(props.note);
const showTicker = const showTicker =
defaultStore.state.instanceTicker === "always" || defaultStore.state.instanceTicker === "always" ||
(defaultStore.state.instanceTicker === "remote" && note.user.instance); (defaultStore.state.instanceTicker === "remote" &&
note.value.user.instance);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -177,7 +177,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, ref } from "vue"; import { inject, ref, computed } from "vue";
import type { Ref } from "vue"; import type { Ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import XNoteHeader from "@/components/MkNoteHeader.vue"; import XNoteHeader from "@/components/MkNoteHeader.vue";
@ -223,7 +223,7 @@ const props = withDefaults(
}, },
); );
let note = $ref(deepClone(props.note)); let note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -236,10 +236,10 @@ const softMuteReasonI18nSrc = (what?: string) => {
}; };
const isRenote = const isRenote =
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null; note.value.poll == null;
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const footerEl = ref<HTMLElement>(); const footerEl = ref<HTMLElement>();
@ -247,11 +247,13 @@ const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>(); const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = $computed(() => let appearNote = computed(() =>
isRenote ? (note.renote as misskey.entities.Note) : note, isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
); );
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords)); const muted = ref(
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
);
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
const replies: misskey.entities.Note[] = const replies: misskey.entities.Note[] =
@ -267,14 +269,14 @@ const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: appearNote,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: appearNote, reply: appearNote.value,
animation: !viaKeyboard, animation: !viaKeyboard,
}).then(() => { }).then(() => {
focus(); focus();
@ -288,7 +290,7 @@ function react(viaKeyboard = false): void {
reactButton.value, reactButton.value,
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction, reaction: reaction,
}); });
}, },
@ -314,7 +316,7 @@ const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
function menu(viaKeyboard = false): void { function menu(viaKeyboard = false): void {
os.popupMenu( os.popupMenu(
getNoteMenu({ getNoteMenu({
note: note, note: note.value,
translating, translating,
translation, translation,
menuButton, menuButton,
@ -346,21 +348,24 @@ function onContextmenu(ev: MouseEvent): void {
[ [
{ {
type: "label", type: "label",
text: notePage(appearNote), text: notePage(appearNote.value),
}, },
{ {
icon: "ph-browser ph-bold ph-lg", icon: "ph-browser ph-bold ph-lg",
text: i18n.ts.openInWindow, text: i18n.ts.openInWindow,
action: () => { action: () => {
os.pageWindow(notePage(appearNote)); os.pageWindow(notePage(appearNote.value));
}, },
}, },
notePage(appearNote) != location.pathname notePage(appearNote.value) != location.pathname
? { ? {
icon: "ph-arrows-out-simple ph-bold ph-lg", icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
action: () => { action: () => {
router.push(notePage(appearNote), "forcePage"); router.push(
notePage(appearNote.value),
"forcePage",
);
}, },
} }
: undefined, : undefined,
@ -369,22 +374,22 @@ function onContextmenu(ev: MouseEvent): void {
type: "a", type: "a",
icon: "ph-arrow-square-out ph-bold ph-lg", icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
href: notePage(appearNote), href: notePage(appearNote.value),
target: "_blank", target: "_blank",
}, },
{ {
icon: "ph-link-simple ph-bold ph-lg", icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink, text: i18n.ts.copyLink,
action: () => { action: () => {
copyToClipboard(`${url}${notePage(appearNote)}`); copyToClipboard(`${url}${notePage(appearNote.value)}`);
}, },
}, },
note.user.host != null note.value.user.host != null
? { ? {
type: "a", type: "a",
icon: "ph-arrow-square-up-right ph-bold ph-lg", icon: "ph-arrow-square-up-right ph-bold ph-lg",
text: i18n.ts.showOnRemote, text: i18n.ts.showOnRemote,
href: note.url ?? note.uri ?? "", href: note.value.url ?? note.value.uri ?? "",
target: "_blank", target: "_blank",
} }
: undefined, : undefined,

View File

@ -39,6 +39,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue";
import {} from "vue"; import {} from "vue";
import { notificationTypes } from "firefish-js"; import { notificationTypes } from "firefish-js";
import MkSwitch from "./form/switch.vue"; import MkSwitch from "./form/switch.vue";
@ -63,43 +65,45 @@ const props = withDefaults(
}, },
); );
let includingTypes = $computed(() => props.includingTypes || []); let includingTypes = computed(() => props.includingTypes || []);
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let typesMap = $ref<Record<(typeof notificationTypes)[number], boolean>>({}); let typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({});
let useGlobalSetting = $ref( let useGlobalSetting = ref(
(includingTypes === null || includingTypes.length === 0) && (includingTypes.value === null || includingTypes.value.length === 0) &&
props.showGlobalToggle, props.showGlobalToggle,
); );
for (const ntype of notificationTypes) { for (const ntype of notificationTypes) {
typesMap[ntype] = includingTypes.includes(ntype); typesMap.value[ntype] = includingTypes.value.includes(ntype);
} }
function ok() { function ok() {
if (useGlobalSetting) { if (useGlobalSetting.value) {
emit("done", { includingTypes: null }); emit("done", { includingTypes: null });
} else { } else {
emit("done", { emit("done", {
includingTypes: ( includingTypes: (
Object.keys(typesMap) as (typeof notificationTypes)[number][] Object.keys(
).filter((type) => typesMap[type]), typesMap.value,
) as (typeof notificationTypes)[number][]
).filter((type) => typesMap.value[type]),
}); });
} }
dialog.close(); dialog.value.close();
} }
function disableAll() { function disableAll() {
for (const type in typesMap) { for (const type in typesMap.value) {
typesMap[type as (typeof notificationTypes)[number]] = false; typesMap.value[type as (typeof notificationTypes)[number]] = false;
} }
} }
function enableAll() { function enableAll() {
for (const type in typesMap) { for (const type in typesMap.value) {
typesMap[type as (typeof notificationTypes)[number]] = true; typesMap.value[type as (typeof notificationTypes)[number]] = true;
} }
} }
</script> </script>

View File

@ -15,7 +15,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import XNotification from "@/components/MkNotification.vue"; import XNotification from "@/components/MkNotification.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -28,11 +28,11 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("high"); const zIndex = os.claimZIndex("high");
let showing = $ref(true); let showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
showing = false; showing.value = false;
}, 6000); }, 6000);
}); });
</script> </script>

View File

@ -30,7 +30,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, provide } from "vue"; import { ComputedRef, provide, ref, computed } from "vue";
import RouterView from "@/components/global/RouterView.vue"; import RouterView from "@/components/global/RouterView.vue";
import XWindow from "@/components/MkWindow.vue"; import XWindow from "@/components/MkWindow.vue";
import { popout as _popout } from "@/scripts/popout"; import { popout as _popout } from "@/scripts/popout";
@ -51,18 +51,18 @@ defineEmits<{
const router = new Router(routes, props.initialPath); const router = new Router(routes, props.initialPath);
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let windowEl = $ref<InstanceType<typeof XWindow>>(); let windowEl = ref<InstanceType<typeof XWindow>>();
const history = $ref<{ path: string; key: any }[]>([ const history = ref<{ path: string; key: any }[]>([
{ {
path: router.getCurrentPath(), path: router.getCurrentPath(),
key: router.getCurrentKey(), key: router.getCurrentKey(),
}, },
]); ]);
const buttonsLeft = $computed(() => { const buttonsLeft = computed(() => {
const buttons = []; const buttons = [];
if (history.length > 1) { if (history.value.length > 1) {
buttons.push({ buttons.push({
icon: "ph-caret-left ph-bold ph-lg", icon: "ph-caret-left ph-bold ph-lg",
onClick: back, onClick: back,
@ -71,7 +71,7 @@ const buttonsLeft = $computed(() => {
return buttons; return buttons;
}); });
const buttonsRight = $computed(() => { const buttonsRight = computed(() => {
const buttons = [ const buttons = [
{ {
icon: "ph-arrows-out-simple ph-bold ph-lg", icon: "ph-arrows-out-simple ph-bold ph-lg",
@ -84,18 +84,18 @@ const buttonsRight = $computed(() => {
}); });
router.addListener("push", (ctx) => { router.addListener("push", (ctx) => {
history.push({ path: ctx.path, key: ctx.key }); history.value.push({ path: ctx.path, key: ctx.key });
}); });
provide("router", router); provide("router", router);
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
pageMetadata = info; pageMetadata.value = info;
}); });
provide("shouldOmitHeaderTitle", true); provide("shouldOmitHeaderTitle", true);
provide("shouldBackButton", false); provide("shouldBackButton", false);
provide("shouldHeaderThin", true); provide("shouldHeaderThin", true);
const contextmenu = $computed(() => [ const contextmenu = computed(() => [
{ {
icon: "ph-arrows-out-simple ph-bold ph-lg", icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
@ -111,7 +111,7 @@ const contextmenu = $computed(() => [
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(url + router.getCurrentPath(), "_blank"); window.open(url + router.getCurrentPath(), "_blank");
windowEl.close(); windowEl.value.close();
}, },
}, },
{ {
@ -124,25 +124,25 @@ const contextmenu = $computed(() => [
]); ]);
function back() { function back() {
history.pop(); history.value.pop();
router.replace( router.replace(
history[history.length - 1].path, history.value[history.value.length - 1].path,
history[history.length - 1].key, history.value[history.value.length - 1].key,
); );
} }
function close() { function close() {
windowEl.close(); windowEl.value.close();
} }
function expand() { function expand() {
mainRouter.push(router.getCurrentPath(), "forcePage"); mainRouter.push(router.getCurrentPath(), "forcePage");
windowEl.close(); windowEl.value.close();
} }
function popout() { function popout() {
_popout(router.getCurrentPath(), windowEl.$el); _popout(router.getCurrentPath(), windowEl.value.$el);
windowEl.close(); windowEl.value.close();
} }
defineExpose({ defineExpose({

View File

@ -24,6 +24,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import MkModal from "./MkModal.vue"; import MkModal from "./MkModal.vue";
import MkMenu from "./MkMenu.vue"; import MkMenu from "./MkMenu.vue";
import { MenuItem } from "@/types/menu"; import { MenuItem } from "@/types/menu";
@ -41,7 +43,7 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let modal = $ref<InstanceType<typeof MkModal>>(); let modal = ref<InstanceType<typeof MkModal>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -233,7 +233,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from "vue"; import {
inject,
watch,
nextTick,
onMounted,
defineAsyncComponent,
ref,
computed,
} from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import autosize from "autosize"; import autosize from "autosize";
@ -302,45 +310,45 @@ const emit = defineEmits<{
(ev: "esc"): void; (ev: "esc"): void;
}>(); }>();
const textareaEl = $ref<HTMLTextAreaElement | null>(null); const textareaEl = ref<HTMLTextAreaElement | null>(null);
const cwInputEl = $ref<HTMLInputElement | null>(null); const cwInputEl = ref<HTMLInputElement | null>(null);
const hashtagsInputEl = $ref<HTMLInputElement | null>(null); const hashtagsInputEl = ref<HTMLInputElement | null>(null);
const visibilityButton = $ref<HTMLElement | null>(null); const visibilityButton = ref<HTMLElement | null>(null);
let posting = $ref(false); let posting = ref(false);
let text = $ref(props.initialText ?? ""); let text = ref(props.initialText ?? "");
let files = $ref(props.initialFiles ?? []); let files = ref(props.initialFiles ?? []);
let poll = $ref<{ let poll = ref<{
choices: string[]; choices: string[];
multiple: boolean; multiple: boolean;
expiresAt: string | null; expiresAt: string | null;
expiredAfter: string | null; expiredAfter: string | null;
} | null>(null); } | null>(null);
let useCw = $ref(false); let useCw = ref(false);
let showPreview = $ref(false); let showPreview = ref(false);
let cw = $ref<string | null>(null); let cw = ref<string | null>(null);
let localOnly = $ref<boolean>( let localOnly = ref<boolean>(
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
? defaultStore.state.localOnly ? defaultStore.state.localOnly
: defaultStore.state.defaultNoteLocalOnly, : defaultStore.state.defaultNoteLocalOnly,
); );
let visibility = $ref( let visibility = ref(
props.initialVisibility ?? props.initialVisibility ??
((defaultStore.state.rememberNoteVisibility ((defaultStore.state.rememberNoteVisibility
? defaultStore.state.visibility ? defaultStore.state.visibility
: defaultStore.state : defaultStore.state
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]), .defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
); );
let visibleUsers = $ref([]); let visibleUsers = ref([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
let autocomplete = $ref(null); let autocomplete = ref(null);
let draghover = $ref(false); let draghover = ref(false);
let quoteId = $ref(null); let quoteId = ref(null);
let hasNotSpecifiedMentions = $ref(false); let hasNotSpecifiedMentions = ref(false);
let recentHashtags = $ref(JSON.parse(localStorage.getItem("hashtags") || "[]")); let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
let imeText = $ref(""); let imeText = ref("");
const typing = throttle(3000, () => { const typing = throttle(3000, () => {
if (props.channel) { if (props.channel) {
@ -348,7 +356,7 @@ const typing = throttle(3000, () => {
} }
}); });
const draftKey = $computed((): string => { const draftKey = computed((): string => {
if (props.editId) { if (props.editId) {
return `edit:${props.editId}`; return `edit:${props.editId}`;
} }
@ -366,7 +374,7 @@ const draftKey = $computed((): string => {
return key; return key;
}); });
const placeholder = $computed((): string => { const placeholder = computed((): string => {
if (props.renote) { if (props.renote) {
return i18n.ts._postForm.quotePlaceholder; return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) { } else if (props.reply) {
@ -386,7 +394,7 @@ const placeholder = $computed((): string => {
} }
}); });
const submitText = $computed((): string => { const submitText = computed((): string => {
return props.editId return props.editId
? i18n.ts.edit ? i18n.ts.edit
: props.renote : props.renote
@ -396,34 +404,37 @@ const submitText = $computed((): string => {
: i18n.ts.note; : i18n.ts.note;
}); });
const textLength = $computed((): number => { const textLength = computed((): number => {
return length((preprocess(text) + imeText).trim()); return length((preprocess(text.value) + imeText.value).trim());
}); });
const maxTextLength = $computed((): number => { const maxTextLength = computed((): number => {
return instance ? instance.maxNoteTextLength : 3000; return instance ? instance.maxNoteTextLength : 3000;
}); });
const canPost = $computed((): boolean => { const canPost = computed((): boolean => {
return ( return (
!posting && !posting.value &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && (1 <= textLength.value ||
textLength <= maxTextLength && 1 <= files.value.length ||
(!poll || poll.choices.length >= 2) !!poll.value ||
!!props.renote) &&
textLength.value <= maxTextLength.value &&
(!poll.value || poll.value.choices.length >= 2)
); );
}); });
const withHashtags = $computed( const withHashtags = computed(
defaultStore.makeGetterSetter("postFormWithHashtags"), defaultStore.makeGetterSetter("postFormWithHashtags"),
); );
const hashtags = $computed(defaultStore.makeGetterSetter("postFormHashtags")); const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
watch($$(text), () => { watch(text, () => {
checkMissingMention(); checkMissingMention();
}); });
watch( watch(
$$(visibleUsers), visibleUsers,
() => { () => {
checkMissingMention(); checkMissingMention();
}, },
@ -433,10 +444,10 @@ watch(
); );
if (props.mention) { if (props.mention) {
text = props.mention.host text.value = props.mention.host
? `@${props.mention.username}@${toASCII(props.mention.host)}` ? `@${props.mention.username}@${toASCII(props.mention.host)}`
: `@${props.mention.username}`; : `@${props.mention.username}`;
text += " "; text.value += " ";
} }
if ( if (
@ -444,7 +455,7 @@ if (
(props.reply.user.username !== $i.username || (props.reply.user.username !== $i.username ||
(props.reply.user.host != null && props.reply.user.host !== host)) (props.reply.user.host != null && props.reply.user.host !== host))
) { ) {
text = `@${props.reply.user.username}${ text.value = `@${props.reply.user.username}${
props.reply.user.host != null props.reply.user.host != null
? "@" + toASCII(props.reply.user.host) ? "@" + toASCII(props.reply.user.host)
: "" : ""
@ -467,15 +478,15 @@ if (props.reply && props.reply.text != null) {
continue; continue;
// //
if (text.includes(`${mention} `)) continue; if (text.value.includes(`${mention} `)) continue;
text += `${mention} `; text.value += `${mention} `;
} }
} }
if (props.channel) { if (props.channel) {
visibility = "public"; visibility.value = "public";
localOnly = true; // TODO: localOnly.value = true; // TODO:
} }
// //
@ -483,17 +494,17 @@ if (
props.reply && props.reply &&
["home", "followers", "specified"].includes(props.reply.visibility) ["home", "followers", "specified"].includes(props.reply.visibility)
) { ) {
if (props.reply.visibility === "home" && visibility === "followers") { if (props.reply.visibility === "home" && visibility.value === "followers") {
visibility = "followers"; visibility.value = "followers";
} else if ( } else if (
["home", "followers"].includes(props.reply.visibility) && ["home", "followers"].includes(props.reply.visibility) &&
visibility === "specified" visibility.value === "specified"
) { ) {
visibility = "specified"; visibility.value = "specified";
} else { } else {
visibility = props.reply.visibility; visibility.value = props.reply.visibility;
} }
if (visibility === "specified") { if (visibility.value === "specified") {
if (props.reply.visibleUserIds) { if (props.reply.visibleUserIds) {
os.api("users/show", { os.api("users/show", {
userIds: props.reply.visibleUserIds.filter( userIds: props.reply.visibleUserIds.filter(
@ -515,7 +526,7 @@ if (
} }
if (props.specified) { if (props.specified) {
visibility = "specified"; visibility.value = "specified";
pushVisibleUser(props.specified); pushVisibleUser(props.specified);
} }
@ -531,53 +542,53 @@ const addRe = (s: string) => {
// keep cw when reply // keep cw when reply
if (defaultStore.state.keepCw && props.reply && props.reply.cw) { if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw = true; useCw.value = true;
cw = cw.value =
props.reply.user.username === $i.username props.reply.user.username === $i.username
? props.reply.cw ? props.reply.cw
: addRe(props.reply.cw); : addRe(props.reply.cw);
} }
function watchForDraft() { function watchForDraft() {
watch($$(text), () => saveDraft()); watch(text, () => saveDraft());
watch($$(useCw), () => saveDraft()); watch(useCw, () => saveDraft());
watch($$(cw), () => saveDraft()); watch(cw, () => saveDraft());
watch($$(poll), () => saveDraft()); watch(poll, () => saveDraft());
watch($$(files), () => saveDraft(), { deep: true }); watch(files, () => saveDraft(), { deep: true });
watch($$(visibility), () => saveDraft()); watch(visibility, () => saveDraft());
watch($$(localOnly), () => saveDraft()); watch(localOnly, () => saveDraft());
} }
function checkMissingMention() { function checkMissingMention() {
if (visibility === "specified") { if (visibility.value === "specified") {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if ( if (
!visibleUsers.some( !visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host, (u) => u.username === x.username && u.host === x.host,
) )
) { ) {
hasNotSpecifiedMentions = true; hasNotSpecifiedMentions.value = true;
return; return;
} }
} }
hasNotSpecifiedMentions = false; hasNotSpecifiedMentions.value = false;
} }
} }
function addMissingMention() { function addMissingMention() {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if ( if (
!visibleUsers.some( !visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host, (u) => u.username === x.username && u.host === x.host,
) )
) { ) {
os.api("users/show", { username: x.username, host: x.host }).then( os.api("users/show", { username: x.username, host: x.host }).then(
(user) => { (user) => {
visibleUsers.push(user); visibleUsers.value.push(user);
}, },
); );
} }
@ -585,10 +596,10 @@ function addMissingMention() {
} }
function togglePoll() { function togglePoll() {
if (poll) { if (poll.value) {
poll = null; poll.value = null;
} else { } else {
poll = { poll.value = {
choices: ["", ""], choices: ["", ""],
multiple: false, multiple: false,
expiresAt: null, expiresAt: null,
@ -598,15 +609,15 @@ function togglePoll() {
} }
function addTag(tag: string) { function addTag(tag: string) {
insertTextAtCursor(textareaEl, ` #${tag} `); insertTextAtCursor(textareaEl.value, ` #${tag} `);
} }
function focus() { function focus() {
if (textareaEl) { if (textareaEl.value) {
textareaEl.focus(); textareaEl.value.focus();
textareaEl.setSelectionRange( textareaEl.value.setSelectionRange(
textareaEl.value.length, textareaEl.value.value.length,
textareaEl.value.length, textareaEl.value.value.length,
); );
} }
} }
@ -615,31 +626,32 @@ function chooseFileFrom(ev) {
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then( selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
(files_) => { (files_) => {
for (const file of files_) { for (const file of files_) {
files.push(file); files.value.push(file);
} }
}, },
); );
} }
function detachFile(id) { function detachFile(id) {
files = files.filter((x) => x.id !== id); files.value = files.value.filter((x) => x.id !== id);
} }
function updateFiles(_files) { function updateFiles(_files) {
files = _files; files.value = _files;
} }
function updateFileSensitive(file, sensitive) { function updateFileSensitive(file, sensitive) {
files[files.findIndex((x) => x.id === file.id)].isSensitive = sensitive; files.value[files.value.findIndex((x) => x.id === file.id)].isSensitive =
sensitive;
} }
function updateFileName(file, name) { function updateFileName(file, name) {
files[files.findIndex((x) => x.id === file.id)].name = name; files.value[files.value.findIndex((x) => x.id === file.id)].name = name;
} }
function upload(file: File, name?: string) { function upload(file: File, name?: string) {
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => { uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
files.push(res); files.value.push(res);
}); });
} }
@ -654,21 +666,21 @@ function setVisibility() {
() => import("@/components/MkVisibilityPicker.vue"), () => import("@/components/MkVisibilityPicker.vue"),
), ),
{ {
currentVisibility: visibility, currentVisibility: visibility.value,
currentLocalOnly: localOnly, currentLocalOnly: localOnly.value,
src: visibilityButton, src: visibilityButton.value,
}, },
{ {
changeVisibility: (v) => { changeVisibility: (v) => {
visibility = v; visibility.value = v;
if (defaultStore.state.rememberNoteVisibility) { if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set("visibility", visibility); defaultStore.set("visibility", visibility.value);
} }
}, },
changeLocalOnly: (v) => { changeLocalOnly: (v) => {
localOnly = v; localOnly.value = v;
if (defaultStore.state.rememberNoteVisibility) { if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set("localOnly", localOnly); defaultStore.set("localOnly", localOnly.value);
} }
}, },
}, },
@ -678,11 +690,11 @@ function setVisibility() {
function pushVisibleUser(user) { function pushVisibleUser(user) {
if ( if (
!visibleUsers.some( !visibleUsers.value.some(
(u) => u.username === user.username && u.host === user.host, (u) => u.username === user.username && u.host === user.host,
) )
) { ) {
visibleUsers.push(user); visibleUsers.value.push(user);
} }
} }
@ -693,21 +705,21 @@ function addVisibleUser() {
} }
function removeVisibleUser(user) { function removeVisibleUser(user) {
visibleUsers = erase(user, visibleUsers); visibleUsers.value = erase(user, visibleUsers.value);
} }
function clear() { function clear() {
text = ""; text.value = "";
files = []; files.value = [];
poll = null; poll.value = null;
quoteId = null; quoteId.value = null;
} }
function onKeydown(ev: KeyboardEvent) { function onKeydown(ev: KeyboardEvent) {
if ( if (
(ev.which === 10 || ev.which === 13) && (ev.which === 10 || ev.which === 13) &&
(ev.ctrlKey || ev.metaKey) && (ev.ctrlKey || ev.metaKey) &&
canPost canPost.value
) )
post(); post();
if (ev.which === 27) emit("esc"); if (ev.which === 27) emit("esc");
@ -715,12 +727,12 @@ function onKeydown(ev: KeyboardEvent) {
} }
function onCompositionUpdate(ev: CompositionEvent) { function onCompositionUpdate(ev: CompositionEvent) {
imeText = ev.data; imeText.value = ev.data;
typing(); typing();
} }
function onCompositionEnd(ev: CompositionEvent) { function onCompositionEnd(ev: CompositionEvent) {
imeText = ""; imeText.value = "";
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
@ -741,7 +753,7 @@ async function onPaste(ev: ClipboardEvent) {
const paste = ev.clipboardData.getData("text"); const paste = ev.clipboardData.getData("text");
if (!props.renote && !quoteId && paste.startsWith(url + "/notes/")) { if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
ev.preventDefault(); ev.preventDefault();
os.yesno({ os.yesno({
@ -749,11 +761,13 @@ async function onPaste(ev: ClipboardEvent) {
text: i18n.ts.quoteQuestion, text: i18n.ts.quoteQuestion,
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) { if (canceled) {
insertTextAtCursor(textareaEl, paste); insertTextAtCursor(textareaEl.value, paste);
return; return;
} }
quoteId = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; quoteId.value = paste
.substr(url.length)
.match(/^\/notes\/(.+?)\/?$/)[1];
}); });
} }
} }
@ -764,7 +778,7 @@ function onDragover(ev) {
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) { if (isFile || isDriveFile) {
ev.preventDefault(); ev.preventDefault();
draghover = true; draghover.value = true;
switch (ev.dataTransfer.effectAllowed) { switch (ev.dataTransfer.effectAllowed) {
case "all": case "all":
case "uninitialized": case "uninitialized":
@ -785,15 +799,15 @@ function onDragover(ev) {
} }
function onDragenter(ev) { function onDragenter(ev) {
draghover = true; draghover.value = true;
} }
function onDragleave(ev) { function onDragleave(ev) {
draghover = false; draghover.value = false;
} }
function onDrop(ev): void { function onDrop(ev): void {
draghover = false; draghover.value = false;
// //
if (ev.dataTransfer.files.length > 0) { if (ev.dataTransfer.files.length > 0) {
@ -806,7 +820,7 @@ function onDrop(ev): void {
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== "") { if (driveFile != null && driveFile !== "") {
const file = JSON.parse(driveFile); const file = JSON.parse(driveFile);
files.push(file); files.value.push(file);
ev.preventDefault(); ev.preventDefault();
} }
//#endregion //#endregion
@ -815,16 +829,16 @@ function onDrop(ev): void {
function saveDraft() { function saveDraft() {
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}"); const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
draftData[draftKey] = { draftData[draftKey.value] = {
updatedAt: new Date(), updatedAt: new Date(),
data: { data: {
text: text, text: text.value,
useCw: useCw, useCw: useCw.value,
cw: cw, cw: cw.value,
visibility: visibility, visibility: visibility.value,
localOnly: localOnly, localOnly: localOnly.value,
files: files, files: files.value,
poll: poll, poll: poll.value,
}, },
}; };
@ -834,37 +848,38 @@ function saveDraft() {
function deleteDraft() { function deleteDraft() {
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}"); const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
delete draftData[draftKey]; delete draftData[draftKey.value];
localStorage.setItem("drafts", JSON.stringify(draftData)); localStorage.setItem("drafts", JSON.stringify(draftData));
} }
async function post() { async function post() {
const processedText = preprocess(text); const processedText = preprocess(text.value);
let postData = { let postData = {
editId: props.editId ? props.editId : undefined, editId: props.editId ? props.editId : undefined,
text: processedText === "" ? undefined : processedText, text: processedText === "" ? undefined : processedText,
fileIds: files.length > 0 ? files.map((f) => f.id) : undefined, fileIds:
files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined, replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote renoteId: props.renote
? props.renote.id ? props.renote.id
: quoteId : quoteId.value
? quoteId ? quoteId.value
: undefined, : undefined,
channelId: props.channel ? props.channel.id : undefined, channelId: props.channel ? props.channel.id : undefined,
poll: poll, poll: poll.value,
cw: useCw ? cw || "" : undefined, cw: useCw.value ? cw.value || "" : undefined,
localOnly: localOnly, localOnly: localOnly.value,
visibility: visibility, visibility: visibility.value,
visibleUserIds: visibleUserIds:
visibility === "specified" visibility.value === "specified"
? visibleUsers.map((u) => u.id) ? visibleUsers.value.map((u) => u.id)
: undefined, : undefined,
}; };
if (withHashtags && hashtags && hashtags.trim() !== "") { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {
const hashtags_ = hashtags const hashtags_ = hashtags.value
.trim() .trim()
.split(" ") .split(" ")
.map((x) => (x.startsWith("#") ? x : "#" + x)) .map((x) => (x.startsWith("#") ? x : "#" + x))
@ -883,12 +898,13 @@ async function post() {
let token = undefined; let token = undefined;
if (postAccount) { if (postAccount.value) {
const storedAccounts = await getAccounts(); const storedAccounts = await getAccounts();
token = storedAccounts.find((x) => x.id === postAccount.id)?.token; token = storedAccounts.find((x) => x.id === postAccount.value.id)
?.token;
} }
posting = true; posting.value = true;
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token) os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
.then(() => { .then(() => {
clear(); clear();
@ -908,12 +924,12 @@ async function post() {
JSON.stringify(unique(hashtags_.concat(history))), JSON.stringify(unique(hashtags_.concat(history))),
); );
} }
posting = false; posting.value = false;
postAccount = null; postAccount.value = null;
}); });
}) })
.catch((err) => { .catch((err) => {
posting = false; posting.value = false;
os.alert({ os.alert({
type: "error", type: "error",
text: err.message + "\n" + (err as any).id, text: err.message + "\n" + (err as any).id,
@ -927,12 +943,12 @@ function cancel() {
function insertMention() { function insertMention() {
os.selectUser().then((user) => { os.selectUser().then((user) => {
insertTextAtCursor(textareaEl, "@" + Acct.toString(user) + " "); insertTextAtCursor(textareaEl.value, "@" + Acct.toString(user) + " ");
}); });
} }
async function insertEmoji(ev: MouseEvent) { async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
} }
async function openCheatSheet(ev: MouseEvent) { async function openCheatSheet(ev: MouseEvent) {
@ -946,11 +962,11 @@ function showActions(ev) {
action: () => { action: () => {
action.handler( action.handler(
{ {
text: text, text: text.value,
}, },
(key, value) => { (key, value) => {
if (key === "text") { if (key === "text") {
text = value; text.value = value;
} }
}, },
); );
@ -960,19 +976,19 @@ function showActions(ev) {
); );
} }
let postAccount = $ref<misskey.entities.UserDetailed | null>(null); let postAccount = ref<misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) { function openAccountMenu(ev: MouseEvent) {
openAccountMenu_( openAccountMenu_(
{ {
withExtraOperation: false, withExtraOperation: false,
includeCurrentAccount: true, includeCurrentAccount: true,
active: postAccount != null ? postAccount.id : $i.id, active: postAccount.value != null ? postAccount.value.id : $i.id,
onChoose: (account) => { onChoose: (account) => {
if (account.id === $i.id) { if (account.id === $i.id) {
postAccount = null; postAccount.value = null;
} else { } else {
postAccount = account; postAccount.value = account;
} }
}, },
}, },
@ -990,30 +1006,30 @@ onMounted(() => {
} }
// TODO: detach when unmount // TODO: detach when unmount
new Autocomplete(textareaEl, $$(text)); new Autocomplete(textareaEl.value, text);
new Autocomplete(cwInputEl, $$(cw)); new Autocomplete(cwInputEl.value, cw);
new Autocomplete(hashtagsInputEl, $$(hashtags)); new Autocomplete(hashtagsInputEl.value, hashtags);
autosize(textareaEl); autosize(textareaEl.value);
nextTick(() => { nextTick(() => {
autosize(textareaEl); autosize(textareaEl.value);
// 稿 // 稿
if (!props.instant && !props.mention && !props.specified) { if (!props.instant && !props.mention && !props.specified) {
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[ const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
draftKey draftKey.value
]; ];
if (draft) { if (draft) {
text = draft.data.text; text.value = draft.data.text;
useCw = draft.data.useCw; useCw.value = draft.data.useCw;
cw = draft.data.cw; cw.value = draft.data.cw;
visibility = draft.data.visibility; visibility.value = draft.data.visibility;
localOnly = draft.data.localOnly; localOnly.value = draft.data.localOnly;
files = (draft.data.files || []).filter( files.value = (draft.data.files || []).filter(
(draftFile) => draftFile, (draftFile) => draftFile,
); );
if (draft.data.poll) { if (draft.data.poll) {
poll = draft.data.poll; poll.value = draft.data.poll;
} }
} }
} }
@ -1021,21 +1037,21 @@ onMounted(() => {
// //
if (props.initialNote) { if (props.initialNote) {
const init = props.initialNote; const init = props.initialNote;
text = init.text ? init.text : ""; text.value = init.text ? init.text : "";
files = init.files; files.value = init.files;
cw = init.cw; cw.value = init.cw;
useCw = init.cw != null; useCw.value = init.cw != null;
if (init.poll) { if (init.poll) {
poll = { poll.value = {
choices: init.poll.choices.map((x) => x.text), choices: init.poll.choices.map((x) => x.text),
multiple: init.poll.multiple, multiple: init.poll.multiple,
expiresAt: init.poll.expiresAt, expiresAt: init.poll.expiresAt,
expiredAfter: init.poll.expiredAfter, expiredAfter: init.poll.expiredAfter,
}; };
} }
visibility = init.visibility; visibility.value = init.visibility;
localOnly = init.localOnly; localOnly.value = init.localOnly;
quoteId = init.renote ? init.renote.id : null; quoteId.value = init.renote ? init.renote.id : null;
} }
nextTick(() => watchForDraft()); nextTick(() => watchForDraft());

View File

@ -19,6 +19,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from "vue";
import {} from "vue"; import {} from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
@ -46,11 +48,11 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let modal = $shallowRef<InstanceType<typeof MkModal>>(); let modal = shallowRef<InstanceType<typeof MkModal>>();
let form = $shallowRef<InstanceType<typeof MkPostForm>>(); let form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() { function onPosted() {
modal.close({ modal.value.close({
useSendAnimation: true, useSendAnimation: true,
}); });
} }

View File

@ -53,6 +53,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { $i, getAccounts } from "@/account"; import { $i, getAccounts } from "@/account";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import { instance } from "@/instance"; import { instance } from "@/instance";
@ -74,12 +76,12 @@ defineProps<{
}>(); }>();
// ServiceWorker registration // ServiceWorker registration
let registration = $ref<ServiceWorkerRegistration | undefined>(); let registration = ref<ServiceWorkerRegistration | undefined>();
// If this browser supports push notification // If this browser supports push notification
let supported = $ref(false); let supported = ref(false);
// If this browser has already subscribed to push notification // If this browser has already subscribed to push notification
let pushSubscription = $ref<PushSubscription | null>(null); let pushSubscription = ref<PushSubscription | null>(null);
let pushRegistrationInServer = $ref< let pushRegistrationInServer = ref<
| { | {
state?: string; state?: string;
key?: string; key?: string;
@ -91,11 +93,12 @@ let pushRegistrationInServer = $ref<
>(); >();
function subscribe() { function subscribe() {
if (!registration || !supported || !instance.swPublickey) return; if (!registration.value || !supported.value || !instance.swPublickey)
return;
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
return promiseDialog( return promiseDialog(
registration.pushManager registration.value.pushManager
.subscribe({ .subscribe({
userVisibleOnly: true, userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array( applicationServerKey: urlBase64ToUint8Array(
@ -104,10 +107,10 @@ function subscribe() {
}) })
.then( .then(
async (subscription) => { async (subscription) => {
pushSubscription = subscription; pushSubscription.value = subscription;
// Register // Register
pushRegistrationInServer = await api("sw/register", { pushRegistrationInServer.value = await api("sw/register", {
endpoint: subscription.endpoint, endpoint: subscription.endpoint,
auth: encode(subscription.getKey("auth")), auth: encode(subscription.getKey("auth")),
publickey: encode(subscription.getKey("p256dh")), publickey: encode(subscription.getKey("p256dh")),
@ -136,12 +139,12 @@ function subscribe() {
} }
async function unsubscribe() { async function unsubscribe() {
if (!pushSubscription) return; if (!pushSubscription.value) return;
const endpoint = pushSubscription.endpoint; const endpoint = pushSubscription.value.endpoint;
const accounts = await getAccounts(); const accounts = await getAccounts();
pushRegistrationInServer = undefined; pushRegistrationInServer.value = undefined;
if ($i && accounts.length >= 2) { if ($i && accounts.length >= 2) {
apiWithDialog("sw/unregister", { apiWithDialog("sw/unregister", {
@ -149,11 +152,11 @@ async function unsubscribe() {
endpoint, endpoint,
}); });
} else { } else {
pushSubscription.unsubscribe(); pushSubscription.value.unsubscribe();
apiWithDialog("sw/unregister", { apiWithDialog("sw/unregister", {
endpoint, endpoint,
}); });
pushSubscription = null; pushSubscription.value = null;
} }
} }
@ -184,20 +187,21 @@ if (navigator.serviceWorker == null) {
// TODO: // TODO:
} else { } else {
navigator.serviceWorker.ready.then(async (swr) => { navigator.serviceWorker.ready.then(async (swr) => {
registration = swr; registration.value = swr;
pushSubscription = await registration.pushManager.getSubscription(); pushSubscription.value =
await registration.value.pushManager.getSubscription();
if (instance.swPublickey && "PushManager" in window && $i && $i.token) { if (instance.swPublickey && "PushManager" in window && $i && $i.token) {
supported = true; supported.value = true;
if (pushSubscription) { if (pushSubscription.value) {
const res = await api("sw/show-registration", { const res = await api("sw/show-registration", {
endpoint: pushSubscription.endpoint, endpoint: pushSubscription.value.endpoint,
}); });
if (res) { if (res) {
pushRegistrationInServer = res; pushRegistrationInServer.value = res;
} }
} }
} }
@ -205,6 +209,6 @@ if (navigator.serviceWorker == null) {
} }
defineExpose({ defineExpose({
pushRegistrationInServer: $$(pushRegistrationInServer), pushRegistrationInServer: pushRegistrationInServer,
}); });
</script> </script>

View File

@ -36,7 +36,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch } from "vue"; import { onMounted, watch, ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkReactionIcon from "@/components/MkReactionIcon.vue"; import MkReactionIcon from "@/components/MkReactionIcon.vue";
import MkUserCardMini from "@/components/MkUserCardMini.vue"; import MkUserCardMini from "@/components/MkUserCardMini.vue";
@ -46,28 +46,28 @@ const props = defineProps<{
noteId: misskey.entities.Note["id"]; noteId: misskey.entities.Note["id"];
}>(); }>();
let note = $ref<misskey.entities.Note>(); let note = ref<misskey.entities.Note>();
let tab = $ref<string>(); let tab = ref<string>();
let reactions = $ref<string[]>(); let reactions = ref<string[]>();
let users = $ref(); let users = ref();
watch($$(tab), async () => { watch(tab, async () => {
const res = await os.api("notes/reactions", { const res = await os.api("notes/reactions", {
noteId: props.noteId, noteId: props.noteId,
type: tab, type: tab.value,
limit: 30, limit: 30,
}); });
users = res.map((x) => x.user); users.value = res.map((x) => x.user);
}); });
onMounted(() => { onMounted(() => {
os.api("notes/show", { os.api("notes/show", {
noteId: props.noteId, noteId: props.noteId,
}).then((res) => { }).then((res) => {
reactions = Object.keys(res.reactions); reactions.value = Object.keys(res.reactions);
tab = reactions[0]; tab.value = reactions.value[0];
note = res; note.value = res;
}); });
}); });
</script> </script>

View File

@ -70,13 +70,13 @@ useTooltip(buttonRef, async (showing) => {
); );
}); });
let hasRenotedBefore = $ref(false); let hasRenotedBefore = ref(false);
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: props.note.id, noteId: props.note.id,
userId: $i.id, userId: $i.id,
limit: 1, limit: 1,
}).then((res) => { }).then((res) => {
hasRenotedBefore = res.length > 0; hasRenotedBefore.value = res.length > 0;
}); });
const renote = (viaKeyboard = false, ev?: MouseEvent) => { const renote = (viaKeyboard = false, ev?: MouseEvent) => {
@ -94,7 +94,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
renoteId: props.note.id, renoteId: props.note.id,
visibility: "public", visibility: "public",
}); });
hasRenotedBefore = true; hasRenotedBefore.value = true;
const el = const el =
ev && ev &&
((ev.currentTarget ?? ev.target) as ((ev.currentTarget ?? ev.target) as
@ -121,7 +121,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
renoteId: props.note.id, renoteId: props.note.id,
visibility: "home", visibility: "home",
}); });
hasRenotedBefore = true; hasRenotedBefore.value = true;
const el = const el =
ev && ev &&
((ev.currentTarget ?? ev.target) as ((ev.currentTarget ?? ev.target) as
@ -149,7 +149,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
visibility: "specified", visibility: "specified",
visibleUserIds: props.note.visibleUserIds, visibleUserIds: props.note.visibleUserIds,
}); });
hasRenotedBefore = true; hasRenotedBefore.value = true;
const el = const el =
ev && ev &&
((ev.currentTarget ?? ev.target) as ((ev.currentTarget ?? ev.target) as
@ -174,7 +174,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
renoteId: props.note.id, renoteId: props.note.id,
visibility: "followers", visibility: "followers",
}); });
hasRenotedBefore = true; hasRenotedBefore.value = true;
const el = const el =
ev && ev &&
((ev.currentTarget ?? ev.target) as ((ev.currentTarget ?? ev.target) as
@ -212,7 +212,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
localOnly: true, localOnly: true,
}, },
); );
hasRenotedBefore = true; hasRenotedBefore.value = true;
const el = const el =
ev && ev &&
((ev.currentTarget ?? ev.target) as ((ev.currentTarget ?? ev.target) as
@ -242,7 +242,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
}); });
} }
if (hasRenotedBefore) { if (hasRenotedBefore.value) {
buttonActions.push({ buttonActions.push({
text: i18n.ts.unrenote, text: i18n.ts.unrenote,
icon: "ph-trash ph-bold ph-lg", icon: "ph-trash ph-bold ph-lg",
@ -251,7 +251,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
os.api("notes/unrenote", { os.api("notes/unrenote", {
noteId: props.note.id, noteId: props.note.id,
}); });
hasRenotedBefore = false; hasRenotedBefore.value = false;
}, },
}); });
} }

View File

@ -160,7 +160,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from "vue"; import { defineAsyncComponent, ref, computed } from "vue";
import { toUnicode } from "punycode/"; import { toUnicode } from "punycode/";
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";
@ -172,19 +172,19 @@ import { login } from "@/account";
import { instance } from "@/instance"; import { instance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
let signing = $ref(false); let signing = ref(false);
let user = $ref(null); let user = ref(null);
let username = $ref(""); let username = ref("");
let password = $ref(""); let password = ref("");
let token = $ref(""); let token = ref("");
let host = $ref(toUnicode(configHost)); let host = ref(toUnicode(configHost));
let totpLogin = $ref(false); let totpLogin = ref(false);
let challengeData = $ref(null); let challengeData = ref(null);
let queryingKey = $ref(false); let queryingKey = ref(false);
let hCaptchaResponse = $ref(null); let hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); let reCaptchaResponse = ref(null);
const meta = $computed(() => instance); const meta = computed(() => instance);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "login", v: any): void; (ev: "login", v: any): void;
@ -210,13 +210,13 @@ const props = defineProps({
function onUsernameChange() { function onUsernameChange() {
os.api("users/show", { os.api("users/show", {
username: username, username: username.value,
}).then( }).then(
(userResponse) => { (userResponse) => {
user = userResponse; user.value = userResponse;
}, },
() => { () => {
user = null; user.value = null;
}, },
); );
} }
@ -228,38 +228,40 @@ function onLogin(res) {
} }
function queryKey() { function queryKey() {
queryingKey = true; queryingKey.value = true;
return navigator.credentials return navigator.credentials
.get({ .get({
publicKey: { publicKey: {
challenge: byteify(challengeData.challenge, "base64"), challenge: byteify(challengeData.value.challenge, "base64"),
allowCredentials: challengeData.securityKeys.map((key) => ({ allowCredentials: challengeData.value.securityKeys.map(
(key) => ({
id: byteify(key.id, "hex"), id: byteify(key.id, "hex"),
type: "public-key", type: "public-key",
transports: ["usb", "nfc", "ble", "internal"], transports: ["usb", "nfc", "ble", "internal"],
})), }),
),
timeout: 60 * 1000, timeout: 60 * 1000,
}, },
}) })
.catch(() => { .catch(() => {
queryingKey = false; queryingKey.value = false;
return Promise.reject(null); return Promise.reject(null);
}) })
.then((credential) => { .then((credential) => {
queryingKey = false; queryingKey.value = false;
signing = true; signing.value = true;
return os.api("signin", { return os.api("signin", {
username, username: username.value,
password, password: password.value,
signature: hexify(credential.response.signature), signature: hexify(credential.response.signature),
authenticatorData: hexify( authenticatorData: hexify(
credential.response.authenticatorData, credential.response.authenticatorData,
), ),
clientDataJSON: hexify(credential.response.clientDataJSON), clientDataJSON: hexify(credential.response.clientDataJSON),
credentialId: credential.id, credentialId: credential.id,
challengeId: challengeData.challengeId, challengeId: challengeData.value.challengeId,
"hcaptcha-response": hCaptchaResponse, "hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse, "g-recaptcha-response": reCaptchaResponse.value,
}); });
}) })
.then((res) => { .then((res) => {
@ -272,39 +274,42 @@ function queryKey() {
type: "error", type: "error",
text: i18n.ts.signinFailed, text: i18n.ts.signinFailed,
}); });
signing = false; signing.value = false;
}); });
} }
function onSubmit() { function onSubmit() {
signing = true; signing.value = true;
console.log("submit"); console.log("submit");
if (!totpLogin && user && user.twoFactorEnabled) { if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
if (window.PublicKeyCredential && user.securityKeys) { if (window.PublicKeyCredential && user.value.securityKeys) {
os.api("signin", { os.api("signin", {
username, username: username.value,
password, password: password.value,
"hcaptcha-response": hCaptchaResponse, "hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse, "g-recaptcha-response": reCaptchaResponse.value,
}) })
.then((res) => { .then((res) => {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
challengeData = res; challengeData.value = res;
return queryKey(); return queryKey();
}) })
.catch(loginFailed); .catch(loginFailed);
} else { } else {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
} }
} else { } else {
os.api("signin", { os.api("signin", {
username, username: username.value,
password, password: password.value,
"hcaptcha-response": hCaptchaResponse, "hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse, "g-recaptcha-response": reCaptchaResponse.value,
token: user && user.twoFactorEnabled ? token : undefined, token:
user.value && user.value.twoFactorEnabled
? token.value
: undefined,
}) })
.then((res) => { .then((res) => {
emit("login", res); emit("login", res);
@ -354,9 +359,9 @@ function loginFailed(err) {
} }
} }
challengeData = null; challengeData.value = null;
totpLogin = false; totpLogin.value = false;
signing = false; signing.value = false;
} }
function resetPassword() { function resetPassword() {

View File

@ -12,6 +12,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import MkSignin from "@/components/MkSignin.vue"; import MkSignin from "@/components/MkSignin.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
@ -34,15 +36,15 @@ const emit = defineEmits<{
(ev: "cancelled"): void; (ev: "cancelled"): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
function onClose() { function onClose() {
emit("cancelled"); emit("cancelled");
dialog.close(); dialog.value.close();
} }
function onLogin(res) { function onLogin(res) {
emit("done", res); emit("done", res);
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -284,6 +284,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import getPasswordStrength from "syuilo-password-strength"; import getPasswordStrength from "syuilo-password-strength";
import { toUnicode } from "punycode/"; import { toUnicode } from "punycode/";
import MkButton from "./MkButton.vue"; import MkButton from "./MkButton.vue";
@ -312,14 +314,14 @@ const emit = defineEmits<{
const host = toUnicode(config.host); const host = toUnicode(config.host);
let hcaptcha = $ref(); let hcaptcha = ref();
let recaptcha = $ref(); let recaptcha = ref();
let username: string = $ref(""); let username: string = ref("");
let password: string = $ref(""); let password: string = ref("");
let retypedPassword: string = $ref(""); let retypedPassword: string = ref("");
let invitationCode: string = $ref(""); let invitationCode: string = ref("");
let email = $ref(""); let email = ref("");
let usernameState: let usernameState:
| null | null
| "wait" | "wait"
@ -328,8 +330,8 @@ let usernameState:
| "error" | "error"
| "invalid-format" | "invalid-format"
| "min-range" | "min-range"
| "max-range" = $ref(null); | "max-range" = ref(null);
let invitationState: null | "entered" = $ref(null); let invitationState: null | "entered" = ref(null);
let emailState: let emailState:
| null | null
| "wait" | "wait"
@ -340,79 +342,79 @@ let emailState:
| "unavailable:mx" | "unavailable:mx"
| "unavailable:smtp" | "unavailable:smtp"
| "unavailable" | "unavailable"
| "error" = $ref(null); | "error" = ref(null);
let passwordStrength: "" | "low" | "medium" | "high" = $ref(""); let passwordStrength: "" | "low" | "medium" | "high" = ref("");
let passwordRetypeState: null | "match" | "not-match" = $ref(null); let passwordRetypeState: null | "match" | "not-match" = ref(null);
let submitting: boolean = $ref(false); let submitting: boolean = ref(false);
let ToSAgreement: boolean = $ref(false); let ToSAgreement: boolean = ref(false);
let hCaptchaResponse = $ref(null); let hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); let reCaptchaResponse = ref(null);
const shouldDisableSubmitting = $computed((): boolean => { const shouldDisableSubmitting = computed((): boolean => {
return ( return (
submitting || submitting.value ||
(instance.tosUrl && !ToSAgreement) || (instance.tosUrl && !ToSAgreement.value) ||
(instance.enableHcaptcha && !hCaptchaResponse) || (instance.enableHcaptcha && !hCaptchaResponse.value) ||
(instance.enableRecaptcha && !reCaptchaResponse) || (instance.enableRecaptcha && !reCaptchaResponse.value) ||
passwordRetypeState === "not-match" passwordRetypeState.value === "not-match"
); );
}); });
function onChangeInvitationCode(): void { function onChangeInvitationCode(): void {
if (invitationCode === "") { if (invitationCode.value === "") {
invitationState = null; invitationState.value = null;
return; return;
} }
invitationState = "entered"; invitationState.value = "entered";
} }
function onChangeUsername(): void { function onChangeUsername(): void {
if (username === "") { if (username.value === "") {
usernameState = null; usernameState.value = null;
return; return;
} }
{ {
const err = !username.match(/^[a-zA-Z0-9_]+$/) const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
? "invalid-format" ? "invalid-format"
: username.length < 1 : username.value.length < 1
? "min-range" ? "min-range"
: username.length > 20 : username.value.length > 20
? "max-range" ? "max-range"
: null; : null;
if (err) { if (err) {
usernameState = err; usernameState.value = err;
return; return;
} }
} }
usernameState = "wait"; usernameState.value = "wait";
os.api("username/available", { os.api("username/available", {
username, username: username.value,
}) })
.then((result) => { .then((result) => {
usernameState = result.available ? "ok" : "unavailable"; usernameState.value = result.available ? "ok" : "unavailable";
}) })
.catch(() => { .catch(() => {
usernameState = "error"; usernameState.value = "error";
}); });
} }
function onChangeEmail(): void { function onChangeEmail(): void {
if (email === "") { if (email.value === "") {
emailState = null; emailState.value = null;
return; return;
} }
emailState = "wait"; emailState.value = "wait";
os.api("email-address/available", { os.api("email-address/available", {
emailAddress: email, emailAddress: email.value,
}) })
.then((result) => { .then((result) => {
emailState = result.available emailState.value = result.available
? "ok" ? "ok"
: result.reason === "used" : result.reason === "used"
? "unavailable:used" ? "unavailable:used"
@ -427,54 +429,55 @@ function onChangeEmail(): void {
: "unavailable"; : "unavailable";
}) })
.catch(() => { .catch(() => {
emailState = "error"; emailState.value = "error";
}); });
} }
function onChangePassword(): void { function onChangePassword(): void {
if (password === "") { if (password.value === "") {
passwordStrength = ""; passwordStrength.value = "";
return; return;
} }
const strength = getPasswordStrength(password); const strength = getPasswordStrength(password.value);
passwordStrength = passwordStrength.value =
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low"; strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
} }
function onChangePasswordRetype(): void { function onChangePasswordRetype(): void {
if (retypedPassword === "") { if (retypedPassword.value === "") {
passwordRetypeState = null; passwordRetypeState.value = null;
return; return;
} }
passwordRetypeState = password === retypedPassword ? "match" : "not-match"; passwordRetypeState.value =
password.value === retypedPassword.value ? "match" : "not-match";
} }
function onSubmit(): void { function onSubmit(): void {
if (submitting) return; if (submitting.value) return;
submitting = true; submitting.value = true;
os.api("signup", { os.api("signup", {
username, username: username.value,
password, password: password.value,
emailAddress: email, emailAddress: email.value,
invitationCode, invitationCode: invitationCode.value,
"hcaptcha-response": hCaptchaResponse, "hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse, "g-recaptcha-response": reCaptchaResponse.value,
}) })
.then(() => { .then(() => {
if (instance.emailRequiredForSignup) { if (instance.emailRequiredForSignup) {
os.alert({ os.alert({
type: "success", type: "success",
title: i18n.ts._signup.almostThere, title: i18n.ts._signup.almostThere,
text: i18n.t("_signup.emailSent", { email }), text: i18n.t("_signup.emailSent", { email: email.value }),
}); });
emit("signupEmailPending"); emit("signupEmailPending");
} else { } else {
os.api("signin", { os.api("signin", {
username, username: username.value,
password, password: password.value,
}).then((res) => { }).then((res) => {
emit("signup", res); emit("signup", res);
@ -485,9 +488,9 @@ function onSubmit(): void {
} }
}) })
.catch(() => { .catch(() => {
submitting = false; submitting.value = false;
hcaptcha.reset?.(); hcaptcha.value.reset?.();
recaptcha.reset?.(); recaptcha.value.reset?.();
os.alert({ os.alert({
type: "error", type: "error",

View File

@ -20,6 +20,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import XSignup from "@/components/MkSignup.vue"; import XSignup from "@/components/MkSignup.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -38,14 +40,14 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
function onSignup(res) { function onSignup(res) {
emit("done", res); emit("done", res);
dialog?.close(); dialog.value?.close();
} }
function onSignupEmailPending() { function onSignupEmailPending() {
dialog?.close(); dialog.value?.close();
} }
</script> </script>

View File

@ -217,23 +217,23 @@ const isLong =
(props.note.text.split("\n").length > 10 || (props.note.text.split("\n").length > 10 ||
props.note.text.length > 800)) || props.note.text.length > 800)) ||
props.note.files.length > 4); props.note.files.length > 4);
const collapsed = $ref(props.note.cw == null && isLong); const collapsed = ref(props.note.cw == null && isLong);
const urls = props.note.text const urls = props.note.text
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5) ? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
: null; : null;
let showContent = $ref(false); let showContent = ref(false);
const mfms = props.note.text const mfms = props.note.text
? extractMfmWithAnimation(mfm.parse(props.note.text)) ? extractMfmWithAnimation(mfm.parse(props.note.text))
: null; : null;
const hasMfm = $ref(mfms && mfms.length > 0); const hasMfm = ref(mfms && mfms.length > 0);
let disableMfm = $ref(defaultStore.state.animatedMfm); let disableMfm = ref(defaultStore.state.animatedMfm);
async function toggleMfm() { async function toggleMfm() {
if (disableMfm) { if (disableMfm.value) {
if (!defaultStore.state.animatedMfmWarnShown) { if (!defaultStore.state.animatedMfmWarnShown) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: "warning", type: "warning",
@ -244,9 +244,9 @@ async function toggleMfm() {
defaultStore.set("animatedMfmWarnShown", true); defaultStore.set("animatedMfmWarnShown", true);
} }
disableMfm = false; disableMfm.value = false;
} else { } else {
disableMfm = true; disableMfm.value = true;
} }
} }

View File

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, onBeforeUnmount } from "vue"; import { onMounted, watch, onBeforeUnmount, ref } from "vue";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
const loaded = !!window.TagCanvas; const loaded = !!window.TagCanvas;
@ -39,13 +39,13 @@ const idForTags = Array.from(Array(16))
], ],
) )
.join(""); .join("");
let available = $ref(false); let available = ref(false);
let rootEl = $ref<HTMLElement | null>(null); let rootEl = ref<HTMLElement | null>(null);
let canvasEl = $ref<HTMLCanvasElement | null>(null); let canvasEl = ref<HTMLCanvasElement | null>(null);
let tagsEl = $ref<HTMLElement | null>(null); let tagsEl = ref<HTMLElement | null>(null);
let width = $ref(300); let width = ref(300);
watch($$(available), () => { watch(available, () => {
try { try {
window.TagCanvas.Start(idForCanvas, idForTags, { window.TagCanvas.Start(idForCanvas, idForTags, {
textColour: "#ffffff", textColour: "#ffffff",
@ -70,10 +70,10 @@ watch($$(available), () => {
}); });
onMounted(() => { onMounted(() => {
width = rootEl.offsetWidth; width.value = rootEl.value.offsetWidth;
if (loaded) { if (loaded) {
available = true; available.value = true;
} else { } else {
document.head document.head
.appendChild( .appendChild(
@ -82,7 +82,7 @@ onMounted(() => {
src: "/client-assets/tagcanvas.min.js", src: "/client-assets/tagcanvas.min.js",
}), }),
) )
.addEventListener("load", () => (available = true)); .addEventListener("load", () => (available.value = true));
} }
}); });

View File

@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, provide, onUnmounted } from "vue"; import { computed, provide, onUnmounted, ref } from "vue";
import XNotes from "@/components/MkNotes.vue"; import XNotes from "@/components/MkNotes.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
import { stream } from "@/stream"; import { stream } from "@/stream";
@ -45,7 +45,7 @@ const props = defineProps<{
sound?: boolean; sound?: boolean;
}>(); }>();
let queue = $ref(0); let queue = ref(0);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "note"): void; (ev: "note"): void;
@ -57,10 +57,10 @@ provide(
computed(() => props.src === "channel"), computed(() => props.src === "channel"),
); );
const tlComponent: InstanceType<typeof XNotes> = $ref(); const tlComponent: InstanceType<typeof XNotes> = ref();
const prepend = (note) => { const prepend = (note) => {
tlComponent.pagingComponent?.prepend(note); tlComponent.value.pagingComponent?.prepend(note);
emit("note"); emit("note");
@ -70,16 +70,16 @@ const prepend = (note) => {
}; };
const onUserAdded = () => { const onUserAdded = () => {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
}; };
const onUserRemoved = () => { const onUserRemoved = () => {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
}; };
const onChangeFollowing = () => { const onChangeFollowing = () => {
if (!tlComponent.pagingComponent?.backed) { if (!tlComponent.value.pagingComponent?.backed) {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
} }
}; };

View File

@ -18,7 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import * as os from "@/os"; import * as os from "@/os";
defineProps<{ defineProps<{
@ -30,11 +30,11 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("high"); const zIndex = os.claimZIndex("high");
let showing = $ref(true); let showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
showing = false; showing.value = false;
}, 4000); }, 4000);
}); });
</script> </script>

View File

@ -43,6 +43,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import { permissions as kinds } from "firefish-js"; import { permissions as kinds } from "firefish-js";
import MkInput from "./form/input.vue"; import MkInput from "./form/input.vue";
@ -72,37 +74,39 @@ const emit = defineEmits<{
(ev: "done", result: { name: string | null; permissions: string[] }): void; (ev: "done", result: { name: string | null; permissions: string[] }): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let name = $ref(props.initialName); let name = ref(props.initialName);
let permissions = $ref({}); let permissions = ref({});
if (props.initialPermissions) { if (props.initialPermissions) {
for (const kind of props.initialPermissions) { for (const kind of props.initialPermissions) {
permissions[kind] = true; permissions.value[kind] = true;
} }
} else { } else {
for (const kind of kinds) { for (const kind of kinds) {
permissions[kind] = false; permissions.value[kind] = false;
} }
} }
function ok(): void { function ok(): void {
emit("done", { emit("done", {
name: name, name: name.value,
permissions: Object.keys(permissions).filter((p) => permissions[p]), permissions: Object.keys(permissions.value).filter(
(p) => permissions.value[p],
),
}); });
dialog.close(); dialog.value.close();
} }
function disableAll(): void { function disableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = false; permissions.value[p] = false;
} }
} }
function enableAll(): void { function enableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = true; permissions.value[p] = true;
} }
} }
</script> </script>

View File

@ -206,7 +206,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import XSettings from "@/pages/settings/profile.vue"; import XSettings from "@/pages/settings/profile.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -250,7 +250,7 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
const tutorial = computed({ const tutorial = computed({
get() { get() {
@ -278,7 +278,7 @@ const reduceAnimation = computed(
function close(res) { function close(res) {
tutorial.value = -1; tutorial.value = -1;
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -33,7 +33,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from "vue"; import { shallowRef, ref } 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";
@ -43,18 +43,18 @@ import * as os from "@/os";
const modal = shallowRef<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);
os.api("release").then((res) => { os.api("release").then((res) => {
data = res; data.value = res;
newRelease = version === data?.version; newRelease.value = version === data.value?.version;
}); });
console.log(`Version: ${version}`); console.log(`Version: ${version}`);
console.log(`Data version: ${data.version}`); console.log(`Data version: ${data.value.version}`);
console.log(newRelease); console.log(newRelease.value);
console.log(data); console.log(data.value);
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View File

@ -99,7 +99,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted } from "vue"; import { onUnmounted, ref } from "vue";
import { url as local, lang } from "@/config"; import { url as local, lang } from "@/config";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -117,22 +117,22 @@ const props = withDefaults(
const self = props.url.startsWith(local); const self = props.url.startsWith(local);
const attr = self ? "to" : "href"; const attr = self ? "to" : "href";
const target = self ? null : "_blank"; const target = self ? null : "_blank";
let fetching = $ref(true); let fetching = ref(true);
let title = $ref<string | null>(null); let title = ref<string | null>(null);
let description = $ref<string | null>(null); let description = ref<string | null>(null);
let thumbnail = $ref<string | null>(null); let thumbnail = ref<string | null>(null);
let icon = $ref<string | null>(null); let icon = ref<string | null>(null);
let sitename = $ref<string | null>(null); let sitename = ref<string | null>(null);
let player = $ref({ let player = ref({
url: null, url: null,
width: null, width: null,
height: null, height: null,
}); });
let playerEnabled = $ref(false); let playerEnabled = ref(false);
let tweetId = $ref<string | null>(null); let tweetId = ref<string | null>(null);
let tweetExpanded = $ref(props.detail); let tweetExpanded = ref(props.detail);
const embedId = `embed${Math.random().toString().replace(/\D/, "")}`; const embedId = `embed${Math.random().toString().replace(/\D/, "")}`;
let tweetHeight = $ref(150); let tweetHeight = ref(150);
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (!["http:", "https:"].includes(requestUrl.protocol)) if (!["http:", "https:"].includes(requestUrl.protocol))
@ -143,7 +143,7 @@ if (
requestUrl.hostname === "mobile.twitter.com" requestUrl.hostname === "mobile.twitter.com"
) { ) {
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
if (m) tweetId = m[1]; if (m) tweetId.value = m[1];
} }
if ( if (
@ -162,13 +162,13 @@ fetch(
).then((res) => { ).then((res) => {
res.json().then((info) => { res.json().then((info) => {
if (info.url == null) return; if (info.url == null) return;
title = info.title; title.value = info.title;
description = info.description; description.value = info.description;
thumbnail = info.thumbnail; thumbnail.value = info.thumbnail;
icon = info.icon; icon.value = info.icon;
sitename = info.sitename; sitename.value = info.sitename;
fetching = false; fetching.value = false;
player = info.player; player.value = info.player;
}); });
}); });
@ -178,7 +178,7 @@ function adjustTweetHeight(message: any) {
if (embed?.method !== "twttr.private.resize") return; if (embed?.method !== "twttr.private.resize") return;
if (embed?.id !== embedId) return; if (embed?.id !== embedId) return;
const height = embed?.params[0]?.height; const height = embed?.params[0]?.height;
if (height) tweetHeight = height; if (height) tweetHeight.value = height;
} }
(window as any).addEventListener("message", adjustTweetHeight); (window as any).addEventListener("message", adjustTweetHeight);

View File

@ -13,7 +13,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import MkUrlPreview from "@/components/MkUrlPreview.vue"; import MkUrlPreview from "@/components/MkUrlPreview.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -28,8 +28,8 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("middle"); const zIndex = os.claimZIndex("middle");
let top = $ref(0); let top = ref(0);
let left = $ref(0); let left = ref(0);
onMounted(() => { onMounted(() => {
const rect = props.source.getBoundingClientRect(); const rect = props.source.getBoundingClientRect();
@ -38,8 +38,8 @@ onMounted(() => {
window.pageXOffset; window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -24,6 +24,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue"; import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -41,7 +43,7 @@ const props = withDefaults(
}, },
); );
let chartValues = $ref<number[] | null>(null); let chartValues = ref<number[] | null>(null);
if (props.withChart) { if (props.withChart) {
os.apiGet("charts/user/notes", { os.apiGet("charts/user/notes", {
@ -51,7 +53,7 @@ if (props.withChart) {
}).then((res) => { }).then((res) => {
// //
res.inc.splice(0, 1); res.inc.splice(0, 1);
chartValues = res.inc; chartValues.value = res.inc;
}); });
} }
</script> </script>

View File

@ -86,6 +86,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkFollowButton from "@/components/MkFollowButton.vue"; import MkFollowButton from "@/components/MkFollowButton.vue";
import XShowMoreButton from "@/components/MkShowMoreButton.vue"; import XShowMoreButton from "@/components/MkShowMoreButton.vue";
@ -98,13 +100,13 @@ const props = defineProps<{
detailed?: boolean; detailed?: boolean;
}>(); }>();
let isLong = $ref( let isLong = ref(
props.detailed && props.detailed &&
props.user.description && props.user.description &&
(props.user.description.split("\n").length > 9 || (props.user.description.split("\n").length > 9 ||
props.user.description.length > 400), props.user.description.length > 400),
); );
let collapsed = $ref(isLong); let collapsed = ref(isLong.value);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -3,6 +3,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -10,7 +12,7 @@ const props = defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
}>(); }>();
const text = $computed(() => { const text = computed(() => {
switch (props.user.onlineStatus) { switch (props.user.onlineStatus) {
case "online": case "online":
return i18n.ts.online; return i18n.ts.online;

View File

@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import MkUserInfo from "@/components/MkUserInfo.vue"; import MkUserInfo from "@/components/MkUserInfo.vue";
import * as Acct from "firefish-js/built/acct"; import * as Acct from "firefish-js/built/acct";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
@ -47,13 +47,13 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("middle"); const zIndex = os.claimZIndex("middle");
let user = $ref<misskey.entities.UserDetailed | null>(null); let user = ref<misskey.entities.UserDetailed | null>(null);
let top = $ref(0); let top = ref(0);
let left = $ref(0); let left = ref(0);
onMounted(() => { onMounted(() => {
if (typeof props.q === "object") { if (typeof props.q === "object") {
user = props.q; user.value = props.q;
} else { } else {
const query = props.q.startsWith("@") const query = props.q.startsWith("@")
? Acct.parse(props.q.slice(1)) ? Acct.parse(props.q.slice(1))
@ -61,7 +61,7 @@ onMounted(() => {
os.api("users/show", query).then((res) => { os.api("users/show", query).then((res) => {
if (!props.showing) return; if (!props.showing) return;
user = res; user.value = res;
}); });
} }
@ -70,8 +70,8 @@ onMounted(() => {
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset; rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -88,7 +88,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import FormSplit from "@/components/form/split.vue"; import FormSplit from "@/components/form/split.vue";
@ -103,50 +103,50 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let username = $ref(""); let username = ref("");
let host = $ref(""); let host = ref("");
let users: misskey.entities.UserDetailed[] = $ref([]); let users: misskey.entities.UserDetailed[] = ref([]);
let recentUsers: misskey.entities.UserDetailed[] = $ref([]); let recentUsers: misskey.entities.UserDetailed[] = ref([]);
let selected: misskey.entities.UserDetailed | null = $ref(null); let selected: misskey.entities.UserDetailed | null = ref(null);
let dialogEl = $ref(); let dialogEl = ref();
const search = () => { const search = () => {
if (username === "" && host === "") { if (username.value === "" && host.value === "") {
users = []; users.value = [];
return; return;
} }
os.api("users/search-by-username-and-host", { os.api("users/search-by-username-and-host", {
username: username, username: username.value,
host: host, host: host.value,
limit: 10, limit: 10,
detail: false, detail: false,
}).then((_users) => { }).then((_users) => {
users = _users; users.value = _users;
}); });
}; };
const ok = () => { const ok = () => {
if (selected == null) return; if (selected.value == null) return;
emit("ok", selected); emit("ok", selected.value);
dialogEl.close(); dialogEl.value.close();
// 使 // 使
let recents = defaultStore.state.recentlyUsedUsers; let recents = defaultStore.state.recentlyUsedUsers;
recents = recents.filter((x) => x !== selected.id); recents = recents.filter((x) => x !== selected.value.id);
recents.unshift(selected.id); recents.unshift(selected.value.id);
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16)); defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
}; };
const cancel = () => { const cancel = () => {
emit("cancel"); emit("cancel");
dialogEl.close(); dialogEl.value.close();
}; };
onMounted(() => { onMounted(() => {
os.api("users/show", { os.api("users/show", {
userIds: defaultStore.state.recentlyUsedUsers, userIds: defaultStore.state.recentlyUsedUsers,
}).then((users) => { }).then((users) => {
recentUsers = users; recentUsers.value = users;
}); });
}); });
</script> </script>

View File

@ -84,7 +84,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import FormSplit from "@/components/form/split.vue"; import FormSplit from "@/components/form/split.vue";
@ -99,49 +99,49 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let username = $ref(""); let username = ref("");
let users: misskey.entities.UserDetailed[] = $ref([]); let users: misskey.entities.UserDetailed[] = ref([]);
let recentUsers: misskey.entities.UserDetailed[] = $ref([]); let recentUsers: misskey.entities.UserDetailed[] = ref([]);
let selected: misskey.entities.UserDetailed | null = $ref(null); let selected: misskey.entities.UserDetailed | null = ref(null);
let dialogEl = $ref(); let dialogEl = ref();
const search = () => { const search = () => {
if (username === "") { if (username.value === "") {
users = []; users.value = [];
return; return;
} }
os.api("users/search", { os.api("users/search", {
query: username, query: username.value,
origin: "local", origin: "local",
limit: 10, limit: 10,
detail: false, detail: false,
}).then((_users) => { }).then((_users) => {
users = _users; users.value = _users;
}); });
}; };
const ok = () => { const ok = () => {
if (selected == null) return; if (selected.value == null) return;
emit("ok", selected); emit("ok", selected.value);
dialogEl.close(); dialogEl.value.close();
// 使 // 使
let recents = defaultStore.state.recentlyUsedUsers; let recents = defaultStore.state.recentlyUsedUsers;
recents = recents.filter((x) => x !== selected.id); recents = recents.filter((x) => x !== selected.value.id);
recents.unshift(selected.id); recents.unshift(selected.value.id);
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16)); defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
}; };
const cancel = () => { const cancel = () => {
emit("cancel"); emit("cancel");
dialogEl.close(); dialogEl.value.close();
}; };
onMounted(() => { onMounted(() => {
os.api("users/show", { os.api("users/show", {
userIds: defaultStore.state.recentlyUsedUsers, userIds: defaultStore.state.recentlyUsedUsers,
}).then((users) => { }).then((users) => {
recentUsers = users; recentUsers.value = users;
}); });
}); });
</script> </script>

View File

@ -25,6 +25,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import XDetails from "@/components/MkUsersTooltip.vue"; import XDetails from "@/components/MkUsersTooltip.vue";
import * as os from "@/os"; import * as os from "@/os";
import { useTooltip } from "@/scripts/use-tooltip"; import { useTooltip } from "@/scripts/use-tooltip";
@ -38,10 +40,10 @@ const props = defineProps<{
}; };
}>(); }>();
const specified = $ref<HTMLElement>(); const specified = ref<HTMLElement>();
if (props.note.visibility === "specified") { if (props.note.visibility === "specified") {
useTooltip($$(specified), async (showing) => { useTooltip(specified, async (showing) => {
const users = await os.api("users/show", { const users = await os.api("users/show", {
userIds: props.note.visibleUserIds, userIds: props.note.visibleUserIds,
limit: 10, limit: 10,
@ -53,7 +55,7 @@ if (props.note.visibility === "specified") {
showing, showing,
users, users,
count: props.note.visibleUserIds.length, count: props.note.visibleUserIds.length,
targetElement: specified, targetElement: specified.value,
}, },
{}, {},
"closed", "closed",

View File

@ -122,12 +122,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, watch } from "vue"; import { nextTick, watch, shallowRef, ref } from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
const modal = $shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -147,18 +147,18 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let v = $ref(props.currentVisibility); let v = ref(props.currentVisibility);
let localOnly = $ref(props.currentLocalOnly); let localOnly = ref(props.currentLocalOnly);
watch($$(localOnly), () => { watch(localOnly, () => {
emit("changeLocalOnly", localOnly); emit("changeLocalOnly", localOnly.value);
}); });
function choose(visibility: (typeof misskey.noteVisibilities)[number]): void { function choose(visibility: (typeof misskey.noteVisibilities)[number]): void {
v = visibility; v.value = visibility;
emit("changeVisibility", visibility); emit("changeVisibility", visibility);
nextTick(() => { nextTick(() => {
modal.close(); modal.value.close();
}); });
} }
</script> </script>

View File

@ -115,7 +115,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide } from "vue"; import { onBeforeUnmount, onMounted, provide, ref } from "vue";
import contains from "@/scripts/contains"; import contains from "@/scripts/contains";
import * as os from "@/os"; import * as os from "@/os";
import { MenuItem } from "@/types/menu"; import { MenuItem } from "@/types/menu";
@ -170,17 +170,17 @@ const emit = defineEmits<{
provide("inWindow", true); provide("inWindow", true);
let rootEl = $ref<HTMLElement | null>(); let rootEl = ref<HTMLElement | null>();
let showing = $ref(true); let showing = ref(true);
let beforeClickedAt = 0; let beforeClickedAt = 0;
let maximized = $ref(false); let maximized = ref(false);
let unMaximizedTop = ""; let unMaximizedTop = "";
let unMaximizedLeft = ""; let unMaximizedLeft = "";
let unMaximizedWidth = ""; let unMaximizedWidth = "";
let unMaximizedHeight = ""; let unMaximizedHeight = "";
function close() { function close() {
showing = false; showing.value = false;
} }
function onKeydown(evt) { function onKeydown(evt) {
@ -200,29 +200,31 @@ function onContextmenu(ev: MouseEvent) {
// //
function top() { function top() {
if (rootEl) { if (rootEl.value) {
rootEl.style.zIndex = os.claimZIndex(props.front ? "middle" : "low"); rootEl.value.style.zIndex = os.claimZIndex(
props.front ? "middle" : "low",
);
} }
} }
function maximize() { function maximize() {
maximized = true; maximized.value = true;
unMaximizedTop = rootEl.style.top; unMaximizedTop = rootEl.value.style.top;
unMaximizedLeft = rootEl.style.left; unMaximizedLeft = rootEl.value.style.left;
unMaximizedWidth = rootEl.style.width; unMaximizedWidth = rootEl.value.style.width;
unMaximizedHeight = rootEl.style.height; unMaximizedHeight = rootEl.value.style.height;
rootEl.style.top = "0"; rootEl.value.style.top = "0";
rootEl.style.left = "0"; rootEl.value.style.left = "0";
rootEl.style.width = "100%"; rootEl.value.style.width = "100%";
rootEl.style.height = "100%"; rootEl.value.style.height = "100%";
} }
function unMaximize() { function unMaximize() {
maximized = false; maximized.value = false;
rootEl.style.top = unMaximizedTop; rootEl.value.style.top = unMaximizedTop;
rootEl.style.left = unMaximizedLeft; rootEl.value.style.left = unMaximizedLeft;
rootEl.style.width = unMaximizedWidth; rootEl.value.style.width = unMaximizedWidth;
rootEl.style.height = unMaximizedHeight; rootEl.value.style.height = unMaximizedHeight;
} }
function onBodyMousedown() { function onBodyMousedown() {
@ -239,7 +241,7 @@ function onHeaderMousedown(evt: MouseEvent) {
let beforeMaximized = false; let beforeMaximized = false;
if (maximized) { if (maximized.value) {
beforeMaximized = true; beforeMaximized = true;
unMaximize(); unMaximize();
} }
@ -253,7 +255,7 @@ function onHeaderMousedown(evt: MouseEvent) {
beforeClickedAt = Date.now(); beforeClickedAt = Date.now();
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
if (!contains(main, document.activeElement)) main.focus(); if (!contains(main, document.activeElement)) main.focus();
@ -295,8 +297,8 @@ function onHeaderMousedown(evt: MouseEvent) {
if (moveLeft + windowWidth > browserWidth) if (moveLeft + windowWidth > browserWidth)
moveLeft = browserWidth - windowWidth; moveLeft = browserWidth - windowWidth;
rootEl.style.left = moveLeft + "px"; rootEl.value.style.left = moveLeft + "px";
rootEl.style.top = moveTop + "px"; rootEl.value.style.top = moveTop + "px";
} }
if (beforeMaximized) { if (beforeMaximized) {
@ -320,7 +322,7 @@ function onHeaderMousedown(evt: MouseEvent) {
// //
function onTopHandleMousedown(evt) { function onTopHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientY; const base = evt.clientY;
const height = parseInt(getComputedStyle(main, "").height, 10); const height = parseInt(getComputedStyle(main, "").height, 10);
@ -348,7 +350,7 @@ function onTopHandleMousedown(evt) {
// //
function onRightHandleMousedown(evt) { function onRightHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientX; const base = evt.clientX;
const width = parseInt(getComputedStyle(main, "").width, 10); const width = parseInt(getComputedStyle(main, "").width, 10);
@ -374,7 +376,7 @@ function onRightHandleMousedown(evt) {
// //
function onBottomHandleMousedown(evt) { function onBottomHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientY; const base = evt.clientY;
const height = parseInt(getComputedStyle(main, "").height, 10); const height = parseInt(getComputedStyle(main, "").height, 10);
@ -400,7 +402,7 @@ function onBottomHandleMousedown(evt) {
// //
function onLeftHandleMousedown(evt) { function onLeftHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientX; const base = evt.clientX;
const width = parseInt(getComputedStyle(main, "").width, 10); const width = parseInt(getComputedStyle(main, "").width, 10);
@ -453,27 +455,27 @@ function onBottomLeftHandleMousedown(evt) {
// //
function applyTransformHeight(height) { function applyTransformHeight(height) {
if (height > window.innerHeight) height = window.innerHeight; if (height > window.innerHeight) height = window.innerHeight;
rootEl.style.height = height + "px"; rootEl.value.style.height = height + "px";
} }
// //
function applyTransformWidth(width) { function applyTransformWidth(width) {
if (width > window.innerWidth) width = window.innerWidth; if (width > window.innerWidth) width = window.innerWidth;
rootEl.style.width = width + "px"; rootEl.value.style.width = width + "px";
} }
// Y // Y
function applyTransformTop(top) { function applyTransformTop(top) {
rootEl.style.top = top + "px"; rootEl.value.style.top = top + "px";
} }
// X // X
function applyTransformLeft(left) { function applyTransformLeft(left) {
rootEl.style.left = left + "px"; rootEl.value.style.left = left + "px";
} }
function onBrowserResize() { function onBrowserResize() {
const main = rootEl; const main = rootEl.value;
const position = main.getBoundingClientRect(); const position = main.getBoundingClientRect();
const browserWidth = window.innerWidth; const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
@ -491,8 +493,8 @@ onMounted(() => {
if (props.initialWidth) applyTransformWidth(props.initialWidth); if (props.initialWidth) applyTransformWidth(props.initialWidth);
if (props.initialHeight) applyTransformHeight(props.initialHeight); if (props.initialHeight) applyTransformHeight(props.initialHeight);
applyTransformTop(window.innerHeight / 2 - rootEl.offsetHeight / 2); applyTransformTop(window.innerHeight / 2 - rootEl.value.offsetHeight / 2);
applyTransformLeft(window.innerWidth / 2 - rootEl.offsetWidth / 2); applyTransformLeft(window.innerWidth / 2 - rootEl.value.offsetWidth / 2);
// //
top(); top();

View File

@ -24,7 +24,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { toRefs, Ref } from "vue"; import { toRefs, Ref, ref } from "vue";
import * as os from "@/os"; import * as os from "@/os";
import Ripple from "@/components/MkRipple.vue"; import Ripple from "@/components/MkRipple.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -38,16 +38,16 @@ const emit = defineEmits<{
(ev: "update:modelValue", v: boolean): void; (ev: "update:modelValue", v: boolean): void;
}>(); }>();
let button = $ref<HTMLElement>(); let button = ref<HTMLElement>();
const checked = toRefs(props).modelValue; const checked = toRefs(props).modelValue;
const toggle = () => { const toggle = () => {
if (props.disabled) return; if (props.disabled) return;
emit("update:modelValue", !checked.value); emit("update:modelValue", !checked.value);
if (!checked.value) { if (!checked.value) {
const rect = button.getBoundingClientRect(); const rect = button.value.getBoundingClientRect();
const x = rect.left + button.offsetWidth / 2; const x = rect.left + button.value.offsetWidth / 2;
const y = rect.top + button.offsetHeight / 2; const y = rect.top + button.value.offsetHeight / 2;
os.popup(Ripple, { x, y, particle: false }, {}, "end"); os.popup(Ripple, { x, y, particle: false }, {}, "end");
} }
}; };

View File

@ -14,6 +14,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
const props = defineProps<{ const props = defineProps<{
modelValue: any; modelValue: any;
value: any; value: any;
@ -24,7 +26,7 @@ const emit = defineEmits<{
(ev: "update:modelValue", value: any): void; (ev: "update:modelValue", value: any): void;
}>(); }>();
const checked = $computed(() => props.modelValue === props.value); const checked = computed(() => props.modelValue === props.value);
function toggle(x) { function toggle(x) {
if (props.disabled) return; if (props.disabled) return;

View File

@ -59,7 +59,7 @@ const props = withDefaults(
); );
const inputEl = ref<HTMLElement>(); const inputEl = ref<HTMLElement>();
const inputVal = $ref(props.modelValue); const inputVal = ref(props.modelValue);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "update:modelValue", value: number): void; (ev: "update:modelValue", value: number): void;
@ -74,7 +74,7 @@ const steps = computed(() => {
}); });
function onChange(x) { function onChange(x) {
emit("update:modelValue", inputVal); emit("update:modelValue", inputVal.value);
} }
const tooltipShowing = ref(false); const tooltipShowing = ref(false);
@ -85,7 +85,7 @@ function tooltipShow() {
{ {
showing: tooltipShowing, showing: tooltipShowing,
text: computed(() => { text: computed(() => {
return props.textConverter(inputVal); return props.textConverter(inputVal.value);
}), }),
targetElement: inputEl, targetElement: inputEl,
}, },

View File

@ -156,7 +156,7 @@ export default defineComponent({
v.value = newValue; v.value = newValue;
}); });
watch($$(v), () => { watch(v, () => {
if (!props.manualSave) { if (!props.manualSave) {
if (props.debounce) { if (props.debounce) {
debouncedUpdated(); debouncedUpdated();

View File

@ -10,6 +10,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
import * as os from "@/os"; import * as os from "@/os";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
import { url } from "@/config"; import { url } from "@/config";
@ -31,7 +33,7 @@ const props = withDefaults(
const router = useRouter(); const router = useRouter();
const active = $computed(() => { const active = computed(() => {
if (props.activeClass == null) return false; if (props.activeClass == null) return false;
const resolved = router.resolve(props.to); const resolved = router.resolve(props.to);
if (resolved == null) return false; if (resolved == null) return false;

View File

@ -36,7 +36,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from "vue"; import { watch, computed, ref } from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url"; import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash"; import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
@ -64,7 +64,7 @@ const emit = defineEmits<{
(ev: "click", v: MouseEvent): void; (ev: "click", v: MouseEvent): void;
}>(); }>();
const url = $computed(() => const url = computed(() =>
defaultStore.state.disableShowingAnimatedImages defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(props.user.avatarUrl) ? getStaticImageUrl(props.user.avatarUrl)
: props.user.avatarUrl, : props.user.avatarUrl,
@ -74,12 +74,12 @@ function onClick(ev: MouseEvent) {
emit("click", ev); emit("click", ev);
} }
let color = $ref(); let color = ref();
watch( watch(
() => props.user.avatarBlurhash, () => props.user.avatarBlurhash,
() => { () => {
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash); color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
}, },
{ {
immediate: true, immediate: true,

View File

@ -121,7 +121,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, nextTick, onMounted, onUnmounted, ref, watch } from "vue"; import {
inject,
nextTick,
onMounted,
onUnmounted,
ref,
watch,
computed,
} from "vue";
import MkFollowButton from "@/components/MkFollowButton.vue"; import MkFollowButton from "@/components/MkFollowButton.vue";
import { popupMenu } from "@/os"; import { popupMenu } from "@/os";
import { scrollToTop } from "@/scripts/scroll"; import { scrollToTop } from "@/scripts/scroll";
@ -165,16 +173,16 @@ const metadata = injectPageMetadata();
const hideTitle = inject("shouldOmitHeaderTitle", false); const hideTitle = inject("shouldOmitHeaderTitle", false);
const thin_ = props.thin || inject("shouldHeaderThin", false); const thin_ = props.thin || inject("shouldHeaderThin", false);
const el = $ref<HTMLElement | null>(null); const el = ref<HTMLElement | null>(null);
const tabRefs = {}; const tabRefs = {};
const tabHighlightEl = $ref<HTMLElement | null>(null); const tabHighlightEl = ref<HTMLElement | null>(null);
const tabsEl = $ref<HTMLElement | null>(null); const tabsEl = ref<HTMLElement | null>(null);
const bg = ref(null); const bg = ref(null);
let narrow = $ref(false); let narrow = ref(false);
const hasTabs = $computed(() => props.tabs && props.tabs.length > 0); const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
const hasActions = $computed(() => props.actions && props.actions.length > 0); const hasActions = computed(() => props.actions && props.actions.length > 0);
const show = $computed(() => { const show = computed(() => {
return !hideTitle || hasTabs || hasActions; return !hideTitle || hasTabs.value || hasActions.value;
}); });
const openAccountMenu = (ev: MouseEvent) => { const openAccountMenu = (ev: MouseEvent) => {
@ -187,8 +195,8 @@ const openAccountMenu = (ev: MouseEvent) => {
}; };
const showTabsPopup = (ev: MouseEvent) => { const showTabsPopup = (ev: MouseEvent) => {
if (!hasTabs) return; if (!hasTabs.value) return;
if (!narrow) return; if (!narrow.value) return;
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
const menu = props.tabs.map((tab) => ({ const menu = props.tabs.map((tab) => ({
@ -210,7 +218,7 @@ const onClick = () => {
if (props.to) { if (props.to) {
location.href = props.to; location.href = props.to;
} else { } else {
scrollToTop(el, { behavior: "smooth" }); scrollToTop(el.value, { behavior: "smooth" });
} }
}; };
@ -244,7 +252,7 @@ onMounted(() => {
() => { () => {
nextTick(() => { nextTick(() => {
const tabEl = tabRefs[props.tab]; const tabEl = tabRefs[props.tab];
if (tabEl && tabHighlightEl) { if (tabEl && tabHighlightEl.value) {
// offsetWidth offsetLeft getBoundingClientRect 使 // offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding
@ -252,10 +260,10 @@ onMounted(() => {
tabEl.style = `--width: ${tabSizeX}px`; tabEl.style = `--width: ${tabSizeX}px`;
} }
setTimeout(() => { setTimeout(() => {
tabHighlightEl.style.width = tabSizeX + "px"; tabHighlightEl.value.style.width = tabSizeX + "px";
tabHighlightEl.style.transform = `translateX(${tabEl.offsetLeft}px)`; tabHighlightEl.value.style.transform = `translateX(${tabEl.offsetLeft}px)`;
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
tabsEl?.scrollTo({ tabsEl.value?.scrollTo({
left: tabEl.offsetLeft - 60, left: tabEl.offsetLeft - 60,
behavior: "smooth", behavior: "smooth",
}); });
@ -269,14 +277,14 @@ onMounted(() => {
}, },
); );
if (el && el.parentElement) { if (el.value && el.value.parentElement) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
ro = new ResizeObserver((entries, observer) => { ro = new ResizeObserver((entries, observer) => {
if (el.parentElement && document.body.contains(el)) { if (el.value.parentElement && document.body.contains(el.value)) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
} }
}); });
ro.observe(el.parentElement); ro.observe(el.value.parentElement);
} }
}); });

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onMounted, onUnmounted } from "vue"; import { inject, onMounted, onUnmounted, ref } from "vue";
import { deviceKind } from "@/scripts/device-kind"; import { deviceKind } from "@/scripts/device-kind";
import { ui } from "@/config"; import { ui } from "@/config";
@ -25,18 +25,18 @@ const props = withDefaults(
); );
let ro: ResizeObserver, let ro: ResizeObserver,
root = $ref<HTMLElement>(), root = ref<HTMLElement>(),
content = $ref<HTMLElement>(), content = ref<HTMLElement>(),
margin = $ref(0); margin = ref(0);
const shouldSpacerMin = inject("shouldSpacerMin", false); const shouldSpacerMin = inject("shouldSpacerMin", false);
const adjust = (rect: { width: number; height: number }) => { const adjust = (rect: { width: number; height: number }) => {
if (shouldSpacerMin || deviceKind === "smartphone") { if (shouldSpacerMin || deviceKind === "smartphone") {
margin = props.marginMin; margin.value = props.marginMin;
return; return;
} }
if (ui === "classic") { if (ui === "classic") {
margin = 12; margin.value = 12;
return; return;
} }
@ -44,9 +44,9 @@ const adjust = (rect: { width: number; height: number }) => {
rect.width > (props.contentMax ?? 0) || rect.width > (props.contentMax ?? 0) ||
(rect.width > 360 && window.innerWidth > 400) (rect.width > 360 && window.innerWidth > 400)
) { ) {
margin = props.marginMax; margin.value = props.marginMax;
} else { } else {
margin = props.marginMin; margin.value = props.marginMin;
} }
}; };
@ -59,14 +59,14 @@ onMounted(() => {
}); });
*/ */
adjust({ adjust({
width: root!.offsetWidth, width: root.value!.offsetWidth,
height: root!.offsetHeight, height: root.value!.offsetHeight,
}); });
}); });
ro.observe(root!); ro.observe(root.value!);
if (props.contentMax) { if (props.contentMax) {
content!.style.maxWidth = `${props.contentMax}px`; content.value!.style.maxWidth = `${props.contentMax}px`;
} }
}); });

View File

@ -19,17 +19,17 @@ const CURRENT_STICKY_TOP = "CURRENT_STICKY_TOP";
import type { Ref } from "vue"; import type { Ref } from "vue";
import { inject, onMounted, onUnmounted, provide, ref, watch } from "vue"; import { inject, onMounted, onUnmounted, provide, ref, watch } from "vue";
const headerEl = $ref<HTMLElement>(); const headerEl = ref<HTMLElement>();
const bodyEl = $ref<HTMLElement>(); const bodyEl = ref<HTMLElement>();
let headerHeight = $ref<string | undefined>(), let headerHeight = ref<string | undefined>(),
childStickyTop = $ref(0); childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0)); const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
provide(CURRENT_STICKY_TOP, $$(childStickyTop)); provide(CURRENT_STICKY_TOP, childStickyTop);
const calc = () => { const calc = () => {
childStickyTop = parentStickyTop.value + headerEl.offsetHeight; childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
headerHeight = headerEl.offsetHeight.toString(); headerHeight.value = headerEl.value.offsetHeight.toString();
}; };
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
@ -44,16 +44,19 @@ onMounted(() => {
watch(parentStickyTop, calc); watch(parentStickyTop, calc);
watch( watch(
$$(childStickyTop), childStickyTop,
() => { () => {
bodyEl.style.setProperty("--stickyTop", `${childStickyTop}px`); bodyEl.value.style.setProperty(
"--stickyTop",
`${childStickyTop.value}px`,
);
}, },
{ {
immediate: true, immediate: true,
}, },
); );
observer.observe(headerEl); observer.observe(headerEl.value);
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -10,7 +10,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from "vue"; import { onMounted, onUnmounted, ref, computed } from "vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { dateTimeFormat } from "@/scripts/intl-const"; import { dateTimeFormat } from "@/scripts/intl-const";
@ -37,12 +37,12 @@ const _time =
const invalid = Number.isNaN(_time); const invalid = Number.isNaN(_time);
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid; const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
let now = $ref((props.origin ?? new Date()).getTime()); let now = ref((props.origin ?? new Date()).getTime());
const relative = $computed<string>(() => { const relative = computed<string>(() => {
if (props.mode === "absolute") return ""; // absoluterelative使 if (props.mode === "absolute") return ""; // absoluterelative使
if (invalid) return i18n.ts._ago.invalid; if (invalid) return i18n.ts._ago.invalid;
const ago = (now - _time) / 1000; /* ms */ const ago = (now.value - _time) / 1000; /* ms */
return ago >= 31536000 return ago >= 31536000
? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() }) ? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() })
: ago >= 2592000 : ago >= 2592000
@ -66,11 +66,11 @@ let tickId: number;
function tick() { function tick() {
const _now = new Date().getTime(); const _now = new Date().getTime();
const agoPrev = (now - _time) / 1000; /* ms */ // interval const agoPrev = (now.value - _time) / 1000; /* ms */ // interval
now = _now; now.value = _now;
const ago = (now - _time) / 1000; /* ms */ // interval const ago = (now.value - _time) / 1000; /* ms */ // interval
const prev = agoPrev < 60 ? 10000 : agoPrev < 3600 ? 60000 : 180000; const prev = agoPrev < 60 ? 10000 : agoPrev < 3600 ? 60000 : 180000;
const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;

View File

@ -18,7 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onBeforeUnmount, provide } from "vue"; import { inject, onBeforeUnmount, provide, shallowRef, ref } from "vue";
import type { Resolved, Router } from "@/nirax"; import type { Resolved, Router } from "@/nirax";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -48,18 +48,18 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
} }
const current = resolveNested(router.current)!; const current = resolveNested(router.current)!;
let currentPageComponent = $shallowRef(current.route.component), let currentPageComponent = shallowRef(current.route.component),
currentPageProps = $ref(current.props), currentPageProps = ref(current.props),
key = $ref( key = ref(
current.route.path + JSON.stringify(Object.fromEntries(current.props)), current.route.path + JSON.stringify(Object.fromEntries(current.props)),
); );
function onChange({ resolved, key: newKey }) { function onChange({ resolved, key: newKey }) {
const current = resolveNested(resolved); const current = resolveNested(resolved);
if (current == null) return; if (current == null) return;
currentPageComponent = current.route.component; currentPageComponent.value = current.route.component;
currentPageProps = current.props; currentPageProps.value = current.props;
key = key.value =
current.route.path + JSON.stringify(Object.fromEntries(current.props)); current.route.path + JSON.stringify(Object.fromEntries(current.props));
} }

View File

@ -3,7 +3,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from "vue"; import { watch, ref } from "vue";
import MkTextarea from "../form/textarea.vue"; import MkTextarea from "../form/textarea.vue";
import { TextBlock } from "@/scripts/hpml/block"; import { TextBlock } from "@/scripts/hpml/block";
import { Hpml } from "@/scripts/hpml/evaluator"; import { Hpml } from "@/scripts/hpml/evaluator";
@ -13,12 +13,12 @@ const props = defineProps<{
hpml: Hpml; hpml: Hpml;
}>(); }>();
let text = $ref(""); let text = ref("");
watch( watch(
props.hpml.vars, props.hpml.vars,
() => { () => {
text = props.hpml.interpolate(props.block.text) as string; text.value = props.hpml.interpolate(props.block.text) as string;
}, },
{ {
deep: true, deep: true,

View File

@ -35,6 +35,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import * as misskey from "firefish-js"; import * as misskey from "firefish-js";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -51,22 +53,22 @@ const props = withDefaults(
{}, {},
); );
let loaded = $ref(false); let loaded = ref(false);
let serverIsDead = $ref(false); let serverIsDead = ref(false);
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null); let meta = ref<misskey.entities.LiteInstanceMetadata | null>(null);
os.api("meta", { os.api("meta", {
detail: false, detail: false,
}).then( }).then(
(res) => { (res) => {
loaded = true; loaded.value = true;
serverIsDead = false; serverIsDead.value = false;
meta = res; meta.value = res;
localStorage.setItem("v", res.version); localStorage.setItem("v", res.version);
}, },
() => { () => {
loaded = true; loaded.value = true;
serverIsDead = true; serverIsDead.value = true;
}, },
); );
@ -74,9 +76,9 @@ function reload() {
unisonReload(); unisonReload();
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.error, title: i18n.ts.error,

View File

@ -203,7 +203,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onBeforeUnmount } from "vue"; import { nextTick, onBeforeUnmount, ref, computed } from "vue";
import { version } from "@/config"; import { version } from "@/config";
import FormLink from "@/components/form/link.vue"; import FormLink from "@/components/form/link.vue";
import FormSection from "@/components/form/section.vue"; import FormSection from "@/components/form/section.vue";
@ -225,15 +225,15 @@ sponsors = patronsResp.sponsors;
patrons = patrons.filter((patron) => !sponsors.includes(patron)); patrons = patrons.filter((patron) => !sponsors.includes(patron));
let easterEggReady = false; let easterEggReady = false;
let easterEggEmojis = $ref([]); let easterEggEmojis = ref([]);
let easterEggEngine = $ref(null); let easterEggEngine = ref(null);
const containerEl = $ref<HTMLElement>(); const containerEl = ref<HTMLElement>();
function iconLoaded() { function iconLoaded() {
const emojis = defaultStore.state.reactions; const emojis = defaultStore.state.reactions;
const containerWidth = containerEl?.offsetWidth; const containerWidth = containerEl.value?.offsetWidth;
for (let i = 0; i < 32; i++) { for (let i = 0; i < 32; i++) {
easterEggEmojis.push({ easterEggEmojis.value.push({
id: i.toString(), id: i.toString(),
top: -(128 + Math.random() * 256), top: -(128 + Math.random() * 256),
left: Math.random() * containerWidth, left: Math.random() * containerWidth,
@ -249,7 +249,7 @@ function iconLoaded() {
function gravity() { function gravity() {
if (!easterEggReady) return; if (!easterEggReady) return;
easterEggReady = false; easterEggReady = false;
easterEggEngine = physics(containerEl); easterEggEngine.value = physics(containerEl.value);
} }
function iLoveMisskey() { function iLoveMisskey() {
@ -260,14 +260,14 @@ function iLoveMisskey() {
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (easterEggEngine) { if (easterEggEngine.value) {
easterEggEngine.stop(); easterEggEngine.value.stop();
} }
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.aboutFirefish, title: i18n.ts.aboutFirefish,

View File

@ -105,7 +105,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue"; import MkSelect from "@/components/form/select.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
@ -113,29 +113,29 @@ import MkInstanceCardMini from "@/components/MkInstanceCardMini.vue";
import FormSplit from "@/components/form/split.vue"; import FormSplit from "@/components/form/split.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
let host = $ref(""); let host = ref("");
let state = $ref("federating"); let state = ref("federating");
let sort = $ref("+pubSub"); let sort = ref("+pubSub");
const pagination = { const pagination = {
endpoint: "federation/instances" as const, endpoint: "federation/instances" as const,
limit: 10, limit: 10,
offsetMode: true, offsetMode: true,
params: computed(() => ({ params: computed(() => ({
sort: sort, sort: sort.value,
host: host !== "" ? host : null, host: host.value !== "" ? host.value : null,
...(state === "federating" ...(state.value === "federating"
? { federating: true } ? { federating: true }
: state === "subscribing" : state.value === "subscribing"
? { subscribing: true } ? { subscribing: true }
: state === "publishing" : state.value === "publishing"
? { publishing: true } ? { publishing: true }
: state === "suspended" : state.value === "suspended"
? { suspended: true } ? { suspended: true }
: state === "blocked" : state.value === "blocked"
? { blocked: true } ? { blocked: true }
: state === "silenced" : state.value === "silenced"
? { silenced: true } ? { silenced: true }
: state === "notResponding" : state.value === "notResponding"
? { notResponding: true } ? { notResponding: true }
: {}), : {}),
})), })),

View File

@ -205,23 +205,23 @@ withDefaults(
}, },
); );
let stats = $ref(null); let stats = ref(null);
let instanceIcon = $ref<HTMLImageElement>(); let instanceIcon = ref<HTMLImageElement>();
let iconClicks = 0; let iconClicks = 0;
let iconSrc = ref(instance.iconUrl || instance.faviconUrl || "/favicon.ico"); let iconSrc = ref(instance.iconUrl || instance.faviconUrl || "/favicon.ico");
let instanceIconAnimation = ref(""); let instanceIconAnimation = ref("");
let tabs = ["overview", "emojis", "charts"]; let tabs = ["overview", "emojis", "charts"];
let tab = $ref(tabs[0]); let tab = ref(tabs[0]);
watch($$(tab), () => syncSlide(tabs.indexOf(tab))); watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
if (iAmModerator) tabs.push("federation"); if (iAmModerator) tabs.push("federation");
const initStats = () => const initStats = () =>
os.api("stats", {}).then((res) => { os.api("stats", {}).then((res) => {
stats = res; stats.value = res;
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
let theTabs = [ let theTabs = [
{ {
@ -249,7 +249,7 @@ if (iAmModerator) {
}); });
} }
let headerTabs = $computed(() => theTabs); let headerTabs = computed(() => theTabs);
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({
@ -261,7 +261,7 @@ definePageMetadata(
onMounted(() => { onMounted(() => {
if (defaultStore.state.woozyMode === true) { if (defaultStore.state.woozyMode === true) {
iconSrc.value = "/static-assets/woozy.png"; iconSrc.value = "/static-assets/woozy.png";
instanceIcon.src = iconSrc.value; instanceIcon.value.src = iconSrc.value;
} }
}); });
@ -270,7 +270,7 @@ function easterEgg() {
if (iconClicks % 3 === 0) { if (iconClicks % 3 === 0) {
defaultStore.state.woozyMode = !defaultStore.state.woozyMode; defaultStore.state.woozyMode = !defaultStore.state.woozyMode;
defaultStore.set("woozyMode", defaultStore.state.woozyMode); defaultStore.set("woozyMode", defaultStore.state.woozyMode);
if (instanceIcon) { if (instanceIcon.value) {
instanceIconAnimation.value = "spin"; instanceIconAnimation.value = "spin";
setTimeout(() => { setTimeout(() => {
if (iconClicks % 6 === 0) { if (iconClicks % 6 === 0) {
@ -281,7 +281,7 @@ function easterEgg() {
} else { } else {
iconSrc.value = "/static-assets/woozy.png"; iconSrc.value = "/static-assets/woozy.png";
} }
instanceIcon.src = iconSrc.value; instanceIcon.value.src = iconSrc.value;
}, 500); }, 500);
} }
} }
@ -299,11 +299,11 @@ let swiperRef = null;
function setSwiperRef(swiper) { function setSwiperRef(swiper) {
swiperRef = swiper; swiperRef = swiper;
syncSlide(tabs.indexOf(tab)); syncSlide(tabs.indexOf(tab.value));
} }
function onSlideChange() { function onSlideChange() {
tab = tabs[swiperRef.activeIndex]; tab.value = tabs[swiperRef.activeIndex];
} }
function syncSlide(index) { function syncSlide(index) {

View File

@ -156,7 +156,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch } from "vue"; import { computed, watch, ref } from "vue";
import { Virtual } from "swiper/modules"; import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -181,21 +181,23 @@ import "swiper/scss/virtual";
let tabs = ["overview"]; let tabs = ["overview"];
if (iAmModerator) tabs.push("ip"); if (iAmModerator) tabs.push("ip");
tabs.push("raw"); tabs.push("raw");
let tab = $ref(tabs[0]); let tab = ref(tabs[0]);
watch($$(tab), () => syncSlide(tabs.indexOf(tab))); watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
let file: any = $ref(null); let file: any = ref(null);
let info: any = $ref(null); let info: any = ref(null);
let isSensitive: boolean = $ref(false); let isSensitive: boolean = ref(false);
const props = defineProps<{ const props = defineProps<{
fileId: string; fileId: string;
}>(); }>();
async function fetch() { async function fetch() {
file = await os.api("drive/files/show", { fileId: props.fileId }); file.value = await os.api("drive/files/show", { fileId: props.fileId });
info = await os.api("admin/drive/show-file", { fileId: props.fileId }); info.value = await os.api("admin/drive/show-file", {
isSensitive = file.isSensitive; fileId: props.fileId,
});
isSensitive.value = file.value.isSensitive;
} }
fetch(); fetch();
@ -203,12 +205,12 @@ fetch();
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: "warning", type: "warning",
text: i18n.t("removeAreYouSure", { x: file.name }), text: i18n.t("removeAreYouSure", { x: file.value.name }),
}); });
if (canceled) return; if (canceled) return;
os.apiWithDialog("drive/files/delete", { os.apiWithDialog("drive/files/delete", {
fileId: file.id, fileId: file.value.id,
}); });
} }
@ -217,20 +219,20 @@ async function toggleIsSensitive(v) {
fileId: props.fileId, fileId: props.fileId,
isSensitive: v, isSensitive: v,
}); });
isSensitive = v; isSensitive.value = v;
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
icon: "ph-arrow-square-out ph-bold ph-lg", icon: "ph-arrow-square-out ph-bold ph-lg",
handler: () => { handler: () => {
window.open(file.url, "_blank"); window.open(file.value.url, "_blank");
}, },
}, },
]); ]);
const headerTabs = $computed(() => [ const headerTabs = computed(() => [
{ {
key: "overview", key: "overview",
title: i18n.ts.overview, title: i18n.ts.overview,
@ -252,7 +254,9 @@ const headerTabs = $computed(() => [
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({
title: file ? i18n.ts.file + ": " + file.name : i18n.ts.file, title: file.value
? i18n.ts.file + ": " + file.value.name
: i18n.ts.file,
icon: "ph-file ph-bold ph-lg", icon: "ph-file ph-bold ph-lg",
})), })),
); );
@ -261,11 +265,11 @@ let swiperRef = null;
function setSwiperRef(swiper) { function setSwiperRef(swiper) {
swiperRef = swiper; swiperRef = swiper;
syncSlide(tabs.indexOf(tab)); syncSlide(tabs.indexOf(tab.value));
} }
function onSlideChange() { function onSlideChange() {
tab = tabs[swiperRef.activeIndex]; tab.value = tabs[swiperRef.activeIndex];
} }
function syncSlide(index) { function syncSlide(index) {

View File

@ -102,7 +102,7 @@ const metadata = injectPageMetadata();
const el = ref<HTMLElement>(null); const el = ref<HTMLElement>(null);
const tabRefs = {}; const tabRefs = {};
const tabHighlightEl = $ref<HTMLElement | null>(null); const tabHighlightEl = ref<HTMLElement | null>(null);
const bg = ref(null); const bg = ref(null);
const height = ref(0); const height = ref(0);
const hasTabs = computed(() => { const hasTabs = computed(() => {
@ -172,14 +172,14 @@ onMounted(() => {
() => { () => {
nextTick(() => { nextTick(() => {
const tabEl = tabRefs[props.tab]; const tabEl = tabRefs[props.tab];
if (tabEl && tabHighlightEl) { if (tabEl && tabHighlightEl.value) {
// offsetWidth offsetLeft getBoundingClientRect 使 // offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = const parentRect =
tabEl.parentElement.getBoundingClientRect(); tabEl.parentElement.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect(); const rect = tabEl.getBoundingClientRect();
tabHighlightEl.style.width = rect.width + "px"; tabHighlightEl.value.style.width = rect.width + "px";
tabHighlightEl.style.left = tabHighlightEl.value.style.left =
rect.left - parentRect.left + "px"; rect.left - parentRect.left + "px";
} }
}); });

View File

@ -91,7 +91,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue"; import MkSelect from "@/components/form/select.vue";
@ -101,31 +101,31 @@ import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let reports = $ref<InstanceType<typeof MkPagination>>(); let reports = ref<InstanceType<typeof MkPagination>>();
let state = $ref("unresolved"); let state = ref("unresolved");
let reporterOrigin = $ref("combined"); let reporterOrigin = ref("combined");
let targetUserOrigin = $ref("combined"); let targetUserOrigin = ref("combined");
let searchUsername = $ref(""); let searchUsername = ref("");
let searchHost = $ref(""); let searchHost = ref("");
const pagination = { const pagination = {
endpoint: "admin/abuse-user-reports" as const, endpoint: "admin/abuse-user-reports" as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
state, state: state.value,
reporterOrigin, reporterOrigin: reporterOrigin.value,
targetUserOrigin, targetUserOrigin: targetUserOrigin.value,
})), })),
}; };
function resolved(reportId) { function resolved(reportId) {
reports.removeItem((item) => item.id === reportId); reports.value.removeItem((item) => item.id === reportId);
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.abuseReports, title: i18n.ts.abuseReports,

View File

@ -65,6 +65,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "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";
@ -74,14 +76,14 @@ import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let announcements: any[] = $ref([]); let announcements: any[] = ref([]);
os.api("admin/announcements/list").then((announcementResponse) => { os.api("admin/announcements/list").then((announcementResponse) => {
announcements = announcementResponse; announcements.value = announcementResponse;
}); });
function add() { function add() {
announcements.unshift({ announcements.value.unshift({
id: null, id: null,
title: "", title: "",
text: "", text: "",
@ -97,7 +99,9 @@ function remove(announcement) {
text: i18n.t("removeAreYouSure", { x: announcement.title }), text: i18n.t("removeAreYouSure", { x: announcement.title }),
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
announcements = announcements.filter((x) => x !== announcement); announcements.value = announcements.value.filter(
(x) => x !== announcement,
);
os.api("admin/announcements/delete", announcement); os.api("admin/announcements/delete", announcement);
}); });
} }
@ -134,7 +138,7 @@ function save(announcement) {
} }
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
asFullButton: true, asFullButton: true,
icon: "ph-plus ph-bold ph-lg", icon: "ph-plus ph-bold ph-lg",
@ -143,7 +147,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.announcements, title: i18n.ts.announcements,

View File

@ -74,7 +74,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from "vue"; import { defineAsyncComponent, ref } from "vue";
import FormRadios from "@/components/form/radios.vue"; import FormRadios from "@/components/form/radios.vue";
import FormInput from "@/components/form/input.vue"; import FormInput from "@/components/form/input.vue";
import FormButton from "@/components/MkButton.vue"; import FormButton from "@/components/MkButton.vue";
@ -88,20 +88,20 @@ const MkCaptcha = defineAsyncComponent(
() => import("@/components/MkCaptcha.vue"), () => import("@/components/MkCaptcha.vue"),
); );
let provider = $ref(null); let provider = ref(null);
let hcaptchaSiteKey: string | null = $ref(null); let hcaptchaSiteKey: string | null = ref(null);
let hcaptchaSecretKey: string | null = $ref(null); let hcaptchaSecretKey: string | null = ref(null);
let recaptchaSiteKey: string | null = $ref(null); let recaptchaSiteKey: string | null = ref(null);
let recaptchaSecretKey: string | null = $ref(null); let recaptchaSecretKey: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
hcaptchaSiteKey = meta.hcaptchaSiteKey; hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
hcaptchaSecretKey = meta.hcaptchaSecretKey; hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
recaptchaSiteKey = meta.recaptchaSiteKey; recaptchaSiteKey.value = meta.recaptchaSiteKey;
recaptchaSecretKey = meta.recaptchaSecretKey; recaptchaSecretKey.value = meta.recaptchaSecretKey;
provider = meta.enableHcaptcha provider.value = meta.enableHcaptcha
? "hcaptcha" ? "hcaptcha"
: meta.enableRecaptcha : meta.enableRecaptcha
? "recaptcha" ? "recaptcha"
@ -110,12 +110,12 @@ async function init() {
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
enableHcaptcha: provider === "hcaptcha", enableHcaptcha: provider.value === "hcaptcha",
hcaptchaSiteKey, hcaptchaSiteKey: hcaptchaSiteKey.value,
hcaptchaSecretKey, hcaptchaSecretKey: hcaptchaSecretKey.value,
enableRecaptcha: provider === "recaptcha", enableRecaptcha: provider.value === "recaptcha",
recaptchaSiteKey, recaptchaSiteKey: recaptchaSiteKey.value,
recaptchaSecretKey, recaptchaSecretKey: recaptchaSecretKey.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from "vue"; import { ref, watch, computed } from "vue";
import FormTextarea from "@/components/form/textarea.vue"; import FormTextarea from "@/components/form/textarea.vue";
import FormInfo from "@/components/MkInfo.vue"; import FormInfo from "@/components/MkInfo.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -45,9 +45,9 @@ watch(globalCustomCss, async () => {
}); });
*/ */
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.customCss, title: i18n.ts.customCss,

View File

@ -34,6 +34,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormSuspense from "@/components/form/suspense.vue"; import FormSuspense from "@/components/form/suspense.vue";
import FormButton from "@/components/MkButton.vue"; import FormButton from "@/components/MkButton.vue";
@ -52,9 +54,9 @@ const databasePromiseFactory = () =>
Object.entries(res).sort((a, b) => b[1].size - a[1].size), Object.entries(res).sort((a, b) => b[1].size - a[1].size),
); );
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.database, title: i18n.ts.database,

View File

@ -91,6 +91,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
import FormInput from "@/components/form/input.vue"; import FormInput from "@/components/form/input.vue";
@ -103,23 +105,23 @@ import { fetchInstance, instance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let enableEmail: boolean = $ref(false); let enableEmail: boolean = ref(false);
let email: any = $ref(null); let email: any = ref(null);
let smtpSecure: boolean = $ref(false); let smtpSecure: boolean = ref(false);
let smtpHost: string = $ref(""); let smtpHost: string = ref("");
let smtpPort: number = $ref(0); let smtpPort: number = ref(0);
let smtpUser: string = $ref(""); let smtpUser: string = ref("");
let smtpPass: string = $ref(""); let smtpPass: string = ref("");
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
enableEmail = meta.enableEmail; enableEmail.value = meta.enableEmail;
email = meta.email; email.value = meta.email;
smtpSecure = meta.smtpSecure; smtpSecure.value = meta.smtpSecure;
smtpHost = meta.smtpHost; smtpHost.value = meta.smtpHost;
smtpPort = meta.smtpPort; smtpPort.value = meta.smtpPort;
smtpUser = meta.smtpUser; smtpUser.value = meta.smtpUser;
smtpPass = meta.smtpPass; smtpPass.value = meta.smtpPass;
} }
async function testEmail() { async function testEmail() {
@ -138,19 +140,19 @@ async function testEmail() {
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
enableEmail, enableEmail: enableEmail.value,
email, email: email.value,
smtpSecure, smtpSecure: smtpSecure.value,
smtpHost, smtpHost: smtpHost.value,
smtpPort, smtpPort: smtpPort.value,
smtpUser, smtpUser: smtpUser.value,
smtpPass, smtpPass: smtpPass.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
asFullButton: true, asFullButton: true,
icon: "ph-test-tube ph-bold ph-lg", icon: "ph-test-tube ph-bold ph-lg",
@ -165,7 +167,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.emailServer, title: i18n.ts.emailServer,

View File

@ -41,6 +41,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -55,12 +57,12 @@ const props = defineProps<{
emoji: any; emoji: any;
}>(); }>();
let dialog = $ref(null); let dialog = ref(null);
let name: string = $ref(props.emoji.name); let name: string = ref(props.emoji.name);
let category: string = $ref(props.emoji.category); let category: string = ref(props.emoji.category);
let aliases: string = $ref(props.emoji.aliases.join(" ")); let aliases: string = ref(props.emoji.aliases.join(" "));
let categories: string[] = $ref(emojiCategories); let categories: string[] = ref(emojiCategories);
let license: string = $ref(props.emoji.license ?? ""); let license: string = ref(props.emoji.license ?? "");
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "done", v: { deleted?: boolean; updated?: any }): void; (ev: "done", v: { deleted?: boolean; updated?: any }): void;
@ -74,29 +76,29 @@ function ok() {
async function update() { async function update() {
await os.apiWithDialog("admin/emoji/update", { await os.apiWithDialog("admin/emoji/update", {
id: props.emoji.id, id: props.emoji.id,
name, name: name.value,
category, category: category.value,
aliases: aliases.split(" "), aliases: aliases.value.split(" "),
license: license === "" ? null : license, license: license.value === "" ? null : license.value,
}); });
emit("done", { emit("done", {
updated: { updated: {
id: props.emoji.id, id: props.emoji.id,
name, name: name.value,
category, category: category.value,
aliases: aliases.split(" "), aliases: aliases.value.split(" "),
license: license === "" ? null : license, license: license.value === "" ? null : license.value,
}, },
}); });
dialog.close(); dialog.value.close();
} }
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: "warning", type: "warning",
text: i18n.t("removeAreYouSure", { x: name }), text: i18n.t("removeAreYouSure", { x: name.value }),
}); });
if (canceled) return; if (canceled) return;
@ -106,7 +108,7 @@ async function del() {
emit("done", { emit("done", {
deleted: true, deleted: true,
}); });
dialog.close(); dialog.value.close();
}); });
} }
</script> </script>

View File

@ -422,7 +422,7 @@ const delBulk = async () => {
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value.reload();
}; };
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
asFullButton: true, asFullButton: true,
icon: "ph-plus ph-bold ph-lg", icon: "ph-plus ph-bold ph-lg",
@ -435,7 +435,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => [ const headerTabs = computed(() => [
{ {
key: "local", key: "local",
icon: "ph-hand-fist ph-bold ph-lg", icon: "ph-hand-fist ph-bold ph-lg",

View File

@ -27,6 +27,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import MkStickyContainer from "@/components/global/MkStickyContainer.vue"; import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
import FormSuspense from "@/components/form/suspense.vue"; import FormSuspense from "@/components/form/suspense.vue";
@ -36,8 +38,8 @@ import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let enablePostImports = $ref(false); let enablePostImports = ref(false);
let meta = $ref<MetaExperiments | null>(null); let meta = ref<MetaExperiments | null>(null);
type MetaExperiments = { type MetaExperiments = {
experimentalFeatures?: { experimentalFeatures?: {
@ -46,16 +48,17 @@ type MetaExperiments = {
}; };
async function init() { async function init() {
meta = (await os.api("admin/meta")) as MetaExperiments; meta.value = (await os.api("admin/meta")) as MetaExperiments;
if (!meta) return; if (!meta.value) return;
enablePostImports = meta.experimentalFeatures?.postImports ?? false; enablePostImports.value =
meta.value.experimentalFeatures?.postImports ?? false;
} }
function save() { function save() {
const experiments: MetaExperiments = { const experiments: MetaExperiments = {
experimentalFeatures: { experimentalFeatures: {
postImports: enablePostImports, postImports: enablePostImports.value,
}, },
}; };
os.apiWithDialog("admin/update-meta", experiments).then(() => { os.apiWithDialog("admin/update-meta", experiments).then(() => {
@ -63,9 +66,9 @@ function save() {
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts._experiments.title, title: i18n.ts._experiments.title,

View File

@ -20,9 +20,9 @@ import { definePageMetadata } from "@/scripts/page-metadata";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import XFederation from "@/pages/about.federation.vue"; import XFederation from "@/pages/about.federation.vue";
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({

View File

@ -80,7 +80,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import * as Acct from "firefish-js/built/acct"; import * as Acct from "firefish-js/built/acct";
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";
@ -91,19 +91,22 @@ import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let origin = $ref("local"); let origin = ref("local");
let type = $ref(null); let type = ref(null);
let searchHost = $ref(""); let searchHost = ref("");
let userId = $ref(""); let userId = ref("");
let viewMode = $ref("grid"); let viewMode = ref("grid");
const pagination = { const pagination = {
endpoint: "admin/drive/files" as const, endpoint: "admin/drive/files" as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
type: type && type !== "" ? type : null, type: type.value && type.value !== "" ? type.value : null,
userId: userId && userId !== "" ? userId : null, userId: userId.value && userId.value !== "" ? userId.value : null,
origin: origin, origin: origin.value,
hostname: searchHost && searchHost !== "" ? searchHost : null, hostname:
searchHost.value && searchHost.value !== ""
? searchHost.value
: null,
})), })),
}; };
@ -118,7 +121,7 @@ function clear() {
}); });
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
text: i18n.ts.lookup, text: i18n.ts.lookup,
icon: "ph-magnifying-glass ph-bold ph-lg", icon: "ph-magnifying-glass ph-bold ph-lg",
@ -131,7 +134,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata( definePageMetadata(
computed(() => ({ computed(() => ({

View File

@ -26,6 +26,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormButton from "@/components/MkButton.vue"; import FormButton from "@/components/MkButton.vue";
import FormTextarea from "@/components/form/textarea.vue"; import FormTextarea from "@/components/form/textarea.vue";
@ -35,24 +37,25 @@ import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let hiddenTags: string = $ref(""); let hiddenTags: string = ref("");
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
hiddenTags = meta.hiddenTags.join("\n"); hiddenTags.value = meta.hiddenTags.join("\n");
} }
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
hiddenTags: hiddenTags.split("\n").map((h: string) => h.trim()) || [], hiddenTags:
hiddenTags.value.split("\n").map((h: string) => h.trim()) || [],
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.hiddenTags, title: i18n.ts.hiddenTags,

View File

@ -59,17 +59,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, onActivated, provide, watch, ref, computed } from 'vue';
defineAsyncComponent,
inject,
nextTick,
onMounted,
onUnmounted,
onActivated,
provide,
watch,
ref,
} from "vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import MkSuperMenu from "@/components/MkSuperMenu.vue"; import MkSuperMenu from "@/components/MkSuperMenu.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
@ -102,11 +92,11 @@ const indexInfo = {
provide("shouldOmitHeaderTitle", false); provide("shouldOmitHeaderTitle", false);
let INFO = $ref(indexInfo); let INFO = ref(indexInfo);
let childInfo = $ref(null); let childInfo = ref(null);
let narrow = $ref(false); let narrow = ref(false);
let view = $ref(null); let view = ref(null);
let pageProps = $ref({}); let pageProps = ref({});
let noMaintainerInformation = let noMaintainerInformation =
isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail); isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
let noBotProtection = let noBotProtection =
@ -114,15 +104,15 @@ let noBotProtection =
!instance.enableHcaptcha && !instance.enableHcaptcha &&
!instance.enableRecaptcha; !instance.enableRecaptcha;
let noEmailServer = !instance.enableEmail; let noEmailServer = !instance.enableEmail;
let thereIsUnresolvedAbuseReport = $ref(false); let thereIsUnresolvedAbuseReport = ref(false);
let updateAvailable = $ref(false); let updateAvailable = ref(false);
let currentPage = $computed(() => router.currentRef.value.child); let currentPage = computed(() => router.currentRef.value.child);
os.api("admin/abuse-user-reports", { os.api("admin/abuse-user-reports", {
state: "unresolved", state: "unresolved",
limit: 1, limit: 1,
}).then((reports) => { }).then((reports) => {
if (reports?.length > 0) thereIsUnresolvedAbuseReport = true; if (reports?.length > 0) thereIsUnresolvedAbuseReport.value = true;
}); });
if (defaultStore.state.showAdminUpdates) { if (defaultStore.state.showAdminUpdates) {
@ -130,7 +120,7 @@ if (defaultStore.state.showAdminUpdates) {
const cleanRes = parseInt(res?.tag_name.replace(/[^0-9]/g, "")); const cleanRes = parseInt(res?.tag_name.replace(/[^0-9]/g, ""));
const cleanVersion = parseInt(version.replace(/[^0-9]/g, "")); const cleanVersion = parseInt(version.replace(/[^0-9]/g, ""));
if (cleanRes > cleanVersion) { if (cleanRes > cleanVersion) {
updateAvailable = true; updateAvailable.value = true;
} }
}); });
} }
@ -138,10 +128,10 @@ if (defaultStore.state.showAdminUpdates) {
const NARROW_THRESHOLD = 600; const NARROW_THRESHOLD = 600;
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {
if (entries.length === 0) return; if (entries.length === 0) return;
narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD; narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
}); });
const menuDef = $computed(() => [ const menuDef = computed(() => [
{ {
title: i18n.ts.quickAction, title: i18n.ts.quickAction,
items: [ items: [
@ -180,55 +170,55 @@ const menuDef = $computed(() => [
icon: "ph-gauge ph-bold ph-lg", icon: "ph-gauge ph-bold ph-lg",
text: i18n.ts.dashboard, text: i18n.ts.dashboard,
to: "/admin/overview", to: "/admin/overview",
active: currentPage?.route.name === "overview", active: currentPage.value?.route.name === "overview",
}, },
{ {
icon: "ph-users ph-bold ph-lg", icon: "ph-users ph-bold ph-lg",
text: i18n.ts.users, text: i18n.ts.users,
to: "/admin/users", to: "/admin/users",
active: currentPage?.route.name === "users", active: currentPage.value?.route.name === "users",
}, },
{ {
icon: "ph-smiley ph-bold ph-lg", icon: "ph-smiley ph-bold ph-lg",
text: i18n.ts.customEmojis, text: i18n.ts.customEmojis,
to: "/admin/emojis", to: "/admin/emojis",
active: currentPage?.route.name === "emojis", active: currentPage.value?.route.name === "emojis",
}, },
{ {
icon: "ph-planet ph-bold ph-lg", icon: "ph-planet ph-bold ph-lg",
text: i18n.ts.federation, text: i18n.ts.federation,
to: "/admin/federation", to: "/admin/federation",
active: currentPage?.route.name === "federation", active: currentPage.value?.route.name === "federation",
}, },
{ {
icon: "ph-queue ph-bold ph-lg", icon: "ph-queue ph-bold ph-lg",
text: i18n.ts.jobQueue, text: i18n.ts.jobQueue,
to: "/admin/queue", to: "/admin/queue",
active: currentPage?.route.name === "queue", active: currentPage.value?.route.name === "queue",
}, },
{ {
icon: "ph-cloud ph-bold ph-lg", icon: "ph-cloud ph-bold ph-lg",
text: i18n.ts.files, text: i18n.ts.files,
to: "/admin/files", to: "/admin/files",
active: currentPage?.route.name === "files", active: currentPage.value?.route.name === "files",
}, },
{ {
icon: "ph-megaphone-simple ph-bold ph-lg", icon: "ph-megaphone-simple ph-bold ph-lg",
text: i18n.ts.announcements, text: i18n.ts.announcements,
to: "/admin/announcements", to: "/admin/announcements",
active: currentPage?.route.name === "announcements", active: currentPage.value?.route.name === "announcements",
}, },
{ {
icon: "ph-money ph-bold ph-lg", icon: "ph-money ph-bold ph-lg",
text: i18n.ts.ads, text: i18n.ts.ads,
to: "/admin/ads", to: "/admin/ads",
active: currentPage?.route.name === "ads", active: currentPage.value?.route.name === "ads",
}, },
{ {
icon: "ph-warning-circle ph-bold ph-lg", icon: "ph-warning-circle ph-bold ph-lg",
text: i18n.ts.abuseReports, text: i18n.ts.abuseReports,
to: "/admin/abuses", to: "/admin/abuses",
active: currentPage?.route.name === "abuses", active: currentPage.value?.route.name === "abuses",
}, },
], ],
}, },
@ -241,70 +231,70 @@ const menuDef = $computed(() => [
icon: "ph-gear-six ph-bold ph-lg", icon: "ph-gear-six ph-bold ph-lg",
text: i18n.ts.general, text: i18n.ts.general,
to: "/admin/settings", to: "/admin/settings",
active: currentPage?.route.name === "settings", active: currentPage.value?.route.name === "settings",
}, },
{ {
icon: "ph-envelope-simple-open ph-bold ph-lg", icon: "ph-envelope-simple-open ph-bold ph-lg",
text: i18n.ts.emailServer, text: i18n.ts.emailServer,
to: "/admin/email-settings", to: "/admin/email-settings",
active: active:
currentPage?.route.name === "email-settings", currentPage.value?.route.name === "email-settings",
}, },
{ {
icon: "ph-cloud ph-bold ph-lg", icon: "ph-cloud ph-bold ph-lg",
text: i18n.ts.objectStorage, text: i18n.ts.objectStorage,
to: "/admin/object-storage", to: "/admin/object-storage",
active: active:
currentPage?.route.name === "object-storage", currentPage.value?.route.name === "object-storage",
}, },
{ {
icon: "ph-lock ph-bold ph-lg", icon: "ph-lock ph-bold ph-lg",
text: i18n.ts.security, text: i18n.ts.security,
to: "/admin/security", to: "/admin/security",
active: currentPage?.route.name === "security", active: currentPage.value?.route.name === "security",
}, },
{ {
icon: "ph-arrows-merge ph-bold ph-lg", icon: "ph-arrows-merge ph-bold ph-lg",
text: i18n.ts.relays, text: i18n.ts.relays,
to: "/admin/relays", to: "/admin/relays",
active: currentPage?.route.name === "relays", active: currentPage.value?.route.name === "relays",
}, },
{ {
icon: "ph-plug ph-bold ph-lg", icon: "ph-plug ph-bold ph-lg",
text: i18n.ts.integration, text: i18n.ts.integration,
to: "/admin/integrations", to: "/admin/integrations",
active: currentPage?.route.name === "integrations", active: currentPage.value?.route.name === "integrations",
}, },
{ {
icon: "ph-prohibit ph-bold ph-lg", icon: "ph-prohibit ph-bold ph-lg",
text: i18n.ts.instanceBlocking, text: i18n.ts.instanceBlocking,
to: "/admin/instance-block", to: "/admin/instance-block",
active: active:
currentPage?.route.name === "instance-block", currentPage.value?.route.name === "instance-block",
}, },
{ {
icon: "ph-hash ph-bold ph-lg", icon: "ph-hash ph-bold ph-lg",
text: i18n.ts.hiddenTags, text: i18n.ts.hiddenTags,
to: "/admin/hashtags", to: "/admin/hashtags",
active: currentPage?.route.name === "hashtags", active: currentPage.value?.route.name === "hashtags",
}, },
{ {
icon: "ph-ghost ph-bold ph-lg", icon: "ph-ghost ph-bold ph-lg",
text: i18n.ts.proxyAccount, text: i18n.ts.proxyAccount,
to: "/admin/proxy-account", to: "/admin/proxy-account",
active: currentPage?.route.name === "proxy-account", active: currentPage.value?.route.name === "proxy-account",
}, },
{ {
icon: "ph-database ph-bold ph-lg", icon: "ph-database ph-bold ph-lg",
text: i18n.ts.database, text: i18n.ts.database,
to: "/admin/database", to: "/admin/database",
active: currentPage?.route.name === "database", active: currentPage.value?.route.name === "database",
}, },
{ {
icon: "ph-flask ph-bold ph-lg", icon: "ph-flask ph-bold ph-lg",
text: i18n.ts._experiments.title, text: i18n.ts._experiments.title,
to: "/admin/experiments", to: "/admin/experiments",
active: currentPage?.route.name === "experiments", active: currentPage.value?.route.name === "experiments",
}, },
], ],
}, },
@ -312,8 +302,8 @@ const menuDef = $computed(() => [
: []), : []),
]); ]);
watch(narrow, () => { watch(narrow.value, () => {
if (currentPage?.route.name == null && !narrow) { if (currentPage.value?.route.name == null && !narrow.value) {
router.push("/admin/overview"); router.push("/admin/overview");
} }
}); });
@ -321,16 +311,16 @@ watch(narrow, () => {
onMounted(() => { onMounted(() => {
ro.observe(el.value); ro.observe(el.value);
narrow = el.value.offsetWidth < NARROW_THRESHOLD; narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
if (currentPage?.route.name == null && !narrow) { if (currentPage.value?.route.name == null && !narrow.value) {
router.push("/admin/overview"); router.push("/admin/overview");
} }
}); });
onActivated(() => { onActivated(() => {
narrow = el.value.offsetWidth < NARROW_THRESHOLD; narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
if (!narrow && currentPage?.route.name == null) { if (!narrow.value && currentPage.value?.route.name == null) {
router.replace("/admin/overview"); router.replace("/admin/overview");
} }
}); });
@ -340,16 +330,16 @@ onUnmounted(() => {
}); });
watch(router.currentRef, (to) => { watch(router.currentRef, (to) => {
if (to.route.path === "/admin" && to.child?.route.name == null && !narrow) { if (to.route.path === "/admin" && to.child?.route.name == null && !narrow.value) {
router.replace("/admin/overview"); router.replace("/admin/overview");
} }
}); });
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
if (info == null) { if (info == null) {
childInfo = null; childInfo.value = null;
} else { } else {
childInfo = info; childInfo.value = info;
} }
}); });
@ -430,11 +420,11 @@ const lookup = (ev) => {
); );
}; };
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata(INFO); definePageMetadata(INFO.value);
defineExpose({ defineExpose({
header: { header: {

View File

@ -43,6 +43,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormButton from "@/components/MkButton.vue"; import FormButton from "@/components/MkButton.vue";
import FormTextarea from "@/components/form/textarea.vue"; import FormTextarea from "@/components/form/textarea.vue";
@ -53,30 +55,31 @@ import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let blockedHosts: string = $ref(""); let blockedHosts: string = ref("");
let silencedHosts: string = $ref(""); let silencedHosts: string = ref("");
let tab = $ref("block"); let tab = ref("block");
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
if (meta) { if (meta) {
blockedHosts = meta.blockedHosts.join("\n"); blockedHosts.value = meta.blockedHosts.join("\n");
silencedHosts = meta.silencedHosts.join("\n"); silencedHosts.value = meta.silencedHosts.join("\n");
} }
} }
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
blockedHosts: blockedHosts.split("\n").map((h) => h.trim()) || [], blockedHosts: blockedHosts.value.split("\n").map((h) => h.trim()) || [],
silencedHosts: silencedHosts.split("\n").map((h) => h.trim()) || [], silencedHosts:
silencedHosts.value.split("\n").map((h) => h.trim()) || [],
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.instanceBlocking, title: i18n.ts.instanceBlocking,

View File

@ -34,6 +34,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
import FormInput from "@/components/form/input.vue"; import FormInput from "@/components/form/input.vue";
@ -44,24 +46,24 @@ import * as os from "@/os";
import { fetchInstance } from "@/instance"; import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
let uri: string = $ref(""); let uri: string = ref("");
let enableDiscordIntegration: boolean = $ref(false); let enableDiscordIntegration: boolean = ref(false);
let discordClientId: string | null = $ref(null); let discordClientId: string | null = ref(null);
let discordClientSecret: string | null = $ref(null); let discordClientSecret: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
uri = meta.uri; uri.value = meta.uri;
enableDiscordIntegration = meta.enableDiscordIntegration; enableDiscordIntegration.value = meta.enableDiscordIntegration;
discordClientId = meta.discordClientId; discordClientId.value = meta.discordClientId;
discordClientSecret = meta.discordClientSecret; discordClientSecret.value = meta.discordClientSecret;
} }
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
enableDiscordIntegration, enableDiscordIntegration: enableDiscordIntegration.value,
discordClientId, discordClientId: discordClientId.value,
discordClientSecret, discordClientSecret: discordClientSecret.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -34,6 +34,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import {} from "vue"; import {} from "vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
import FormInput from "@/components/form/input.vue"; import FormInput from "@/components/form/input.vue";
@ -44,24 +46,24 @@ import * as os from "@/os";
import { fetchInstance } from "@/instance"; import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
let uri: string = $ref(""); let uri: string = ref("");
let enableGithubIntegration: boolean = $ref(false); let enableGithubIntegration: boolean = ref(false);
let githubClientId: string | null = $ref(null); let githubClientId: string | null = ref(null);
let githubClientSecret: string | null = $ref(null); let githubClientSecret: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
uri = meta.uri; uri.value = meta.uri;
enableGithubIntegration = meta.enableGithubIntegration; enableGithubIntegration.value = meta.enableGithubIntegration;
githubClientId = meta.githubClientId; githubClientId.value = meta.githubClientId;
githubClientSecret = meta.githubClientSecret; githubClientSecret.value = meta.githubClientSecret;
} }
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
enableGithubIntegration, enableGithubIntegration: enableGithubIntegration.value,
githubClientId, githubClientId: githubClientId.value,
githubClientSecret, githubClientSecret: githubClientSecret.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -38,6 +38,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "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";
@ -47,20 +49,20 @@ import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let enableTwitterIntegration: boolean = $ref(false); let enableTwitterIntegration: boolean = ref(false);
let enableGithubIntegration: boolean = $ref(false); let enableGithubIntegration: boolean = ref(false);
let enableDiscordIntegration: boolean = $ref(false); let enableDiscordIntegration: boolean = ref(false);
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
enableTwitterIntegration = meta.enableTwitterIntegration; enableTwitterIntegration.value = meta.enableTwitterIntegration;
enableGithubIntegration = meta.enableGithubIntegration; enableGithubIntegration.value = meta.enableGithubIntegration;
enableDiscordIntegration = meta.enableDiscordIntegration; enableDiscordIntegration.value = meta.enableDiscordIntegration;
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.integration, title: i18n.ts.integration,

View File

@ -149,6 +149,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
import FormInput from "@/components/form/input.vue"; import FormInput from "@/components/form/input.vue";
@ -159,58 +161,58 @@ import { fetchInstance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
let useObjectStorage: boolean = $ref(false); let useObjectStorage: boolean = ref(false);
let objectStorageBaseUrl: string | null = $ref(null); let objectStorageBaseUrl: string | null = ref(null);
let objectStorageBucket: string | null = $ref(null); let objectStorageBucket: string | null = ref(null);
let objectStoragePrefix: string | null = $ref(null); let objectStoragePrefix: string | null = ref(null);
let objectStorageEndpoint: string | null = $ref(null); let objectStorageEndpoint: string | null = ref(null);
let objectStorageRegion: string | null = $ref(null); let objectStorageRegion: string | null = ref(null);
let objectStoragePort: number | null = $ref(null); let objectStoragePort: number | null = ref(null);
let objectStorageAccessKey: string | null = $ref(null); let objectStorageAccessKey: string | null = ref(null);
let objectStorageSecretKey: string | null = $ref(null); let objectStorageSecretKey: string | null = ref(null);
let objectStorageUseSSL: boolean = $ref(false); let objectStorageUseSSL: boolean = ref(false);
let objectStorageUseProxy: boolean = $ref(false); let objectStorageUseProxy: boolean = ref(false);
let objectStorageSetPublicRead: boolean = $ref(false); let objectStorageSetPublicRead: boolean = ref(false);
let objectStorageS3ForcePathStyle: boolean = $ref(true); let objectStorageS3ForcePathStyle: boolean = ref(true);
async function init() { async function init() {
const meta = await os.api("admin/meta"); const meta = await os.api("admin/meta");
useObjectStorage = meta.useObjectStorage; useObjectStorage.value = meta.useObjectStorage;
objectStorageBaseUrl = meta.objectStorageBaseUrl; objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
objectStorageBucket = meta.objectStorageBucket; objectStorageBucket.value = meta.objectStorageBucket;
objectStoragePrefix = meta.objectStoragePrefix; objectStoragePrefix.value = meta.objectStoragePrefix;
objectStorageEndpoint = meta.objectStorageEndpoint; objectStorageEndpoint.value = meta.objectStorageEndpoint;
objectStorageRegion = meta.objectStorageRegion; objectStorageRegion.value = meta.objectStorageRegion;
objectStoragePort = meta.objectStoragePort; objectStoragePort.value = meta.objectStoragePort;
objectStorageAccessKey = meta.objectStorageAccessKey; objectStorageAccessKey.value = meta.objectStorageAccessKey;
objectStorageSecretKey = meta.objectStorageSecretKey; objectStorageSecretKey.value = meta.objectStorageSecretKey;
objectStorageUseSSL = meta.objectStorageUseSSL; objectStorageUseSSL.value = meta.objectStorageUseSSL;
objectStorageUseProxy = meta.objectStorageUseProxy; objectStorageUseProxy.value = meta.objectStorageUseProxy;
objectStorageSetPublicRead = meta.objectStorageSetPublicRead; objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle; objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
} }
function save() { function save() {
os.apiWithDialog("admin/update-meta", { os.apiWithDialog("admin/update-meta", {
useObjectStorage, useObjectStorage: useObjectStorage.value,
objectStorageBaseUrl, objectStorageBaseUrl: objectStorageBaseUrl.value,
objectStorageBucket, objectStorageBucket: objectStorageBucket.value,
objectStoragePrefix, objectStoragePrefix: objectStoragePrefix.value,
objectStorageEndpoint, objectStorageEndpoint: objectStorageEndpoint.value,
objectStorageRegion, objectStorageRegion: objectStorageRegion.value,
objectStoragePort, objectStoragePort: objectStoragePort.value,
objectStorageAccessKey, objectStorageAccessKey: objectStorageAccessKey.value,
objectStorageSecretKey, objectStorageSecretKey: objectStorageSecretKey.value,
objectStorageUseSSL, objectStorageUseSSL: objectStorageUseSSL.value,
objectStorageUseProxy, objectStorageUseProxy: objectStorageUseProxy.value,
objectStorageSetPublicRead, objectStorageSetPublicRead: objectStorageSetPublicRead.value,
objectStorageS3ForcePathStyle, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
asFullButton: true, asFullButton: true,
icon: "ph-check ph-bold ph-lg", icon: "ph-check ph-bold ph-lg",
@ -219,7 +221,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.objectStorage, title: i18n.ts.objectStorage,

View File

@ -13,6 +13,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
import {} from "vue"; import {} from "vue";
import FormSuspense from "@/components/form/suspense.vue"; import FormSuspense from "@/components/form/suspense.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -30,7 +32,7 @@ function save() {
}); });
} }
const headerActions = $computed(() => [ const headerActions = computed(() => [
{ {
asFullButton: true, asFullButton: true,
icon: "ph-check ph-bold ph-lg", icon: "ph-check ph-bold ph-lg",
@ -39,7 +41,7 @@ const headerActions = $computed(() => [
}, },
]); ]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.other, title: i18n.ts.other,

View File

@ -8,13 +8,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, shallowRef, ref } from 'vue';
markRaw,
version as vueVersion,
onMounted,
onBeforeUnmount,
nextTick,
} from "vue";
import { Chart } from "chart.js"; import { Chart } from "chart.js";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import gradient from "chartjs-plugin-gradient"; import gradient from "chartjs-plugin-gradient";
@ -27,11 +21,11 @@ import { initChart } from "@/scripts/init-chart";
initChart(); initChart();
const chartEl = $shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
const chartLimit = 7; const chartLimit = 7;
let fetching = $ref(true); let fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip(); const { handler: externalTooltipHandler } = useChartTooltip();
@ -69,7 +63,7 @@ async function renderChart() {
const max = Math.max(...raw.read); const max = Math.max(...raw.read);
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: "bar", type: "bar",
data: { data: {
datasets: [ datasets: [
@ -166,7 +160,7 @@ async function renderChart() {
plugins: [chartVLine(vLineColor)], plugins: [chartVLine(vLineColor)],
}); });
fetching = false; fetching.value = false;
} }
onMounted(async () => { onMounted(async () => {

View File

@ -15,7 +15,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue"; import { onMounted, onUnmounted, ref, shallowRef } from "vue";
import { Chart } from "chart.js"; import { Chart } from "chart.js";
import gradient from "chartjs-plugin-gradient"; import gradient from "chartjs-plugin-gradient";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
@ -33,9 +33,9 @@ import { initChart } from "@/scripts/init-chart";
initChart(); initChart();
const chartLimit = 50; const chartLimit = 50;
const chartEl = $shallowRef<HTMLCanvasElement>(); const chartEl = shallowRef<HTMLCanvasElement>();
const chartEl2 = $shallowRef<HTMLCanvasElement>(); const chartEl2 = shallowRef<HTMLCanvasElement>();
let fetching = $ref(true); let fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip(); const { handler: externalTooltipHandler } = useChartTooltip();
const { handler: externalTooltipHandler2 } = useChartTooltip(); const { handler: externalTooltipHandler2 } = useChartTooltip();
@ -79,7 +79,7 @@ onMounted(async () => {
const succMax = Math.max(...raw.deliverSucceeded); const succMax = Math.max(...raw.deliverSucceeded);
const failMax = Math.max(...raw.deliverFailed); const failMax = Math.max(...raw.deliverFailed);
new Chart(chartEl, { new Chart(chartEl.value, {
type: "line", type: "line",
data: { data: {
datasets: [ datasets: [
@ -187,7 +187,7 @@ onMounted(async () => {
plugins: [chartVLine(vLineColor)], plugins: [chartVLine(vLineColor)],
}); });
new Chart(chartEl2, { new Chart(chartEl2.value, {
type: "bar", type: "bar",
data: { data: {
datasets: [ datasets: [
@ -276,7 +276,7 @@ onMounted(async () => {
plugins: [chartVLine(vLineColor)], plugins: [chartVLine(vLineColor)],
}); });
fetching = false; fetching.value = false;
}); });
</script> </script>

View File

@ -65,13 +65,13 @@ import MkNumberDiff from "@/components/MkNumberDiff.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { useChartTooltip } from "@/scripts/use-chart-tooltip"; import { useChartTooltip } from "@/scripts/use-chart-tooltip";
let topSubInstancesForPie: any = $ref(null); let topSubInstancesForPie: any = ref(null);
let topPubInstancesForPie: any = $ref(null); let topPubInstancesForPie: any = ref(null);
let federationPubActive = $ref<number | null>(null); let federationPubActive = ref<number | null>(null);
let federationPubActiveDiff = $ref<number | null>(null); let federationPubActiveDiff = ref<number | null>(null);
let federationSubActive = $ref<number | null>(null); let federationSubActive = ref<number | null>(null);
let federationSubActiveDiff = $ref<number | null>(null); let federationSubActiveDiff = ref<number | null>(null);
let fetching = $ref(true); let fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip(); const { handler: externalTooltipHandler } = useChartTooltip();
@ -80,13 +80,13 @@ onMounted(async () => {
limit: 2, limit: 2,
span: "day", span: "day",
}); });
federationPubActive = chart.pubActive[0]; federationPubActive.value = chart.pubActive[0];
federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1]; federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
federationSubActive = chart.subActive[0]; federationSubActive.value = chart.subActive[0];
federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
os.apiGet("federation/stats", { limit: 10 }).then((res) => { os.apiGet("federation/stats", { limit: 10 }).then((res) => {
topSubInstancesForPie = res.topSubInstances topSubInstancesForPie.value = res.topSubInstances
.map((x) => ({ .map((x) => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
@ -102,7 +102,7 @@ onMounted(async () => {
value: res.otherFollowersCount, value: res.otherFollowersCount,
}, },
]); ]);
topPubInstancesForPie = res.topPubInstances topPubInstancesForPie.value = res.topPubInstances
.map((x) => ({ .map((x) => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
@ -120,7 +120,7 @@ onMounted(async () => {
]); ]);
}); });
fetching = false; fetching.value = false;
}); });
</script> </script>

Some files were not shown because too many files have changed in this diff Show More