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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted } from "vue";
import { onBeforeUnmount, onMounted, ref } from "vue";
import MkMenu from "@/components/MkMenu.vue";
import type { MenuItem } from "@/types/menu";
import contains from "@/scripts/contains";
@ -27,16 +27,16 @@ const emit = defineEmits<{
(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(() => {
let left = props.ev.pageX + 1, // + 1
top = props.ev.pageY + 1; // + 1
const width = rootEl.offsetWidth;
const height = rootEl.offsetHeight;
const width = rootEl.value.offsetWidth;
const height = rootEl.value.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
@ -54,8 +54,8 @@ onMounted(() => {
left = 0;
}
rootEl.style.top = `${top}px`;
rootEl.style.left = `${left}px`;
rootEl.value.style.top = `${top}px`;
rootEl.value.style.left = `${left}px`;
document.body.addEventListener("mousedown", onMousedown);
});
@ -65,7 +65,8 @@ onBeforeUnmount(() => {
});
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>

View File

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

View File

@ -199,7 +199,7 @@
</template>
<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 MkModal from "@/components/MkModal.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 selectedValue = ref(props.select?.default ?? null);
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
null,
);
const okButtonDisabled = $computed<boolean>(() => {
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
const okButtonDisabled = computed<boolean>(() => {
if (props.input) {
if (props.input.minLength) {
if (
(inputValue.value || inputValue.value === "") &&
(inputValue.value as string).length < props.input.minLength
) {
disabledReason = "charactersBelow";
disabledReason.value = "charactersBelow";
return true;
}
}
@ -300,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
inputValue.value &&
(inputValue.value as string).length > props.input.maxLength
) {
disabledReason = "charactersExceeded";
disabledReason.value = "charactersExceeded";
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,6 +11,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { instanceName } from "@/config";
import { instance as Instance } from "@/instance";
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
const instance = props.instance ?? {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
import { inject, onMounted, onUnmounted } from "vue";
import { inject, onMounted, onUnmounted, ref } from "vue";
import { deviceKind } from "@/scripts/device-kind";
import { ui } from "@/config";
@ -25,18 +25,18 @@ const props = withDefaults(
);
let ro: ResizeObserver,
root = $ref<HTMLElement>(),
content = $ref<HTMLElement>(),
margin = $ref(0);
root = ref<HTMLElement>(),
content = ref<HTMLElement>(),
margin = ref(0);
const shouldSpacerMin = inject("shouldSpacerMin", false);
const adjust = (rect: { width: number; height: number }) => {
if (shouldSpacerMin || deviceKind === "smartphone") {
margin = props.marginMin;
margin.value = props.marginMin;
return;
}
if (ui === "classic") {
margin = 12;
margin.value = 12;
return;
}
@ -44,9 +44,9 @@ const adjust = (rect: { width: number; height: number }) => {
rect.width > (props.contentMax ?? 0) ||
(rect.width > 360 && window.innerWidth > 400)
) {
margin = props.marginMax;
margin.value = props.marginMax;
} else {
margin = props.marginMin;
margin.value = props.marginMin;
}
};
@ -59,14 +59,14 @@ onMounted(() => {
});
*/
adjust({
width: root!.offsetWidth,
height: root!.offsetHeight,
width: root.value!.offsetWidth,
height: root.value!.offsetHeight,
});
});
ro.observe(root!);
ro.observe(root.value!);
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 { inject, onMounted, onUnmounted, provide, ref, watch } from "vue";
const headerEl = $ref<HTMLElement>();
const bodyEl = $ref<HTMLElement>();
const headerEl = ref<HTMLElement>();
const bodyEl = ref<HTMLElement>();
let headerHeight = $ref<string | undefined>(),
childStickyTop = $ref(0);
let headerHeight = ref<string | undefined>(),
childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
provide(CURRENT_STICKY_TOP, $$(childStickyTop));
provide(CURRENT_STICKY_TOP, childStickyTop);
const calc = () => {
childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
headerHeight = headerEl.offsetHeight.toString();
childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
headerHeight.value = headerEl.value.offsetHeight.toString();
};
const observer = new ResizeObserver(() => {
@ -44,16 +44,19 @@ onMounted(() => {
watch(parentStickyTop, calc);
watch(
$$(childStickyTop),
childStickyTop,
() => {
bodyEl.style.setProperty("--stickyTop", `${childStickyTop}px`);
bodyEl.value.style.setProperty(
"--stickyTop",
`${childStickyTop.value}px`,
);
},
{
immediate: true,
},
);
observer.observe(headerEl);
observer.observe(headerEl.value);
});
onUnmounted(() => {

View File

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

View File

@ -18,7 +18,7 @@
</template>
<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 { defaultStore } from "@/store";
@ -48,18 +48,18 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
}
const current = resolveNested(router.current)!;
let currentPageComponent = $shallowRef(current.route.component),
currentPageProps = $ref(current.props),
key = $ref(
let currentPageComponent = shallowRef(current.route.component),
currentPageProps = ref(current.props),
key = ref(
current.route.path + JSON.stringify(Object.fromEntries(current.props)),
);
function onChange({ resolved, key: newKey }) {
const current = resolveNested(resolved);
if (current == null) return;
currentPageComponent = current.route.component;
currentPageProps = current.props;
key =
currentPageComponent.value = current.route.component;
currentPageProps.value = current.props;
key.value =
current.route.path + JSON.stringify(Object.fromEntries(current.props));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import { ref, watch, computed } from "vue";
import FormTextarea from "@/components/form/textarea.vue";
import FormInfo from "@/components/MkInfo.vue";
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({
title: i18n.ts.customCss,

View File

@ -34,6 +34,8 @@
</template>
<script lang="ts" setup>
import { computed } from "vue";
import {} from "vue";
import FormSuspense from "@/components/form/suspense.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),
);
const headerActions = $computed(() => []);
const headerActions = computed(() => []);
const headerTabs = $computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.database,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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