Merge branch 'refactor/no-reactivity' into 'develop'
refactor: ♻️ No Vue Reactivity See merge request firefish/firefish!10560
This commit is contained in:
commit
3d8fb7b76f
247 changed files with 3327 additions and 3021 deletions
|
@ -65,6 +65,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkSwitch from "@/components/form/switch.vue";
|
import MkSwitch from "@/components/form/switch.vue";
|
||||||
import MkKeyValue from "@/components/MkKeyValue.vue";
|
import MkKeyValue from "@/components/MkKeyValue.vue";
|
||||||
|
@ -79,11 +81,11 @@ const emit = defineEmits<{
|
||||||
(ev: "resolved", reportId: string): void;
|
(ev: "resolved", reportId: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const forward = $ref(props.report.forwarded);
|
const forward = ref(props.report.forwarded);
|
||||||
|
|
||||||
function resolve() {
|
function resolve() {
|
||||||
os.apiWithDialog("admin/resolve-abuse-user-report", {
|
os.apiWithDialog("admin/resolve-abuse-user-report", {
|
||||||
forward,
|
forward: forward.value,
|
||||||
reportId: props.report.id,
|
reportId: props.report.id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
emit("resolved", props.report.id);
|
emit("resolved", props.report.id);
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onBeforeUnmount, onMounted } from "vue";
|
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import { globalEvents } from "@/events.js";
|
import { globalEvents } from "@/events.js";
|
||||||
|
|
||||||
|
@ -167,19 +167,19 @@ const texts = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let enabled = true,
|
let enabled = true,
|
||||||
majorGraduationColor = $ref<string>(),
|
majorGraduationColor = ref<string>(),
|
||||||
// let minorGraduationColor = $ref<string>();
|
// let minorGraduationColor = $ref<string>();
|
||||||
sHandColor = $ref<string>(),
|
sHandColor = ref<string>(),
|
||||||
mHandColor = $ref<string>(),
|
mHandColor = ref<string>(),
|
||||||
hHandColor = $ref<string>(),
|
hHandColor = ref<string>(),
|
||||||
nowColor = $ref<string>(),
|
nowColor = ref<string>(),
|
||||||
h = $ref<number>(0),
|
h = ref<number>(0),
|
||||||
m = $ref<number>(0),
|
m = ref<number>(0),
|
||||||
s = $ref<number>(0),
|
s = ref<number>(0),
|
||||||
hAngle = $ref<number>(0),
|
hAngle = ref<number>(0),
|
||||||
mAngle = $ref<number>(0),
|
mAngle = ref<number>(0),
|
||||||
sAngle = $ref<number>(0),
|
sAngle = ref<number>(0),
|
||||||
disableSAnimate = $ref(false),
|
disableSAnimate = ref(false),
|
||||||
sOneRound = false;
|
sOneRound = false;
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
@ -187,29 +187,31 @@ function tick() {
|
||||||
now.setMinutes(
|
now.setMinutes(
|
||||||
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
|
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
|
||||||
);
|
);
|
||||||
s = now.getSeconds();
|
s.value = now.getSeconds();
|
||||||
m = now.getMinutes();
|
m.value = now.getMinutes();
|
||||||
h = now.getHours();
|
h.value = now.getHours();
|
||||||
hAngle =
|
hAngle.value =
|
||||||
(Math.PI * ((h % (props.twentyfour ? 24 : 12)) + (m + s / 60) / 60)) /
|
(Math.PI *
|
||||||
|
((h.value % (props.twentyfour ? 24 : 12)) +
|
||||||
|
(m.value + s.value / 60) / 60)) /
|
||||||
(props.twentyfour ? 12 : 6);
|
(props.twentyfour ? 12 : 6);
|
||||||
mAngle = (Math.PI * (m + s / 60)) / 30;
|
mAngle.value = (Math.PI * (m.value + s.value / 60)) / 30;
|
||||||
if (sOneRound) {
|
if (sOneRound) {
|
||||||
// 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
// 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||||
sAngle = (Math.PI * 60) / 30;
|
sAngle.value = (Math.PI * 60) / 30;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
disableSAnimate = true;
|
disableSAnimate.value = true;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
sAngle = 0;
|
sAngle.value = 0;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
disableSAnimate = false;
|
disableSAnimate.value = false;
|
||||||
}, 100);
|
}, 100);
|
||||||
}, 100);
|
}, 100);
|
||||||
}, 700);
|
}, 700);
|
||||||
} else {
|
} else {
|
||||||
sAngle = (Math.PI * s) / 30;
|
sAngle.value = (Math.PI * s.value) / 30;
|
||||||
}
|
}
|
||||||
sOneRound = s === 59;
|
sOneRound = s.value === 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
|
@ -220,16 +222,16 @@ function calcColors() {
|
||||||
const accent = tinycolor(
|
const accent = tinycolor(
|
||||||
computedStyle.getPropertyValue("--accent"),
|
computedStyle.getPropertyValue("--accent"),
|
||||||
).toHexString();
|
).toHexString();
|
||||||
majorGraduationColor = dark
|
majorGraduationColor.value = dark
|
||||||
? "rgba(255, 255, 255, 0.3)"
|
? "rgba(255, 255, 255, 0.3)"
|
||||||
: "rgba(0, 0, 0, 0.3)";
|
: "rgba(0, 0, 0, 0.3)";
|
||||||
// minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
// minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||||
sHandColor = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
sHandColor.value = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
||||||
mHandColor = tinycolor(
|
mHandColor.value = tinycolor(
|
||||||
computedStyle.getPropertyValue("--fg"),
|
computedStyle.getPropertyValue("--fg"),
|
||||||
).toHexString();
|
).toHexString();
|
||||||
hHandColor = accent;
|
hHandColor.value = accent;
|
||||||
nowColor = accent;
|
nowColor.value = accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcColors();
|
calcColors();
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted } from "vue";
|
import { nextTick, onMounted, ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type?: "button" | "submit" | "reset";
|
type?: "button" | "submit" | "reset";
|
||||||
|
@ -49,13 +49,13 @@ const emit = defineEmits<{
|
||||||
(ev: "click", payload: MouseEvent): void;
|
(ev: "click", payload: MouseEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const el = $ref<HTMLElement | null>(null);
|
const el = ref<HTMLElement | null>(null);
|
||||||
const ripples = $ref<HTMLElement | null>(null);
|
const ripples = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.autofocus) {
|
if (props.autofocus) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
el!.focus();
|
el.value!.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px";
|
ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px";
|
||||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px";
|
ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px";
|
||||||
|
|
||||||
ripples!.appendChild(ripple);
|
ripples.value!.appendChild(ripple);
|
||||||
|
|
||||||
const circleCenterX = evt.clientX - rect.left;
|
const circleCenterX = evt.clientX - rect.left;
|
||||||
const circleCenterY = evt.clientY - rect.top;
|
const circleCenterY = evt.clientY - rect.top;
|
||||||
|
@ -101,7 +101,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
ripple.style.opacity = "0";
|
ripple.style.opacity = "0";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
if (ripples) ripples.removeChild(ripple);
|
if (ripples.value) ripples.value.removeChild(ripple);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
|
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -20,7 +22,7 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted } from "vue";
|
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import MkMenu from "@/components/MkMenu.vue";
|
import MkMenu from "@/components/MkMenu.vue";
|
||||||
import type { MenuItem } from "@/types/menu";
|
import type { MenuItem } from "@/types/menu";
|
||||||
import contains from "@/scripts/contains";
|
import contains from "@/scripts/contains";
|
||||||
|
@ -27,16 +27,16 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const rootEl = $ref<HTMLDivElement>();
|
const rootEl = ref<HTMLDivElement>();
|
||||||
|
|
||||||
const zIndex = $ref<number>(os.claimZIndex("high"));
|
const zIndex = ref<number>(os.claimZIndex("high"));
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let left = props.ev.pageX + 1, // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let left = props.ev.pageX + 1, // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
|
|
||||||
const width = rootEl.offsetWidth;
|
const width = rootEl.value.offsetWidth;
|
||||||
const height = rootEl.offsetHeight;
|
const height = rootEl.value.offsetHeight;
|
||||||
|
|
||||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||||
left = window.innerWidth - width + window.pageXOffset;
|
left = window.innerWidth - width + window.pageXOffset;
|
||||||
|
@ -54,8 +54,8 @@ onMounted(() => {
|
||||||
left = 0;
|
left = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootEl.style.top = `${top}px`;
|
rootEl.value.style.top = `${top}px`;
|
||||||
rootEl.style.left = `${left}px`;
|
rootEl.value.style.left = `${left}px`;
|
||||||
|
|
||||||
document.body.addEventListener("mousedown", onMousedown);
|
document.body.addEventListener("mousedown", onMousedown);
|
||||||
});
|
});
|
||||||
|
@ -65,7 +65,8 @@ onBeforeUnmount(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMousedown(evt: Event) {
|
function onMousedown(evt: Event) {
|
||||||
if (!contains(rootEl, evt.target) && rootEl !== evt.target) emit("closed");
|
if (!contains(rootEl.value, evt.target) && rootEl.value !== evt.target)
|
||||||
|
emit("closed");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
import Cropper from "cropperjs";
|
import Cropper from "cropperjs";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
@ -62,10 +62,10 @@ const props = defineProps<{
|
||||||
const imgUrl = `${url}/proxy/image.webp?${query({
|
const imgUrl = `${url}/proxy/image.webp?${query({
|
||||||
url: props.file.url,
|
url: props.file.url,
|
||||||
})}`;
|
})}`;
|
||||||
const dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
const dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||||
const imgEl = $ref<HTMLImageElement>();
|
const imgEl = ref<HTMLImageElement>();
|
||||||
let cropper: Cropper | null = null,
|
let cropper: Cropper | null = null,
|
||||||
loading = $ref(true);
|
loading = ref(true);
|
||||||
|
|
||||||
const ok = async () => {
|
const ok = async () => {
|
||||||
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
||||||
|
@ -96,16 +96,16 @@ const ok = async () => {
|
||||||
const f = await promise;
|
const f = await promise;
|
||||||
|
|
||||||
emit("ok", f);
|
emit("ok", f);
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onImageLoad = () => {
|
const onImageLoad = () => {
|
||||||
loading = false;
|
loading.value = false;
|
||||||
|
|
||||||
if (cropper) {
|
if (cropper) {
|
||||||
cropper.getCropperImage()!.$center("contain");
|
cropper.getCropperImage()!.$center("contain");
|
||||||
|
@ -114,7 +114,7 @@ const onImageLoad = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
cropper = new Cropper(imgEl, {});
|
cropper = new Cropper(imgEl.value, {});
|
||||||
|
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
|
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from "vue";
|
||||||
import * as Acct from "firefish-js/built/acct";
|
import * as Acct from "firefish-js/built/acct";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -281,17 +281,15 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
||||||
const selectedValue = ref(props.select?.default ?? null);
|
const selectedValue = ref(props.select?.default ?? null);
|
||||||
|
|
||||||
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
|
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
|
||||||
null,
|
const okButtonDisabled = computed<boolean>(() => {
|
||||||
);
|
|
||||||
const okButtonDisabled = $computed<boolean>(() => {
|
|
||||||
if (props.input) {
|
if (props.input) {
|
||||||
if (props.input.minLength) {
|
if (props.input.minLength) {
|
||||||
if (
|
if (
|
||||||
(inputValue.value || inputValue.value === "") &&
|
(inputValue.value || inputValue.value === "") &&
|
||||||
(inputValue.value as string).length < props.input.minLength
|
(inputValue.value as string).length < props.input.minLength
|
||||||
) {
|
) {
|
||||||
disabledReason = "charactersBelow";
|
disabledReason.value = "charactersBelow";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
|
||||||
inputValue.value &&
|
inputValue.value &&
|
||||||
(inputValue.value as string).length > props.input.maxLength
|
(inputValue.value as string).length > props.input.maxLength
|
||||||
) {
|
) {
|
||||||
disabledReason = "charactersExceeded";
|
disabledReason.value = "charactersExceeded";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onBeforeUnmount, onMounted } from "vue";
|
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import type * as Misskey from "firefish-js";
|
import type * as Misskey from "firefish-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { stream } from "@/stream";
|
import { stream } from "@/stream";
|
||||||
|
@ -88,13 +88,13 @@ const props = withDefaults(
|
||||||
|
|
||||||
const isBlocking = computed(() => props.user.isBlocking);
|
const isBlocking = computed(() => props.user.isBlocking);
|
||||||
|
|
||||||
let state = $ref(i18n.ts.processing);
|
let state = ref(i18n.ts.processing);
|
||||||
|
|
||||||
let isFollowing = $ref(props.user.isFollowing);
|
let isFollowing = ref(props.user.isFollowing);
|
||||||
let hasPendingFollowRequestFromYou = $ref(
|
let hasPendingFollowRequestFromYou = ref(
|
||||||
props.user.hasPendingFollowRequestFromYou,
|
props.user.hasPendingFollowRequestFromYou,
|
||||||
);
|
);
|
||||||
let wait = $ref(false);
|
let wait = ref(false);
|
||||||
const connection = stream.useChannel("main");
|
const connection = stream.useChannel("main");
|
||||||
|
|
||||||
if (props.user.isFollowing == null) {
|
if (props.user.isFollowing == null) {
|
||||||
|
@ -105,13 +105,14 @@ if (props.user.isFollowing == null) {
|
||||||
|
|
||||||
function onFollowChange(user: Misskey.entities.UserDetailed) {
|
function onFollowChange(user: Misskey.entities.UserDetailed) {
|
||||||
if (user.id === props.user.id) {
|
if (user.id === props.user.id) {
|
||||||
isFollowing = user.isFollowing;
|
isFollowing.value = user.isFollowing;
|
||||||
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
hasPendingFollowRequestFromYou.value =
|
||||||
|
user.hasPendingFollowRequestFromYou;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onClick() {
|
async function onClick() {
|
||||||
wait = true;
|
wait.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isBlocking.value) {
|
if (isBlocking.value) {
|
||||||
|
@ -130,7 +131,7 @@ async function onClick() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
emit("refresh");
|
emit("refresh");
|
||||||
} else if (isFollowing) {
|
} else if (isFollowing.value) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
text: i18n.t("unfollowConfirm", {
|
text: i18n.t("unfollowConfirm", {
|
||||||
|
@ -144,22 +145,22 @@ async function onClick() {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (hasPendingFollowRequestFromYou) {
|
if (hasPendingFollowRequestFromYou.value) {
|
||||||
await os.api("following/requests/cancel", {
|
await os.api("following/requests/cancel", {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
hasPendingFollowRequestFromYou = false;
|
hasPendingFollowRequestFromYou.value = false;
|
||||||
} else {
|
} else {
|
||||||
await os.api("following/create", {
|
await os.api("following/create", {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
hasPendingFollowRequestFromYou = true;
|
hasPendingFollowRequestFromYou.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
wait = false;
|
wait.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -75,20 +77,20 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog: InstanceType<typeof XModalWindow> = $ref();
|
let dialog: InstanceType<typeof XModalWindow> = ref();
|
||||||
|
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let email = $ref("");
|
let email = ref("");
|
||||||
let processing = $ref(false);
|
let processing = ref(false);
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
processing = true;
|
processing.value = true;
|
||||||
await os.apiWithDialog("request-reset-password", {
|
await os.apiWithDialog("request-reset-password", {
|
||||||
username,
|
username: username.value,
|
||||||
email,
|
email: email.value,
|
||||||
});
|
});
|
||||||
emit("done");
|
emit("done");
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, nextTick, watch } from "vue";
|
import { onMounted, nextTick, watch, shallowRef, ref } from "vue";
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -23,11 +23,11 @@ const props = defineProps<{
|
||||||
src: string;
|
src: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const rootEl = $shallowRef<HTMLDivElement>(null);
|
const rootEl = shallowRef<HTMLDivElement>(null);
|
||||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart = null;
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||||
position: "middle",
|
position: "middle",
|
||||||
|
@ -43,8 +43,8 @@ async function renderChart() {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const wide = rootEl.offsetWidth > 700;
|
const wide = rootEl.value.offsetWidth > 700;
|
||||||
const narrow = rootEl.offsetWidth < 400;
|
const narrow = rootEl.value.offsetWidth < 400;
|
||||||
|
|
||||||
const weeks = wide ? 50 : narrow ? 10 : 25;
|
const weeks = wide ? 50 : narrow ? 10 : 25;
|
||||||
const chartLimit = 7 * weeks;
|
const chartLimit = 7 * weeks;
|
||||||
|
@ -113,7 +113,7 @@ async function renderChart() {
|
||||||
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
|
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ async function renderChart() {
|
||||||
|
|
||||||
const marginEachCell = 4;
|
const marginEachCell = 4;
|
||||||
|
|
||||||
chartInstance = new Chart(chartEl, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
type: "matrix",
|
type: "matrix",
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
|
@ -247,7 +247,7 @@ async function renderChart() {
|
||||||
watch(
|
watch(
|
||||||
() => props.src,
|
() => props.src,
|
||||||
() => {
|
() => {
|
||||||
fetching = true;
|
fetching.value = true;
|
||||||
renderChart();
|
renderChart();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
import bytes from "@/filters/bytes";
|
import bytes from "@/filters/bytes";
|
||||||
|
@ -43,7 +45,7 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
const modal = ref<InstanceType<typeof MkModal>>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { decodeBlurHash } from "fast-blurhash";
|
import { decodeBlurHash } from "fast-blurhash";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
@ -48,20 +48,20 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const canvas = $ref<HTMLCanvasElement>();
|
const canvas = ref<HTMLCanvasElement>();
|
||||||
let loaded = $ref(false);
|
let loaded = ref(false);
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
if (props.hash == null || canvas == null) return;
|
if (props.hash == null || canvas.value == null) return;
|
||||||
const pixels = decodeBlurHash(props.hash, props.size, props.size);
|
const pixels = decodeBlurHash(props.hash, props.size, props.size);
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.value.getContext("2d");
|
||||||
const imageData = ctx!.createImageData(props.size, props.size);
|
const imageData = ctx!.createImageData(props.size, props.size);
|
||||||
imageData.data.set(pixels);
|
imageData.data.set(pixels);
|
||||||
ctx!.putImageData(imageData, 0, 0);
|
ctx!.putImageData(imageData, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import * as firefish from "firefish-js";
|
import * as firefish from "firefish-js";
|
||||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -33,7 +35,7 @@ const props = defineProps<{
|
||||||
instance: firefish.entities.Instance;
|
instance: firefish.entities.Instance;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let chartValues = $ref<number[] | null>(null);
|
let chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
os.apiGet("charts/instance", {
|
os.apiGet("charts/instance", {
|
||||||
host: props.instance.host,
|
host: props.instance.host,
|
||||||
|
@ -42,7 +44,7 @@ os.apiGet("charts/instance", {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||||
res.requests.received.splice(0, 1);
|
res.requests.received.splice(0, 1);
|
||||||
chartValues = res.requests.received;
|
chartValues.value = res.requests.received;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getInstanceIcon(instance): string {
|
function getInstanceIcon(instance): string {
|
||||||
|
|
|
@ -56,6 +56,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -68,28 +70,28 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let hostname = $ref("");
|
let hostname = ref("");
|
||||||
let instances: Instance[] = $ref([]);
|
let instances: Instance[] = ref([]);
|
||||||
let selected: Instance | null = $ref(null);
|
let selected: Instance | null = ref(null);
|
||||||
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
let searchOrderLatch = 0;
|
let searchOrderLatch = 0;
|
||||||
const search = () => {
|
const search = () => {
|
||||||
if (hostname === "") {
|
if (hostname.value === "") {
|
||||||
instances = [];
|
instances.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchId = ++searchOrderLatch;
|
const searchId = ++searchOrderLatch;
|
||||||
os.api("federation/instances", {
|
os.api("federation/instances", {
|
||||||
host: hostname,
|
host: hostname.value,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
blocked: false,
|
blocked: false,
|
||||||
suspended: false,
|
suspended: false,
|
||||||
sort: "+pubSub",
|
sort: "+pubSub",
|
||||||
}).then((_instances) => {
|
}).then((_instances) => {
|
||||||
if (searchId !== searchOrderLatch) return;
|
if (searchId !== searchOrderLatch) return;
|
||||||
instances = _instances.map(
|
instances.value = _instances.map(
|
||||||
(x) =>
|
(x) =>
|
||||||
({
|
({
|
||||||
id: x.id,
|
id: x.id,
|
||||||
|
@ -101,14 +103,14 @@ const search = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ok = () => {
|
const ok = () => {
|
||||||
if (selected == null) return;
|
if (selected.value == null) return;
|
||||||
emit("ok", selected);
|
emit("ok", selected.value);
|
||||||
dialogEl?.close();
|
dialogEl.value?.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl?.close();
|
dialogEl.value?.close();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref, shallowRef } from "vue";
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import MkSelect from "@/components/form/select.vue";
|
import MkSelect from "@/components/form/select.vue";
|
||||||
import MkChart from "@/components/MkChart.vue";
|
import MkChart from "@/components/MkChart.vue";
|
||||||
|
@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const chartLimit = 500;
|
const chartLimit = 500;
|
||||||
let chartSpan = $ref<"hour" | "day">("hour");
|
let chartSpan = ref<"hour" | "day">("hour");
|
||||||
let chartSrc = $ref("active-users");
|
let chartSrc = ref("active-users");
|
||||||
let heatmapSrc = $ref("active-users");
|
let heatmapSrc = ref("active-users");
|
||||||
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
let subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
let pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
|
|
||||||
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
||||||
position: "middle",
|
position: "middle",
|
||||||
|
@ -189,7 +189,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
|
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
|
||||||
createDoughnut(
|
createDoughnut(
|
||||||
subDoughnutEl,
|
subDoughnutEl.value,
|
||||||
externalTooltipHandler1,
|
externalTooltipHandler1,
|
||||||
fedStats.topSubInstances
|
fedStats.topSubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
|
@ -210,7 +210,7 @@ onMounted(() => {
|
||||||
);
|
);
|
||||||
|
|
||||||
createDoughnut(
|
createDoughnut(
|
||||||
pubDoughnutEl,
|
pubDoughnutEl.value,
|
||||||
externalTooltipHandler2,
|
externalTooltipHandler2,
|
||||||
fedStats.topPubInstances
|
fedStats.topPubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import { instanceName } from "@/config";
|
import { instanceName } from "@/config";
|
||||||
import { instance as Instance } from "@/instance";
|
import { instance as Instance } from "@/instance";
|
||||||
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
||||||
|
@ -24,7 +26,7 @@ const props = defineProps<{
|
||||||
};
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let ticker = $ref<HTMLElement | null>(null);
|
let ticker = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
// if no instance data is given, this is for the local instance
|
// if no instance data is given, this is for the local instance
|
||||||
const instance = props.instance ?? {
|
const instance = props.instance ?? {
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import { navbarItemDef } from "@/navbar";
|
import { navbarItemDef } from "@/navbar";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -89,7 +91,7 @@ const preferedModalType =
|
||||||
? "drawer"
|
? "drawer"
|
||||||
: "dialog";
|
: "dialog";
|
||||||
|
|
||||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
const modal = ref<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const menu = defaultStore.state.menu;
|
const menu = defaultStore.state.menu;
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ const items = Object.keys(navbarItemDef)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref } from "vue";
|
||||||
import { url as local } from "@/config";
|
import { url as local } from "@/config";
|
||||||
import { useTooltip } from "@/scripts/use-tooltip";
|
import { useTooltip } from "@/scripts/use-tooltip";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -35,9 +35,9 @@ const self = props.url.startsWith(local);
|
||||||
const attr = self ? "to" : "href";
|
const attr = self ? "to" : "href";
|
||||||
const target = self ? null : "_blank";
|
const target = self ? null : "_blank";
|
||||||
|
|
||||||
const el = $ref();
|
const el = ref();
|
||||||
|
|
||||||
useTooltip($$(el), (showing) => {
|
useTooltip(el, (showing) => {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkUrlPreviewPopup.vue"),
|
() => import("@/components/MkUrlPreviewPopup.vue"),
|
||||||
|
@ -45,7 +45,7 @@ useTooltip($$(el), (showing) => {
|
||||||
{
|
{
|
||||||
showing,
|
showing,
|
||||||
url: props.url,
|
url: props.url,
|
||||||
source: el,
|
source: el.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed",
|
"closed",
|
||||||
|
|
|
@ -104,7 +104,7 @@ const props = defineProps<{
|
||||||
raw?: boolean;
|
raw?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let hide = $ref(true);
|
let hide = ref(true);
|
||||||
|
|
||||||
const plyr = ref();
|
const plyr = ref();
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ function captionPopup() {
|
||||||
watch(
|
watch(
|
||||||
() => props.media,
|
() => props.media,
|
||||||
() => {
|
() => {
|
||||||
hide =
|
hide.value =
|
||||||
defaultStore.state.nsfw === "force"
|
defaultStore.state.nsfw === "force"
|
||||||
? true
|
? true
|
||||||
: props.media.isSensitive &&
|
: props.media.isSensitive &&
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import VuePlyr from "vue-plyr";
|
import VuePlyr from "vue-plyr";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
import { ColdDeviceStorage } from "@/store";
|
import { ColdDeviceStorage } from "@/store";
|
||||||
|
@ -70,15 +70,17 @@ const props = withDefaults(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
const audioEl = $ref<HTMLAudioElement | null>();
|
const audioEl = ref<HTMLAudioElement | null>();
|
||||||
let hide = $ref(true);
|
let hide = ref(true);
|
||||||
|
|
||||||
function volumechange() {
|
function volumechange() {
|
||||||
if (audioEl) ColdDeviceStorage.set("mediaVolume", audioEl.volume);
|
if (audioEl.value)
|
||||||
|
ColdDeviceStorage.set("mediaVolume", audioEl.value.volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (audioEl) audioEl.volume = ColdDeviceStorage.get("mediaVolume");
|
if (audioEl.value)
|
||||||
|
audioEl.value.volume = ColdDeviceStorage.get("mediaVolume");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -233,13 +233,13 @@ const emit = defineEmits<{
|
||||||
(ev: "close", actioned?: boolean): void;
|
(ev: "close", actioned?: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let itemsEl = $ref<HTMLDivElement>();
|
let itemsEl = ref<HTMLDivElement>();
|
||||||
|
|
||||||
let items2: InnerMenuItem[] = $ref([]);
|
let items2: InnerMenuItem[] = ref([]);
|
||||||
|
|
||||||
let child = $ref<InstanceType<typeof XChild>>();
|
let child = ref<InstanceType<typeof XChild>>();
|
||||||
|
|
||||||
let childShowingItem = $ref<MenuItem | null>();
|
let childShowingItem = ref<MenuItem | null>();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.items,
|
() => props.items,
|
||||||
|
@ -255,24 +255,24 @@ watch(
|
||||||
// if item is Promise
|
// if item is Promise
|
||||||
items[i] = { type: "pending" };
|
items[i] = { type: "pending" };
|
||||||
item.then((actualItem) => {
|
item.then((actualItem) => {
|
||||||
items2[i] = actualItem;
|
items2.value[i] = actualItem;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items2 = items as InnerMenuItem[];
|
items2.value = items as InnerMenuItem[];
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let childMenu = $ref<MenuItem[] | null>();
|
let childMenu = ref<MenuItem[] | null>();
|
||||||
let childTarget = $ref<HTMLElement | null>();
|
let childTarget = ref<HTMLElement | null>();
|
||||||
|
|
||||||
function closeChild() {
|
function closeChild() {
|
||||||
childMenu = null;
|
childMenu.value = null;
|
||||||
childShowingItem = null;
|
childShowingItem.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function childActioned() {
|
function childActioned() {
|
||||||
|
@ -282,11 +282,12 @@ function childActioned() {
|
||||||
|
|
||||||
function onGlobalMousedown(event: MouseEvent) {
|
function onGlobalMousedown(event: MouseEvent) {
|
||||||
if (
|
if (
|
||||||
childTarget &&
|
childTarget.value &&
|
||||||
(event.target === childTarget || childTarget.contains(event.target))
|
(event.target === childTarget.value ||
|
||||||
|
childTarget.value.contains(event.target))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
if (child && child.checkHit(event)) return;
|
if (child.value && child.value.checkHit(event)) return;
|
||||||
closeChild();
|
closeChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,9 +306,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
|
||||||
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
childTarget = ev.currentTarget ?? ev.target;
|
childTarget.value = ev.currentTarget ?? ev.target;
|
||||||
childMenu = item.children;
|
childMenu.value = item.children;
|
||||||
childShowingItem = item;
|
childShowingItem.value = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from "vue";
|
import { watch, ref } from "vue";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import { useInterval } from "@/scripts/use-interval";
|
import { useInterval } from "@/scripts/use-interval";
|
||||||
|
@ -37,10 +37,10 @@ const props = defineProps<{
|
||||||
const viewBoxX = 50;
|
const viewBoxX = 50;
|
||||||
const viewBoxY = 50;
|
const viewBoxY = 50;
|
||||||
const gradientId = uuid();
|
const gradientId = uuid();
|
||||||
let polylinePoints = $ref("");
|
let polylinePoints = ref("");
|
||||||
let polygonPoints = $ref("");
|
let polygonPoints = ref("");
|
||||||
let headX = $ref<number | null>(null);
|
let headX = ref<number | null>(null);
|
||||||
let headY = $ref<number | null>(null);
|
let headY = ref<number | null>(null);
|
||||||
const accent = tinycolor(
|
const accent = tinycolor(
|
||||||
getComputedStyle(document.documentElement).getPropertyValue("--accent"),
|
getComputedStyle(document.documentElement).getPropertyValue("--accent"),
|
||||||
);
|
);
|
||||||
|
@ -55,12 +55,14 @@ function draw(): void {
|
||||||
(1 - n / peak) * viewBoxY,
|
(1 - n / peak) * viewBoxY,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
polylinePoints = _polylinePoints.map((xy) => `${xy[0]},${xy[1]}`).join(" ");
|
polylinePoints.value = _polylinePoints
|
||||||
|
.map((xy) => `${xy[0]},${xy[1]}`)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
polygonPoints = `0,${viewBoxY} ${polylinePoints} ${viewBoxX},${viewBoxY}`;
|
polygonPoints.value = `0,${viewBoxY} ${polylinePoints.value} ${viewBoxX},${viewBoxY}`;
|
||||||
|
|
||||||
headX = _polylinePoints[_polylinePoints.length - 1][0];
|
headX.value = _polylinePoints[_polylinePoints.length - 1][0];
|
||||||
headY = _polylinePoints[_polylinePoints.length - 1][1];
|
headY.value = _polylinePoints[_polylinePoints.length - 1][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.src, draw, { immediate: true });
|
watch(() => props.src, draw, { immediate: true });
|
||||||
|
|
|
@ -77,7 +77,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, watch, provide, onUnmounted } from "vue";
|
import {
|
||||||
|
nextTick,
|
||||||
|
onMounted,
|
||||||
|
watch,
|
||||||
|
provide,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
shallowRef,
|
||||||
|
computed,
|
||||||
|
} from "vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { isTouchUsing } from "@/scripts/touch";
|
import { isTouchUsing } from "@/scripts/touch";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -130,14 +139,14 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
provide("modal", true);
|
provide("modal", true);
|
||||||
|
|
||||||
let maxHeight = $ref<number>();
|
let maxHeight = ref<number>();
|
||||||
let fixed = $ref(false);
|
let fixed = ref(false);
|
||||||
let transformOrigin = $ref("center");
|
let transformOrigin = ref("center");
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
let content = $shallowRef<HTMLElement>();
|
let content = shallowRef<HTMLElement>();
|
||||||
const zIndex = os.claimZIndex(props.zPriority);
|
const zIndex = os.claimZIndex(props.zPriority);
|
||||||
let useSendAnime = $ref(false);
|
let useSendAnime = ref(false);
|
||||||
const type = $computed<ModalTypes>(() => {
|
const type = computed<ModalTypes>(() => {
|
||||||
if (props.preferType === "auto") {
|
if (props.preferType === "auto") {
|
||||||
if (
|
if (
|
||||||
!defaultStore.state.disableDrawer &&
|
!defaultStore.state.disableDrawer &&
|
||||||
|
@ -152,28 +161,28 @@ const type = $computed<ModalTypes>(() => {
|
||||||
return props.preferType!;
|
return props.preferType!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const isEnableBgTransparent = $computed(
|
const isEnableBgTransparent = computed(
|
||||||
() => props.transparentBg && type === "popup",
|
() => props.transparentBg && type.value === "popup",
|
||||||
);
|
);
|
||||||
let transitionName = $computed(() =>
|
let transitionName = computed(() =>
|
||||||
defaultStore.state.animation
|
defaultStore.state.animation
|
||||||
? useSendAnime
|
? useSendAnime.value
|
||||||
? "send"
|
? "send"
|
||||||
: type === "drawer"
|
: type.value === "drawer"
|
||||||
? "modal-drawer"
|
? "modal-drawer"
|
||||||
: type === "popup"
|
: type.value === "popup"
|
||||||
? "modal-popup"
|
? "modal-popup"
|
||||||
: "modal"
|
: "modal"
|
||||||
: "",
|
: "",
|
||||||
);
|
);
|
||||||
let transitionDuration = $computed(() =>
|
let transitionDuration = computed(() =>
|
||||||
transitionName === "send"
|
transitionName.value === "send"
|
||||||
? 400
|
? 400
|
||||||
: transitionName === "modal-popup"
|
: transitionName.value === "modal-popup"
|
||||||
? 100
|
? 100
|
||||||
: transitionName === "modal"
|
: transitionName.value === "modal"
|
||||||
? 200
|
? 200
|
||||||
: transitionName === "modal-drawer"
|
: transitionName.value === "modal-drawer"
|
||||||
? 200
|
? 200
|
||||||
: 0,
|
: 0,
|
||||||
);
|
);
|
||||||
|
@ -187,12 +196,12 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
|
||||||
// history.forward();
|
// history.forward();
|
||||||
// }
|
// }
|
||||||
if (opts.useSendAnimation) {
|
if (opts.useSendAnimation) {
|
||||||
useSendAnime = true;
|
useSendAnime.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
if (props.src) props.src.style.pointerEvents = "auto";
|
if (props.src) props.src.style.pointerEvents = "auto";
|
||||||
showing = false;
|
showing.value = false;
|
||||||
emit("close");
|
emit("close");
|
||||||
if (!props.noReturnFocus) {
|
if (!props.noReturnFocus) {
|
||||||
focusedElement.focus();
|
focusedElement.focus();
|
||||||
|
@ -204,8 +213,8 @@ function onBgClick() {
|
||||||
emit("click");
|
emit("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === "drawer") {
|
if (type.value === "drawer") {
|
||||||
maxHeight = window.innerHeight / 1.5;
|
maxHeight.value = window.innerHeight / 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keymap = {
|
const keymap = {
|
||||||
|
@ -216,21 +225,21 @@ const MARGIN = 16;
|
||||||
|
|
||||||
const align = () => {
|
const align = () => {
|
||||||
if (props.src == null) return;
|
if (props.src == null) return;
|
||||||
if (type === "drawer") return;
|
if (type.value === "drawer") return;
|
||||||
if (type === "dialog") return;
|
if (type.value === "dialog") return;
|
||||||
|
|
||||||
if (content == null) return;
|
if (content.value == null) return;
|
||||||
|
|
||||||
const srcRect = props.src.getBoundingClientRect();
|
const srcRect = props.src.getBoundingClientRect();
|
||||||
|
|
||||||
const width = content!.offsetWidth;
|
const width = content.value!.offsetWidth;
|
||||||
const height = content!.offsetHeight;
|
const height = content.value!.offsetHeight;
|
||||||
|
|
||||||
let left;
|
let left;
|
||||||
let top;
|
let top;
|
||||||
|
|
||||||
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
|
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||||
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
|
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||||
|
|
||||||
if (props.anchor.x === "center") {
|
if (props.anchor.x === "center") {
|
||||||
left = x + props.src.offsetWidth / 2 - width / 2;
|
left = x + props.src.offsetWidth / 2 - width / 2;
|
||||||
|
@ -248,7 +257,7 @@ const align = () => {
|
||||||
top = y + props.src.offsetHeight;
|
top = y + props.src.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixed) {
|
if (fixed.value) {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
if (left + width > window.innerWidth) {
|
if (left + width > window.innerWidth) {
|
||||||
left = window.innerWidth - width;
|
left = window.innerWidth - width;
|
||||||
|
@ -261,16 +270,16 @@ const align = () => {
|
||||||
if (top + height > window.innerHeight - MARGIN) {
|
if (top + height > window.innerHeight - MARGIN) {
|
||||||
if (props.noOverlap && props.anchor.x === "center") {
|
if (props.noOverlap && props.anchor.x === "center") {
|
||||||
if (underSpace >= upperSpace / 3) {
|
if (underSpace >= upperSpace / 3) {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = upperSpace;
|
maxHeight.value = upperSpace;
|
||||||
top = upperSpace + MARGIN - height;
|
top = upperSpace + MARGIN - height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
top = window.innerHeight - MARGIN - height;
|
top = window.innerHeight - MARGIN - height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
|
@ -286,9 +295,9 @@ const align = () => {
|
||||||
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
|
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
|
||||||
if (props.noOverlap && props.anchor.x === "center") {
|
if (props.noOverlap && props.anchor.x === "center") {
|
||||||
if (underSpace >= upperSpace / 3) {
|
if (underSpace >= upperSpace / 3) {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = upperSpace;
|
maxHeight.value = upperSpace;
|
||||||
top = window.scrollY + (upperSpace + MARGIN - height);
|
top = window.scrollY + (upperSpace + MARGIN - height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -300,7 +309,7 @@ const align = () => {
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,36 +326,43 @@ const align = () => {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
top >=
|
top >=
|
||||||
srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)
|
srcRect.top +
|
||||||
|
props.src.offsetHeight +
|
||||||
|
(fixed.value ? 0 : window.pageYOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginY = "top";
|
transformOriginY = "top";
|
||||||
} else if (top + height <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
|
} else if (
|
||||||
|
top + height <=
|
||||||
|
srcRect.top + (fixed.value ? 0 : window.pageYOffset)
|
||||||
|
) {
|
||||||
transformOriginY = "bottom";
|
transformOriginY = "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
left >=
|
left >=
|
||||||
srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)
|
srcRect.left +
|
||||||
|
props.src.offsetWidth +
|
||||||
|
(fixed.value ? 0 : window.pageXOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginX = "left";
|
transformOriginX = "left";
|
||||||
} else if (
|
} else if (
|
||||||
left + width <=
|
left + width <=
|
||||||
srcRect.left + (fixed ? 0 : window.pageXOffset)
|
srcRect.left + (fixed.value ? 0 : window.pageXOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginX = "right";
|
transformOriginX = "right";
|
||||||
}
|
}
|
||||||
|
|
||||||
transformOrigin = `${transformOriginX} ${transformOriginY}`;
|
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
|
||||||
|
|
||||||
content.style.left = left + "px";
|
content.value.style.left = left + "px";
|
||||||
content.style.top = top + "px";
|
content.value.style.top = top + "px";
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpened = () => {
|
const onOpened = () => {
|
||||||
emit("opened");
|
emit("opened");
|
||||||
|
|
||||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||||
const el = content!.children[0];
|
const el = content.value!.children[0];
|
||||||
el.addEventListener(
|
el.addEventListener(
|
||||||
"mousedown",
|
"mousedown",
|
||||||
(ev) => {
|
(ev) => {
|
||||||
|
@ -378,7 +394,8 @@ onMounted(() => {
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
props.src.style.pointerEvents = "none";
|
props.src.style.pointerEvents = "none";
|
||||||
}
|
}
|
||||||
fixed = type === "drawer" || getFixedContainer(props.src) != null;
|
fixed.value =
|
||||||
|
type.value === "drawer" || getFixedContainer(props.src) != null;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
|
@ -390,7 +407,7 @@ onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
new ResizeObserver((entries, observer) => {
|
new ResizeObserver((entries, observer) => {
|
||||||
align();
|
align();
|
||||||
}).observe(content!);
|
}).observe(content.value!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ComputedRef, provide } from "vue";
|
import { ComputedRef, provide, ref, computed } from "vue";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import { popout as _popout } from "@/scripts/popout";
|
import { popout as _popout } from "@/scripts/popout";
|
||||||
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
||||||
|
@ -76,27 +76,27 @@ const router = new Router(routes, props.initialPath);
|
||||||
|
|
||||||
router.addListener("push", (ctx) => {});
|
router.addListener("push", (ctx) => {});
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||||
let rootEl = $ref();
|
let rootEl = ref();
|
||||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
let modal = ref<InstanceType<typeof MkModal>>();
|
||||||
let path = $ref(props.initialPath);
|
let path = ref(props.initialPath);
|
||||||
let width = $ref(860);
|
let width = ref(860);
|
||||||
let height = $ref(660);
|
let height = ref(660);
|
||||||
const history = [];
|
const history = [];
|
||||||
|
|
||||||
provide("router", router);
|
provide("router", router);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata = info;
|
pageMetadata.value = info;
|
||||||
});
|
});
|
||||||
provide("shouldOmitHeaderTitle", true);
|
provide("shouldOmitHeaderTitle", true);
|
||||||
provide("shouldHeaderThin", true);
|
provide("shouldHeaderThin", true);
|
||||||
|
|
||||||
const pageUrl = $computed(() => url + path);
|
const pageUrl = computed(() => url + path.value);
|
||||||
const contextmenu = $computed(() => {
|
const contextmenu = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: path,
|
text: path.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
|
@ -113,15 +113,15 @@ const contextmenu = $computed(() => {
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(pageUrl, "_blank");
|
window.open(pageUrl.value, "_blank");
|
||||||
modal.close();
|
modal.value.close();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(pageUrl);
|
copyToClipboard(pageUrl.value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -137,17 +137,17 @@ function back() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function expand() {
|
function expand() {
|
||||||
mainRouter.push(path);
|
mainRouter.push(path.value);
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function popout() {
|
function popout() {
|
||||||
_popout(path, rootEl);
|
_popout(path.value, rootEl.value);
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent) {
|
function onContextmenu(ev: MouseEvent) {
|
||||||
os.contextMenu(contextmenu, ev);
|
os.contextMenu(contextmenu.value, ev);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from "vue";
|
||||||
|
|
||||||
import { FocusTrap } from "focus-trap-vue";
|
import { FocusTrap } from "focus-trap-vue";
|
||||||
import MkModal from "./MkModal.vue";
|
import MkModal from "./MkModal.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -90,12 +92,12 @@ const emit = defineEmits<{
|
||||||
(event: "ok"): void;
|
(event: "ok"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let rootEl = $shallowRef<HTMLElement>();
|
let rootEl = shallowRef<HTMLElement>();
|
||||||
let headerEl = $shallowRef<HTMLElement>();
|
let headerEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
const close = (ev) => {
|
const close = (ev) => {
|
||||||
modal?.close(ev);
|
modal.value?.close(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBgClick = () => {
|
const onBgClick = () => {
|
||||||
|
|
|
@ -297,7 +297,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const inChannel = inject("inChannel", null);
|
const inChannel = inject("inChannel", null);
|
||||||
|
|
||||||
let note = $ref(deepClone(props.note));
|
let note = ref(deepClone(props.note));
|
||||||
|
|
||||||
const softMuteReasonI18nSrc = (what?: string) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||||
|
@ -312,19 +312,19 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
// plugin
|
// plugin
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result = deepClone(note);
|
let result = deepClone(note.value);
|
||||||
for (const interruptor of noteViewInterruptors) {
|
for (const interruptor of noteViewInterruptors) {
|
||||||
result = await interruptor.handler(result);
|
result = await interruptor.handler(result);
|
||||||
}
|
}
|
||||||
note = result;
|
note.value = result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isRenote =
|
const isRenote =
|
||||||
note.renote != null &&
|
note.value.renote != null &&
|
||||||
note.text == null &&
|
note.value.text == null &&
|
||||||
note.fileIds.length === 0 &&
|
note.value.fileIds.length === 0 &&
|
||||||
note.poll == null;
|
note.value.poll == null;
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement>();
|
||||||
const footerEl = ref<HTMLElement>();
|
const footerEl = ref<HTMLElement>();
|
||||||
|
@ -333,13 +333,15 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const renoteTime = ref<HTMLElement>();
|
const renoteTime = ref<HTMLElement>();
|
||||||
const reactButton = ref<HTMLElement>();
|
const reactButton = ref<HTMLElement>();
|
||||||
let appearNote = $computed(() =>
|
let appearNote = computed(() =>
|
||||||
isRenote ? (note.renote as misskey.entities.Note) : note,
|
isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
|
||||||
);
|
);
|
||||||
const isMyRenote = $i && $i.id === note.userId;
|
const isMyRenote = $i && $i.id === note.value.userId;
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
const muted = ref(
|
||||||
|
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||||
|
);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||||
|
@ -358,7 +360,7 @@ const keymap = {
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -366,7 +368,7 @@ function reply(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
os.post(
|
os.post(
|
||||||
{
|
{
|
||||||
reply: appearNote,
|
reply: appearNote.value,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
@ -382,7 +384,7 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -425,21 +427,24 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: notePage(appearNote),
|
text: notePage(appearNote.value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-browser ph-bold ph-lg",
|
icon: "ph-browser ph-bold ph-lg",
|
||||||
text: i18n.ts.openInWindow,
|
text: i18n.ts.openInWindow,
|
||||||
action: () => {
|
action: () => {
|
||||||
os.pageWindow(notePage(appearNote));
|
os.pageWindow(notePage(appearNote.value));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notePage(appearNote) != location.pathname
|
notePage(appearNote.value) != location.pathname
|
||||||
? {
|
? {
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
action: () => {
|
action: () => {
|
||||||
router.push(notePage(appearNote), "forcePage");
|
router.push(
|
||||||
|
notePage(appearNote.value),
|
||||||
|
"forcePage",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -448,22 +453,25 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
href: notePage(appearNote),
|
href: notePage(appearNote.value),
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appearNote.user.host != null
|
appearNote.value.user.host != null
|
||||||
? {
|
? {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||||
text: i18n.ts.showOnRemote,
|
text: i18n.ts.showOnRemote,
|
||||||
href: appearNote.url ?? appearNote.uri ?? "",
|
href:
|
||||||
|
appearNote.value.url ??
|
||||||
|
appearNote.value.uri ??
|
||||||
|
"",
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -476,7 +484,7 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton,
|
menuButton,
|
||||||
|
@ -500,7 +508,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
||||||
danger: true,
|
danger: true,
|
||||||
action: () => {
|
action: () => {
|
||||||
os.api("notes/delete", {
|
os.api("notes/delete", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
});
|
});
|
||||||
isDeleted.value = true;
|
isDeleted.value = true;
|
||||||
},
|
},
|
||||||
|
@ -541,13 +549,13 @@ function noteClick(e) {
|
||||||
) {
|
) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
} else {
|
} else {
|
||||||
router.push(notePage(appearNote));
|
router.push(notePage(appearNote.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPromo() {
|
function readPromo() {
|
||||||
os.api("promo/read", {
|
os.api("promo/read", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
});
|
});
|
||||||
isDeleted.value = true;
|
isDeleted.value = true;
|
||||||
}
|
}
|
||||||
|
@ -559,28 +567,30 @@ function setPostExpanded(val: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessibleLabel = computed(() => {
|
const accessibleLabel = computed(() => {
|
||||||
let label = `${appearNote.user.username}; `;
|
let label = `${appearNote.value.user.username}; `;
|
||||||
if (appearNote.renote) {
|
if (appearNote.value.renote) {
|
||||||
label += `${i18n.t("renoted")} ${appearNote.renote.user.username}; `;
|
label += `${i18n.t("renoted")} ${
|
||||||
if (appearNote.renote.cw) {
|
appearNote.value.renote.user.username
|
||||||
label += `${i18n.t("cw")}: ${appearNote.renote.cw}; `;
|
}; `;
|
||||||
|
if (appearNote.value.renote.cw) {
|
||||||
|
label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
|
||||||
if (postIsExpanded.value) {
|
if (postIsExpanded.value) {
|
||||||
label += `${appearNote.renote.text}; `;
|
label += `${appearNote.value.renote.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
label += `${appearNote.renote.text}; `;
|
label += `${appearNote.value.renote.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (appearNote.cw) {
|
if (appearNote.value.cw) {
|
||||||
label += `${i18n.t("cw")}: ${appearNote.cw}; `;
|
label += `${i18n.t("cw")}: ${appearNote.value.cw}; `;
|
||||||
if (postIsExpanded.value) {
|
if (postIsExpanded.value) {
|
||||||
label += `${appearNote.text}; `;
|
label += `${appearNote.value.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
label += `${appearNote.text}; `;
|
label += `${appearNote.value.text}; `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const date = new Date(appearNote.createdAt);
|
const date = new Date(appearNote.value.createdAt);
|
||||||
label += `${date.toLocaleTimeString()}`;
|
label += `${date.toLocaleTimeString()}`;
|
||||||
return label;
|
return label;
|
||||||
});
|
});
|
||||||
|
|
|
@ -177,9 +177,9 @@ const props = defineProps<{
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let tab = $ref("replies");
|
let tab = ref("replies");
|
||||||
|
|
||||||
let note = $ref(deepClone(props.note));
|
let note = ref(deepClone(props.note));
|
||||||
|
|
||||||
const softMuteReasonI18nSrc = (what?: string) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||||
|
@ -194,30 +194,32 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
// plugin
|
// plugin
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result = deepClone(note);
|
let result = deepClone(note.value);
|
||||||
for (const interruptor of noteViewInterruptors) {
|
for (const interruptor of noteViewInterruptors) {
|
||||||
result = await interruptor.handler(result);
|
result = await interruptor.handler(result);
|
||||||
}
|
}
|
||||||
note = result;
|
note.value = result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement>();
|
||||||
const noteEl = $ref();
|
const noteEl = ref();
|
||||||
const menuButton = ref<HTMLElement>();
|
const menuButton = ref<HTMLElement>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const reactButton = ref<HTMLElement>();
|
const reactButton = ref<HTMLElement>();
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
const muted = ref(
|
||||||
|
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||||
|
);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
let conversation = $ref<null | misskey.entities.Note[]>([]);
|
let conversation = ref<null | misskey.entities.Note[]>([]);
|
||||||
const replies = ref<misskey.entities.Note[]>([]);
|
const replies = ref<misskey.entities.Note[]>([]);
|
||||||
let directReplies = $ref<null | misskey.entities.Note[]>([]);
|
let directReplies = ref<null | misskey.entities.Note[]>([]);
|
||||||
let directQuotes = $ref<null | misskey.entities.Note[]>([]);
|
let directQuotes = ref<null | misskey.entities.Note[]>([]);
|
||||||
let clips = $ref();
|
let clips = ref();
|
||||||
let renotes = $ref();
|
let renotes = ref();
|
||||||
let isScrolling;
|
let isScrolling;
|
||||||
|
|
||||||
const reactionsCount = Object.values(props.note.reactions).reduce(
|
const reactionsCount = Object.values(props.note.reactions).reduce(
|
||||||
|
@ -236,14 +238,14 @@ const keymap = {
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(note),
|
note: note,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
function reply(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
os.post({
|
os.post({
|
||||||
reply: note,
|
reply: note.value,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
focus();
|
focus();
|
||||||
|
@ -257,7 +259,7 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -291,7 +293,7 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
} else {
|
} else {
|
||||||
os.contextMenu(
|
os.contextMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton,
|
menuButton,
|
||||||
|
@ -305,7 +307,7 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton,
|
menuButton,
|
||||||
|
@ -319,48 +321,50 @@ function menu(viaKeyboard = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
noteEl.focus();
|
noteEl.value.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function blur() {
|
function blur() {
|
||||||
noteEl.blur();
|
noteEl.value.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
directReplies = null;
|
directReplies.value = null;
|
||||||
os.api("notes/children", {
|
os.api("notes/children", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
depth: 12,
|
depth: 12,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
res = res.reduce((acc, resNote) => {
|
res = res.reduce((acc, resNote) => {
|
||||||
if (resNote.userId == note.userId) {
|
if (resNote.userId == note.value.userId) {
|
||||||
return [...acc, resNote];
|
return [...acc, resNote];
|
||||||
}
|
}
|
||||||
return [resNote, ...acc];
|
return [resNote, ...acc];
|
||||||
}, []);
|
}, []);
|
||||||
replies.value = res;
|
replies.value = res;
|
||||||
directReplies = res
|
directReplies.value = res
|
||||||
.filter((resNote) => resNote.replyId === note.id)
|
.filter((resNote) => resNote.replyId === note.value.id)
|
||||||
.reverse();
|
.reverse();
|
||||||
directQuotes = res.filter((resNote) => resNote.renoteId === note.id);
|
directQuotes.value = res.filter(
|
||||||
|
(resNote) => resNote.renoteId === note.value.id,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
conversation = null;
|
conversation.value = null;
|
||||||
if (note.replyId) {
|
if (note.value.replyId) {
|
||||||
os.api("notes/conversation", {
|
os.api("notes/conversation", {
|
||||||
noteId: note.replyId,
|
noteId: note.value.replyId,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
conversation = res.reverse();
|
conversation.value = res.reverse();
|
||||||
focus();
|
focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clips = null;
|
clips.value = null;
|
||||||
os.api("notes/clips", {
|
os.api("notes/clips", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
clips = res;
|
clips.value = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
// const pagination = {
|
// const pagination = {
|
||||||
|
@ -371,14 +375,14 @@ os.api("notes/clips", {
|
||||||
|
|
||||||
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
renotes = null;
|
renotes.value = null;
|
||||||
function loadTab() {
|
function loadTab() {
|
||||||
if (tab === "renotes" && !renotes) {
|
if (tab.value === "renotes" && !renotes.value) {
|
||||||
os.api("notes/renotes", {
|
os.api("notes/renotes", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
renotes = res;
|
renotes.value = res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -387,7 +391,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
||||||
const { type, id, body } = noteData;
|
const { type, id, body } = noteData;
|
||||||
|
|
||||||
let found = -1;
|
let found = -1;
|
||||||
if (id === note.id) {
|
if (id === note.value.id) {
|
||||||
found = 0;
|
found = 0;
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < replies.value.length; i++) {
|
for (let i = 0; i < replies.value.length; i++) {
|
||||||
|
@ -412,7 +416,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
||||||
|
|
||||||
replies.value.splice(found, 0, replyNote);
|
replies.value.splice(found, 0, replyNote);
|
||||||
if (found === 0) {
|
if (found === 0) {
|
||||||
directReplies.push(replyNote);
|
directReplies.value.push(replyNote);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -433,12 +437,12 @@ document.addEventListener("wheel", () => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
stream.on("noteUpdated", onNoteUpdated);
|
stream.on("noteUpdated", onNoteUpdated);
|
||||||
isScrolling = false;
|
isScrolling = false;
|
||||||
noteEl.scrollIntoView();
|
noteEl.value.scrollIntoView();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
if (!isScrolling) {
|
if (!isScrolling) {
|
||||||
noteEl.scrollIntoView();
|
noteEl.value.scrollIntoView();
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
location.replace(location.hash); // Jump to highlighted reply
|
location.replace(location.hash); // Jump to highlighted reply
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -61,11 +63,12 @@ const props = defineProps<{
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let note = $ref(props.note);
|
let note = ref(props.note);
|
||||||
|
|
||||||
const showTicker =
|
const showTicker =
|
||||||
defaultStore.state.instanceTicker === "always" ||
|
defaultStore.state.instanceTicker === "always" ||
|
||||||
(defaultStore.state.instanceTicker === "remote" && note.user.instance);
|
(defaultStore.state.instanceTicker === "remote" &&
|
||||||
|
note.value.user.instance);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -177,7 +177,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, ref } from "vue";
|
import { inject, ref, computed } from "vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
||||||
|
@ -223,7 +223,7 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let note = $ref(deepClone(props.note));
|
let note = ref(deepClone(props.note));
|
||||||
|
|
||||||
const softMuteReasonI18nSrc = (what?: string) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||||
|
@ -236,10 +236,10 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isRenote =
|
const isRenote =
|
||||||
note.renote != null &&
|
note.value.renote != null &&
|
||||||
note.text == null &&
|
note.value.text == null &&
|
||||||
note.fileIds.length === 0 &&
|
note.value.fileIds.length === 0 &&
|
||||||
note.poll == null;
|
note.value.poll == null;
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement>();
|
||||||
const footerEl = ref<HTMLElement>();
|
const footerEl = ref<HTMLElement>();
|
||||||
|
@ -247,11 +247,13 @@ const menuButton = ref<HTMLElement>();
|
||||||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const reactButton = ref<HTMLElement>();
|
const reactButton = ref<HTMLElement>();
|
||||||
let appearNote = $computed(() =>
|
let appearNote = computed(() =>
|
||||||
isRenote ? (note.renote as misskey.entities.Note) : note,
|
isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
|
||||||
);
|
);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
const muted = ref(
|
||||||
|
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||||
|
);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const replies: misskey.entities.Note[] =
|
const replies: misskey.entities.Note[] =
|
||||||
|
@ -267,14 +269,14 @@ const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
function reply(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote,
|
reply: appearNote.value,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
focus();
|
focus();
|
||||||
|
@ -288,7 +290,7 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -314,7 +316,7 @@ const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton,
|
menuButton,
|
||||||
|
@ -346,21 +348,24 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: notePage(appearNote),
|
text: notePage(appearNote.value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-browser ph-bold ph-lg",
|
icon: "ph-browser ph-bold ph-lg",
|
||||||
text: i18n.ts.openInWindow,
|
text: i18n.ts.openInWindow,
|
||||||
action: () => {
|
action: () => {
|
||||||
os.pageWindow(notePage(appearNote));
|
os.pageWindow(notePage(appearNote.value));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notePage(appearNote) != location.pathname
|
notePage(appearNote.value) != location.pathname
|
||||||
? {
|
? {
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
action: () => {
|
action: () => {
|
||||||
router.push(notePage(appearNote), "forcePage");
|
router.push(
|
||||||
|
notePage(appearNote.value),
|
||||||
|
"forcePage",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -369,22 +374,22 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
href: notePage(appearNote),
|
href: notePage(appearNote.value),
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
note.user.host != null
|
note.value.user.host != null
|
||||||
? {
|
? {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||||
text: i18n.ts.showOnRemote,
|
text: i18n.ts.showOnRemote,
|
||||||
href: note.url ?? note.uri ?? "",
|
href: note.value.url ?? note.value.uri ?? "",
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import { notificationTypes } from "firefish-js";
|
import { notificationTypes } from "firefish-js";
|
||||||
import MkSwitch from "./form/switch.vue";
|
import MkSwitch from "./form/switch.vue";
|
||||||
|
@ -63,43 +65,45 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let includingTypes = $computed(() => props.includingTypes || []);
|
let includingTypes = computed(() => props.includingTypes || []);
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
let typesMap = $ref<Record<(typeof notificationTypes)[number], boolean>>({});
|
let typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({});
|
||||||
let useGlobalSetting = $ref(
|
let useGlobalSetting = ref(
|
||||||
(includingTypes === null || includingTypes.length === 0) &&
|
(includingTypes.value === null || includingTypes.value.length === 0) &&
|
||||||
props.showGlobalToggle,
|
props.showGlobalToggle,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const ntype of notificationTypes) {
|
for (const ntype of notificationTypes) {
|
||||||
typesMap[ntype] = includingTypes.includes(ntype);
|
typesMap.value[ntype] = includingTypes.value.includes(ntype);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ok() {
|
function ok() {
|
||||||
if (useGlobalSetting) {
|
if (useGlobalSetting.value) {
|
||||||
emit("done", { includingTypes: null });
|
emit("done", { includingTypes: null });
|
||||||
} else {
|
} else {
|
||||||
emit("done", {
|
emit("done", {
|
||||||
includingTypes: (
|
includingTypes: (
|
||||||
Object.keys(typesMap) as (typeof notificationTypes)[number][]
|
Object.keys(
|
||||||
).filter((type) => typesMap[type]),
|
typesMap.value,
|
||||||
|
) as (typeof notificationTypes)[number][]
|
||||||
|
).filter((type) => typesMap.value[type]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableAll() {
|
function disableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap.value) {
|
||||||
typesMap[type as (typeof notificationTypes)[number]] = false;
|
typesMap.value[type as (typeof notificationTypes)[number]] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAll() {
|
function enableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap.value) {
|
||||||
typesMap[type as (typeof notificationTypes)[number]] = true;
|
typesMap.value[type as (typeof notificationTypes)[number]] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import XNotification from "@/components/MkNotification.vue";
|
import XNotification from "@/components/MkNotification.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("high");
|
const zIndex = os.claimZIndex("high");
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}, 6000);
|
}, 6000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ComputedRef, provide } from "vue";
|
import { ComputedRef, provide, ref, computed } from "vue";
|
||||||
import RouterView from "@/components/global/RouterView.vue";
|
import RouterView from "@/components/global/RouterView.vue";
|
||||||
import XWindow from "@/components/MkWindow.vue";
|
import XWindow from "@/components/MkWindow.vue";
|
||||||
import { popout as _popout } from "@/scripts/popout";
|
import { popout as _popout } from "@/scripts/popout";
|
||||||
|
@ -51,18 +51,18 @@ defineEmits<{
|
||||||
|
|
||||||
const router = new Router(routes, props.initialPath);
|
const router = new Router(routes, props.initialPath);
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||||
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
let windowEl = ref<InstanceType<typeof XWindow>>();
|
||||||
const history = $ref<{ path: string; key: any }[]>([
|
const history = ref<{ path: string; key: any }[]>([
|
||||||
{
|
{
|
||||||
path: router.getCurrentPath(),
|
path: router.getCurrentPath(),
|
||||||
key: router.getCurrentKey(),
|
key: router.getCurrentKey(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const buttonsLeft = $computed(() => {
|
const buttonsLeft = computed(() => {
|
||||||
const buttons = [];
|
const buttons = [];
|
||||||
|
|
||||||
if (history.length > 1) {
|
if (history.value.length > 1) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: "ph-caret-left ph-bold ph-lg",
|
icon: "ph-caret-left ph-bold ph-lg",
|
||||||
onClick: back,
|
onClick: back,
|
||||||
|
@ -71,7 +71,7 @@ const buttonsLeft = $computed(() => {
|
||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
});
|
});
|
||||||
const buttonsRight = $computed(() => {
|
const buttonsRight = computed(() => {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
|
@ -84,18 +84,18 @@ const buttonsRight = $computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.addListener("push", (ctx) => {
|
router.addListener("push", (ctx) => {
|
||||||
history.push({ path: ctx.path, key: ctx.key });
|
history.value.push({ path: ctx.path, key: ctx.key });
|
||||||
});
|
});
|
||||||
|
|
||||||
provide("router", router);
|
provide("router", router);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata = info;
|
pageMetadata.value = info;
|
||||||
});
|
});
|
||||||
provide("shouldOmitHeaderTitle", true);
|
provide("shouldOmitHeaderTitle", true);
|
||||||
provide("shouldBackButton", false);
|
provide("shouldBackButton", false);
|
||||||
provide("shouldHeaderThin", true);
|
provide("shouldHeaderThin", true);
|
||||||
|
|
||||||
const contextmenu = $computed(() => [
|
const contextmenu = computed(() => [
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
|
@ -111,7 +111,7 @@ const contextmenu = $computed(() => [
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(url + router.getCurrentPath(), "_blank");
|
window.open(url + router.getCurrentPath(), "_blank");
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -124,25 +124,25 @@ const contextmenu = $computed(() => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
history.pop();
|
history.value.pop();
|
||||||
router.replace(
|
router.replace(
|
||||||
history[history.length - 1].path,
|
history.value[history.value.length - 1].path,
|
||||||
history[history.length - 1].key,
|
history.value[history.value.length - 1].key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function expand() {
|
function expand() {
|
||||||
mainRouter.push(router.getCurrentPath(), "forcePage");
|
mainRouter.push(router.getCurrentPath(), "forcePage");
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function popout() {
|
function popout() {
|
||||||
_popout(router.getCurrentPath(), windowEl.$el);
|
_popout(router.getCurrentPath(), windowEl.value.$el);
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkModal from "./MkModal.vue";
|
import MkModal from "./MkModal.vue";
|
||||||
import MkMenu from "./MkMenu.vue";
|
import MkMenu from "./MkMenu.vue";
|
||||||
import { MenuItem } from "@/types/menu";
|
import { MenuItem } from "@/types/menu";
|
||||||
|
@ -41,7 +43,7 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
let modal = ref<InstanceType<typeof MkModal>>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -233,7 +233,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from "vue";
|
import {
|
||||||
|
inject,
|
||||||
|
watch,
|
||||||
|
nextTick,
|
||||||
|
onMounted,
|
||||||
|
defineAsyncComponent,
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
} from "vue";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
|
@ -302,45 +310,45 @@ const emit = defineEmits<{
|
||||||
(ev: "esc"): void;
|
(ev: "esc"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
|
const textareaEl = ref<HTMLTextAreaElement | null>(null);
|
||||||
const cwInputEl = $ref<HTMLInputElement | null>(null);
|
const cwInputEl = ref<HTMLInputElement | null>(null);
|
||||||
const hashtagsInputEl = $ref<HTMLInputElement | null>(null);
|
const hashtagsInputEl = ref<HTMLInputElement | null>(null);
|
||||||
const visibilityButton = $ref<HTMLElement | null>(null);
|
const visibilityButton = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
let posting = $ref(false);
|
let posting = ref(false);
|
||||||
let text = $ref(props.initialText ?? "");
|
let text = ref(props.initialText ?? "");
|
||||||
let files = $ref(props.initialFiles ?? []);
|
let files = ref(props.initialFiles ?? []);
|
||||||
let poll = $ref<{
|
let poll = ref<{
|
||||||
choices: string[];
|
choices: string[];
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
expiresAt: string | null;
|
expiresAt: string | null;
|
||||||
expiredAfter: string | null;
|
expiredAfter: string | null;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
let useCw = $ref(false);
|
let useCw = ref(false);
|
||||||
let showPreview = $ref(false);
|
let showPreview = ref(false);
|
||||||
let cw = $ref<string | null>(null);
|
let cw = ref<string | null>(null);
|
||||||
let localOnly = $ref<boolean>(
|
let localOnly = ref<boolean>(
|
||||||
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
||||||
? defaultStore.state.localOnly
|
? defaultStore.state.localOnly
|
||||||
: defaultStore.state.defaultNoteLocalOnly,
|
: defaultStore.state.defaultNoteLocalOnly,
|
||||||
);
|
);
|
||||||
let visibility = $ref(
|
let visibility = ref(
|
||||||
props.initialVisibility ??
|
props.initialVisibility ??
|
||||||
((defaultStore.state.rememberNoteVisibility
|
((defaultStore.state.rememberNoteVisibility
|
||||||
? defaultStore.state.visibility
|
? defaultStore.state.visibility
|
||||||
: defaultStore.state
|
: defaultStore.state
|
||||||
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
|
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
|
||||||
);
|
);
|
||||||
let visibleUsers = $ref([]);
|
let visibleUsers = ref([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
let autocomplete = $ref(null);
|
let autocomplete = ref(null);
|
||||||
let draghover = $ref(false);
|
let draghover = ref(false);
|
||||||
let quoteId = $ref(null);
|
let quoteId = ref(null);
|
||||||
let hasNotSpecifiedMentions = $ref(false);
|
let hasNotSpecifiedMentions = ref(false);
|
||||||
let recentHashtags = $ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
||||||
let imeText = $ref("");
|
let imeText = ref("");
|
||||||
|
|
||||||
const typing = throttle(3000, () => {
|
const typing = throttle(3000, () => {
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
|
@ -348,7 +356,7 @@ const typing = throttle(3000, () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const draftKey = $computed((): string => {
|
const draftKey = computed((): string => {
|
||||||
if (props.editId) {
|
if (props.editId) {
|
||||||
return `edit:${props.editId}`;
|
return `edit:${props.editId}`;
|
||||||
}
|
}
|
||||||
|
@ -366,7 +374,7 @@ const draftKey = $computed((): string => {
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
|
|
||||||
const placeholder = $computed((): string => {
|
const placeholder = computed((): string => {
|
||||||
if (props.renote) {
|
if (props.renote) {
|
||||||
return i18n.ts._postForm.quotePlaceholder;
|
return i18n.ts._postForm.quotePlaceholder;
|
||||||
} else if (props.reply) {
|
} else if (props.reply) {
|
||||||
|
@ -386,7 +394,7 @@ const placeholder = $computed((): string => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitText = $computed((): string => {
|
const submitText = computed((): string => {
|
||||||
return props.editId
|
return props.editId
|
||||||
? i18n.ts.edit
|
? i18n.ts.edit
|
||||||
: props.renote
|
: props.renote
|
||||||
|
@ -396,34 +404,37 @@ const submitText = $computed((): string => {
|
||||||
: i18n.ts.note;
|
: i18n.ts.note;
|
||||||
});
|
});
|
||||||
|
|
||||||
const textLength = $computed((): number => {
|
const textLength = computed((): number => {
|
||||||
return length((preprocess(text) + imeText).trim());
|
return length((preprocess(text.value) + imeText.value).trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxTextLength = $computed((): number => {
|
const maxTextLength = computed((): number => {
|
||||||
return instance ? instance.maxNoteTextLength : 3000;
|
return instance ? instance.maxNoteTextLength : 3000;
|
||||||
});
|
});
|
||||||
|
|
||||||
const canPost = $computed((): boolean => {
|
const canPost = computed((): boolean => {
|
||||||
return (
|
return (
|
||||||
!posting &&
|
!posting.value &&
|
||||||
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
|
(1 <= textLength.value ||
|
||||||
textLength <= maxTextLength &&
|
1 <= files.value.length ||
|
||||||
(!poll || poll.choices.length >= 2)
|
!!poll.value ||
|
||||||
|
!!props.renote) &&
|
||||||
|
textLength.value <= maxTextLength.value &&
|
||||||
|
(!poll.value || poll.value.choices.length >= 2)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const withHashtags = $computed(
|
const withHashtags = computed(
|
||||||
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
||||||
);
|
);
|
||||||
const hashtags = $computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
||||||
|
|
||||||
watch($$(text), () => {
|
watch(text, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
$$(visibleUsers),
|
visibleUsers,
|
||||||
() => {
|
() => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
},
|
},
|
||||||
|
@ -433,10 +444,10 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (props.mention) {
|
if (props.mention) {
|
||||||
text = props.mention.host
|
text.value = props.mention.host
|
||||||
? `@${props.mention.username}@${toASCII(props.mention.host)}`
|
? `@${props.mention.username}@${toASCII(props.mention.host)}`
|
||||||
: `@${props.mention.username}`;
|
: `@${props.mention.username}`;
|
||||||
text += " ";
|
text.value += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -444,7 +455,7 @@ if (
|
||||||
(props.reply.user.username !== $i.username ||
|
(props.reply.user.username !== $i.username ||
|
||||||
(props.reply.user.host != null && props.reply.user.host !== host))
|
(props.reply.user.host != null && props.reply.user.host !== host))
|
||||||
) {
|
) {
|
||||||
text = `@${props.reply.user.username}${
|
text.value = `@${props.reply.user.username}${
|
||||||
props.reply.user.host != null
|
props.reply.user.host != null
|
||||||
? "@" + toASCII(props.reply.user.host)
|
? "@" + toASCII(props.reply.user.host)
|
||||||
: ""
|
: ""
|
||||||
|
@ -467,15 +478,15 @@ if (props.reply && props.reply.text != null) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 重複は除外
|
// 重複は除外
|
||||||
if (text.includes(`${mention} `)) continue;
|
if (text.value.includes(`${mention} `)) continue;
|
||||||
|
|
||||||
text += `${mention} `;
|
text.value += `${mention} `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
visibility = "public";
|
visibility.value = "public";
|
||||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||||
|
@ -483,17 +494,17 @@ if (
|
||||||
props.reply &&
|
props.reply &&
|
||||||
["home", "followers", "specified"].includes(props.reply.visibility)
|
["home", "followers", "specified"].includes(props.reply.visibility)
|
||||||
) {
|
) {
|
||||||
if (props.reply.visibility === "home" && visibility === "followers") {
|
if (props.reply.visibility === "home" && visibility.value === "followers") {
|
||||||
visibility = "followers";
|
visibility.value = "followers";
|
||||||
} else if (
|
} else if (
|
||||||
["home", "followers"].includes(props.reply.visibility) &&
|
["home", "followers"].includes(props.reply.visibility) &&
|
||||||
visibility === "specified"
|
visibility.value === "specified"
|
||||||
) {
|
) {
|
||||||
visibility = "specified";
|
visibility.value = "specified";
|
||||||
} else {
|
} else {
|
||||||
visibility = props.reply.visibility;
|
visibility.value = props.reply.visibility;
|
||||||
}
|
}
|
||||||
if (visibility === "specified") {
|
if (visibility.value === "specified") {
|
||||||
if (props.reply.visibleUserIds) {
|
if (props.reply.visibleUserIds) {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: props.reply.visibleUserIds.filter(
|
userIds: props.reply.visibleUserIds.filter(
|
||||||
|
@ -515,7 +526,7 @@ if (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.specified) {
|
if (props.specified) {
|
||||||
visibility = "specified";
|
visibility.value = "specified";
|
||||||
pushVisibleUser(props.specified);
|
pushVisibleUser(props.specified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,53 +542,53 @@ const addRe = (s: string) => {
|
||||||
|
|
||||||
// keep cw when reply
|
// keep cw when reply
|
||||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||||
useCw = true;
|
useCw.value = true;
|
||||||
cw =
|
cw.value =
|
||||||
props.reply.user.username === $i.username
|
props.reply.user.username === $i.username
|
||||||
? props.reply.cw
|
? props.reply.cw
|
||||||
: addRe(props.reply.cw);
|
: addRe(props.reply.cw);
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchForDraft() {
|
function watchForDraft() {
|
||||||
watch($$(text), () => saveDraft());
|
watch(text, () => saveDraft());
|
||||||
watch($$(useCw), () => saveDraft());
|
watch(useCw, () => saveDraft());
|
||||||
watch($$(cw), () => saveDraft());
|
watch(cw, () => saveDraft());
|
||||||
watch($$(poll), () => saveDraft());
|
watch(poll, () => saveDraft());
|
||||||
watch($$(files), () => saveDraft(), { deep: true });
|
watch(files, () => saveDraft(), { deep: true });
|
||||||
watch($$(visibility), () => saveDraft());
|
watch(visibility, () => saveDraft());
|
||||||
watch($$(localOnly), () => saveDraft());
|
watch(localOnly, () => saveDraft());
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMissingMention() {
|
function checkMissingMention() {
|
||||||
if (visibility === "specified") {
|
if (visibility.value === "specified") {
|
||||||
const ast = mfm.parse(text);
|
const ast = mfm.parse(text.value);
|
||||||
|
|
||||||
for (const x of extractMentions(ast)) {
|
for (const x of extractMentions(ast)) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === x.username && u.host === x.host,
|
(u) => u.username === x.username && u.host === x.host,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
hasNotSpecifiedMentions = true;
|
hasNotSpecifiedMentions.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasNotSpecifiedMentions = false;
|
hasNotSpecifiedMentions.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMissingMention() {
|
function addMissingMention() {
|
||||||
const ast = mfm.parse(text);
|
const ast = mfm.parse(text.value);
|
||||||
|
|
||||||
for (const x of extractMentions(ast)) {
|
for (const x of extractMentions(ast)) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === x.username && u.host === x.host,
|
(u) => u.username === x.username && u.host === x.host,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
os.api("users/show", { username: x.username, host: x.host }).then(
|
os.api("users/show", { username: x.username, host: x.host }).then(
|
||||||
(user) => {
|
(user) => {
|
||||||
visibleUsers.push(user);
|
visibleUsers.value.push(user);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -585,10 +596,10 @@ function addMissingMention() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePoll() {
|
function togglePoll() {
|
||||||
if (poll) {
|
if (poll.value) {
|
||||||
poll = null;
|
poll.value = null;
|
||||||
} else {
|
} else {
|
||||||
poll = {
|
poll.value = {
|
||||||
choices: ["", ""],
|
choices: ["", ""],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
|
@ -598,15 +609,15 @@ function togglePoll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTag(tag: string) {
|
function addTag(tag: string) {
|
||||||
insertTextAtCursor(textareaEl, ` #${tag} `);
|
insertTextAtCursor(textareaEl.value, ` #${tag} `);
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
if (textareaEl) {
|
if (textareaEl.value) {
|
||||||
textareaEl.focus();
|
textareaEl.value.focus();
|
||||||
textareaEl.setSelectionRange(
|
textareaEl.value.setSelectionRange(
|
||||||
textareaEl.value.length,
|
textareaEl.value.value.length,
|
||||||
textareaEl.value.length,
|
textareaEl.value.value.length,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -615,31 +626,32 @@ function chooseFileFrom(ev) {
|
||||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
|
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
|
||||||
(files_) => {
|
(files_) => {
|
||||||
for (const file of files_) {
|
for (const file of files_) {
|
||||||
files.push(file);
|
files.value.push(file);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function detachFile(id) {
|
function detachFile(id) {
|
||||||
files = files.filter((x) => x.id !== id);
|
files.value = files.value.filter((x) => x.id !== id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFiles(_files) {
|
function updateFiles(_files) {
|
||||||
files = _files;
|
files.value = _files;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFileSensitive(file, sensitive) {
|
function updateFileSensitive(file, sensitive) {
|
||||||
files[files.findIndex((x) => x.id === file.id)].isSensitive = sensitive;
|
files.value[files.value.findIndex((x) => x.id === file.id)].isSensitive =
|
||||||
|
sensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFileName(file, name) {
|
function updateFileName(file, name) {
|
||||||
files[files.findIndex((x) => x.id === file.id)].name = name;
|
files.value[files.value.findIndex((x) => x.id === file.id)].name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function upload(file: File, name?: string) {
|
function upload(file: File, name?: string) {
|
||||||
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
|
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
|
||||||
files.push(res);
|
files.value.push(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,21 +666,21 @@ function setVisibility() {
|
||||||
() => import("@/components/MkVisibilityPicker.vue"),
|
() => import("@/components/MkVisibilityPicker.vue"),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
currentVisibility: visibility,
|
currentVisibility: visibility.value,
|
||||||
currentLocalOnly: localOnly,
|
currentLocalOnly: localOnly.value,
|
||||||
src: visibilityButton,
|
src: visibilityButton.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
changeVisibility: (v) => {
|
changeVisibility: (v) => {
|
||||||
visibility = v;
|
visibility.value = v;
|
||||||
if (defaultStore.state.rememberNoteVisibility) {
|
if (defaultStore.state.rememberNoteVisibility) {
|
||||||
defaultStore.set("visibility", visibility);
|
defaultStore.set("visibility", visibility.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeLocalOnly: (v) => {
|
changeLocalOnly: (v) => {
|
||||||
localOnly = v;
|
localOnly.value = v;
|
||||||
if (defaultStore.state.rememberNoteVisibility) {
|
if (defaultStore.state.rememberNoteVisibility) {
|
||||||
defaultStore.set("localOnly", localOnly);
|
defaultStore.set("localOnly", localOnly.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -678,11 +690,11 @@ function setVisibility() {
|
||||||
|
|
||||||
function pushVisibleUser(user) {
|
function pushVisibleUser(user) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === user.username && u.host === user.host,
|
(u) => u.username === user.username && u.host === user.host,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
visibleUsers.push(user);
|
visibleUsers.value.push(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,21 +705,21 @@ function addVisibleUser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeVisibleUser(user) {
|
function removeVisibleUser(user) {
|
||||||
visibleUsers = erase(user, visibleUsers);
|
visibleUsers.value = erase(user, visibleUsers.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
text = "";
|
text.value = "";
|
||||||
files = [];
|
files.value = [];
|
||||||
poll = null;
|
poll.value = null;
|
||||||
quoteId = null;
|
quoteId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(ev: KeyboardEvent) {
|
function onKeydown(ev: KeyboardEvent) {
|
||||||
if (
|
if (
|
||||||
(ev.which === 10 || ev.which === 13) &&
|
(ev.which === 10 || ev.which === 13) &&
|
||||||
(ev.ctrlKey || ev.metaKey) &&
|
(ev.ctrlKey || ev.metaKey) &&
|
||||||
canPost
|
canPost.value
|
||||||
)
|
)
|
||||||
post();
|
post();
|
||||||
if (ev.which === 27) emit("esc");
|
if (ev.which === 27) emit("esc");
|
||||||
|
@ -715,12 +727,12 @@ function onKeydown(ev: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionUpdate(ev: CompositionEvent) {
|
function onCompositionUpdate(ev: CompositionEvent) {
|
||||||
imeText = ev.data;
|
imeText.value = ev.data;
|
||||||
typing();
|
typing();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionEnd(ev: CompositionEvent) {
|
function onCompositionEnd(ev: CompositionEvent) {
|
||||||
imeText = "";
|
imeText.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPaste(ev: ClipboardEvent) {
|
async function onPaste(ev: ClipboardEvent) {
|
||||||
|
@ -741,7 +753,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
|
|
||||||
const paste = ev.clipboardData.getData("text");
|
const paste = ev.clipboardData.getData("text");
|
||||||
|
|
||||||
if (!props.renote && !quoteId && paste.startsWith(url + "/notes/")) {
|
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
os.yesno({
|
os.yesno({
|
||||||
|
@ -749,11 +761,13 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
text: i18n.ts.quoteQuestion,
|
text: i18n.ts.quoteQuestion,
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
insertTextAtCursor(textareaEl, paste);
|
insertTextAtCursor(textareaEl.value, paste);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteId = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
|
quoteId.value = paste
|
||||||
|
.substr(url.length)
|
||||||
|
.match(/^\/notes\/(.+?)\/?$/)[1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -764,7 +778,7 @@ function onDragover(ev) {
|
||||||
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
||||||
if (isFile || isDriveFile) {
|
if (isFile || isDriveFile) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
draghover = true;
|
draghover.value = true;
|
||||||
switch (ev.dataTransfer.effectAllowed) {
|
switch (ev.dataTransfer.effectAllowed) {
|
||||||
case "all":
|
case "all":
|
||||||
case "uninitialized":
|
case "uninitialized":
|
||||||
|
@ -785,15 +799,15 @@ function onDragover(ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragenter(ev) {
|
function onDragenter(ev) {
|
||||||
draghover = true;
|
draghover.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragleave(ev) {
|
function onDragleave(ev) {
|
||||||
draghover = false;
|
draghover.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrop(ev): void {
|
function onDrop(ev): void {
|
||||||
draghover = false;
|
draghover.value = false;
|
||||||
|
|
||||||
// ファイルだったら
|
// ファイルだったら
|
||||||
if (ev.dataTransfer.files.length > 0) {
|
if (ev.dataTransfer.files.length > 0) {
|
||||||
|
@ -806,7 +820,7 @@ function onDrop(ev): void {
|
||||||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
||||||
if (driveFile != null && driveFile !== "") {
|
if (driveFile != null && driveFile !== "") {
|
||||||
const file = JSON.parse(driveFile);
|
const file = JSON.parse(driveFile);
|
||||||
files.push(file);
|
files.value.push(file);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -815,16 +829,16 @@ function onDrop(ev): void {
|
||||||
function saveDraft() {
|
function saveDraft() {
|
||||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||||
|
|
||||||
draftData[draftKey] = {
|
draftData[draftKey.value] = {
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
data: {
|
data: {
|
||||||
text: text,
|
text: text.value,
|
||||||
useCw: useCw,
|
useCw: useCw.value,
|
||||||
cw: cw,
|
cw: cw.value,
|
||||||
visibility: visibility,
|
visibility: visibility.value,
|
||||||
localOnly: localOnly,
|
localOnly: localOnly.value,
|
||||||
files: files,
|
files: files.value,
|
||||||
poll: poll,
|
poll: poll.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -834,37 +848,38 @@ function saveDraft() {
|
||||||
function deleteDraft() {
|
function deleteDraft() {
|
||||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||||
|
|
||||||
delete draftData[draftKey];
|
delete draftData[draftKey.value];
|
||||||
|
|
||||||
localStorage.setItem("drafts", JSON.stringify(draftData));
|
localStorage.setItem("drafts", JSON.stringify(draftData));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function post() {
|
async function post() {
|
||||||
const processedText = preprocess(text);
|
const processedText = preprocess(text.value);
|
||||||
|
|
||||||
let postData = {
|
let postData = {
|
||||||
editId: props.editId ? props.editId : undefined,
|
editId: props.editId ? props.editId : undefined,
|
||||||
text: processedText === "" ? undefined : processedText,
|
text: processedText === "" ? undefined : processedText,
|
||||||
fileIds: files.length > 0 ? files.map((f) => f.id) : undefined,
|
fileIds:
|
||||||
|
files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
|
||||||
replyId: props.reply ? props.reply.id : undefined,
|
replyId: props.reply ? props.reply.id : undefined,
|
||||||
renoteId: props.renote
|
renoteId: props.renote
|
||||||
? props.renote.id
|
? props.renote.id
|
||||||
: quoteId
|
: quoteId.value
|
||||||
? quoteId
|
? quoteId.value
|
||||||
: undefined,
|
: undefined,
|
||||||
channelId: props.channel ? props.channel.id : undefined,
|
channelId: props.channel ? props.channel.id : undefined,
|
||||||
poll: poll,
|
poll: poll.value,
|
||||||
cw: useCw ? cw || "" : undefined,
|
cw: useCw.value ? cw.value || "" : undefined,
|
||||||
localOnly: localOnly,
|
localOnly: localOnly.value,
|
||||||
visibility: visibility,
|
visibility: visibility.value,
|
||||||
visibleUserIds:
|
visibleUserIds:
|
||||||
visibility === "specified"
|
visibility.value === "specified"
|
||||||
? visibleUsers.map((u) => u.id)
|
? visibleUsers.value.map((u) => u.id)
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (withHashtags && hashtags && hashtags.trim() !== "") {
|
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {
|
||||||
const hashtags_ = hashtags
|
const hashtags_ = hashtags.value
|
||||||
.trim()
|
.trim()
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
||||||
|
@ -883,12 +898,13 @@ async function post() {
|
||||||
|
|
||||||
let token = undefined;
|
let token = undefined;
|
||||||
|
|
||||||
if (postAccount) {
|
if (postAccount.value) {
|
||||||
const storedAccounts = await getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
token = storedAccounts.find((x) => x.id === postAccount.id)?.token;
|
token = storedAccounts.find((x) => x.id === postAccount.value.id)
|
||||||
|
?.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
posting = true;
|
posting.value = true;
|
||||||
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
|
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
clear();
|
clear();
|
||||||
|
@ -908,12 +924,12 @@ async function post() {
|
||||||
JSON.stringify(unique(hashtags_.concat(history))),
|
JSON.stringify(unique(hashtags_.concat(history))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
posting = false;
|
posting.value = false;
|
||||||
postAccount = null;
|
postAccount.value = null;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
posting = false;
|
posting.value = false;
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: err.message + "\n" + (err as any).id,
|
text: err.message + "\n" + (err as any).id,
|
||||||
|
@ -927,12 +943,12 @@ function cancel() {
|
||||||
|
|
||||||
function insertMention() {
|
function insertMention() {
|
||||||
os.selectUser().then((user) => {
|
os.selectUser().then((user) => {
|
||||||
insertTextAtCursor(textareaEl, "@" + Acct.toString(user) + " ");
|
insertTextAtCursor(textareaEl.value, "@" + Acct.toString(user) + " ");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertEmoji(ev: MouseEvent) {
|
async function insertEmoji(ev: MouseEvent) {
|
||||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
|
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openCheatSheet(ev: MouseEvent) {
|
async function openCheatSheet(ev: MouseEvent) {
|
||||||
|
@ -946,11 +962,11 @@ function showActions(ev) {
|
||||||
action: () => {
|
action: () => {
|
||||||
action.handler(
|
action.handler(
|
||||||
{
|
{
|
||||||
text: text,
|
text: text.value,
|
||||||
},
|
},
|
||||||
(key, value) => {
|
(key, value) => {
|
||||||
if (key === "text") {
|
if (key === "text") {
|
||||||
text = value;
|
text.value = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -960,19 +976,19 @@ function showActions(ev) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
|
let postAccount = ref<misskey.entities.UserDetailed | null>(null);
|
||||||
|
|
||||||
function openAccountMenu(ev: MouseEvent) {
|
function openAccountMenu(ev: MouseEvent) {
|
||||||
openAccountMenu_(
|
openAccountMenu_(
|
||||||
{
|
{
|
||||||
withExtraOperation: false,
|
withExtraOperation: false,
|
||||||
includeCurrentAccount: true,
|
includeCurrentAccount: true,
|
||||||
active: postAccount != null ? postAccount.id : $i.id,
|
active: postAccount.value != null ? postAccount.value.id : $i.id,
|
||||||
onChoose: (account) => {
|
onChoose: (account) => {
|
||||||
if (account.id === $i.id) {
|
if (account.id === $i.id) {
|
||||||
postAccount = null;
|
postAccount.value = null;
|
||||||
} else {
|
} else {
|
||||||
postAccount = account;
|
postAccount.value = account;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -990,30 +1006,30 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: detach when unmount
|
// TODO: detach when unmount
|
||||||
new Autocomplete(textareaEl, $$(text));
|
new Autocomplete(textareaEl.value, text);
|
||||||
new Autocomplete(cwInputEl, $$(cw));
|
new Autocomplete(cwInputEl.value, cw);
|
||||||
new Autocomplete(hashtagsInputEl, $$(hashtags));
|
new Autocomplete(hashtagsInputEl.value, hashtags);
|
||||||
|
|
||||||
autosize(textareaEl);
|
autosize(textareaEl.value);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
autosize(textareaEl);
|
autosize(textareaEl.value);
|
||||||
// 書きかけの投稿を復元
|
// 書きかけの投稿を復元
|
||||||
if (!props.instant && !props.mention && !props.specified) {
|
if (!props.instant && !props.mention && !props.specified) {
|
||||||
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
||||||
draftKey
|
draftKey.value
|
||||||
];
|
];
|
||||||
if (draft) {
|
if (draft) {
|
||||||
text = draft.data.text;
|
text.value = draft.data.text;
|
||||||
useCw = draft.data.useCw;
|
useCw.value = draft.data.useCw;
|
||||||
cw = draft.data.cw;
|
cw.value = draft.data.cw;
|
||||||
visibility = draft.data.visibility;
|
visibility.value = draft.data.visibility;
|
||||||
localOnly = draft.data.localOnly;
|
localOnly.value = draft.data.localOnly;
|
||||||
files = (draft.data.files || []).filter(
|
files.value = (draft.data.files || []).filter(
|
||||||
(draftFile) => draftFile,
|
(draftFile) => draftFile,
|
||||||
);
|
);
|
||||||
if (draft.data.poll) {
|
if (draft.data.poll) {
|
||||||
poll = draft.data.poll;
|
poll.value = draft.data.poll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1021,21 +1037,21 @@ onMounted(() => {
|
||||||
// 削除して編集
|
// 削除して編集
|
||||||
if (props.initialNote) {
|
if (props.initialNote) {
|
||||||
const init = props.initialNote;
|
const init = props.initialNote;
|
||||||
text = init.text ? init.text : "";
|
text.value = init.text ? init.text : "";
|
||||||
files = init.files;
|
files.value = init.files;
|
||||||
cw = init.cw;
|
cw.value = init.cw;
|
||||||
useCw = init.cw != null;
|
useCw.value = init.cw != null;
|
||||||
if (init.poll) {
|
if (init.poll) {
|
||||||
poll = {
|
poll.value = {
|
||||||
choices: init.poll.choices.map((x) => x.text),
|
choices: init.poll.choices.map((x) => x.text),
|
||||||
multiple: init.poll.multiple,
|
multiple: init.poll.multiple,
|
||||||
expiresAt: init.poll.expiresAt,
|
expiresAt: init.poll.expiresAt,
|
||||||
expiredAfter: init.poll.expiredAfter,
|
expiredAfter: init.poll.expiredAfter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
visibility = init.visibility;
|
visibility.value = init.visibility;
|
||||||
localOnly = init.localOnly;
|
localOnly.value = init.localOnly;
|
||||||
quoteId = init.renote ? init.renote.id : null;
|
quoteId.value = init.renote ? init.renote.id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => watchForDraft());
|
nextTick(() => watchForDraft());
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
|
@ -46,11 +48,11 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
|
let form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
function onPosted() {
|
function onPosted() {
|
||||||
modal.close({
|
modal.value.close({
|
||||||
useSendAnimation: true,
|
useSendAnimation: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import { $i, getAccounts } from "@/account";
|
import { $i, getAccounts } from "@/account";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
|
@ -74,12 +76,12 @@ defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// ServiceWorker registration
|
// ServiceWorker registration
|
||||||
let registration = $ref<ServiceWorkerRegistration | undefined>();
|
let registration = ref<ServiceWorkerRegistration | undefined>();
|
||||||
// If this browser supports push notification
|
// If this browser supports push notification
|
||||||
let supported = $ref(false);
|
let supported = ref(false);
|
||||||
// If this browser has already subscribed to push notification
|
// If this browser has already subscribed to push notification
|
||||||
let pushSubscription = $ref<PushSubscription | null>(null);
|
let pushSubscription = ref<PushSubscription | null>(null);
|
||||||
let pushRegistrationInServer = $ref<
|
let pushRegistrationInServer = ref<
|
||||||
| {
|
| {
|
||||||
state?: string;
|
state?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
@ -91,11 +93,12 @@ let pushRegistrationInServer = $ref<
|
||||||
>();
|
>();
|
||||||
|
|
||||||
function subscribe() {
|
function subscribe() {
|
||||||
if (!registration || !supported || !instance.swPublickey) return;
|
if (!registration.value || !supported.value || !instance.swPublickey)
|
||||||
|
return;
|
||||||
|
|
||||||
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
||||||
return promiseDialog(
|
return promiseDialog(
|
||||||
registration.pushManager
|
registration.value.pushManager
|
||||||
.subscribe({
|
.subscribe({
|
||||||
userVisibleOnly: true,
|
userVisibleOnly: true,
|
||||||
applicationServerKey: urlBase64ToUint8Array(
|
applicationServerKey: urlBase64ToUint8Array(
|
||||||
|
@ -104,10 +107,10 @@ function subscribe() {
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
async (subscription) => {
|
async (subscription) => {
|
||||||
pushSubscription = subscription;
|
pushSubscription.value = subscription;
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
pushRegistrationInServer = await api("sw/register", {
|
pushRegistrationInServer.value = await api("sw/register", {
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
||||||
auth: encode(subscription.getKey("auth")),
|
auth: encode(subscription.getKey("auth")),
|
||||||
publickey: encode(subscription.getKey("p256dh")),
|
publickey: encode(subscription.getKey("p256dh")),
|
||||||
|
@ -136,12 +139,12 @@ function subscribe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unsubscribe() {
|
async function unsubscribe() {
|
||||||
if (!pushSubscription) return;
|
if (!pushSubscription.value) return;
|
||||||
|
|
||||||
const endpoint = pushSubscription.endpoint;
|
const endpoint = pushSubscription.value.endpoint;
|
||||||
const accounts = await getAccounts();
|
const accounts = await getAccounts();
|
||||||
|
|
||||||
pushRegistrationInServer = undefined;
|
pushRegistrationInServer.value = undefined;
|
||||||
|
|
||||||
if ($i && accounts.length >= 2) {
|
if ($i && accounts.length >= 2) {
|
||||||
apiWithDialog("sw/unregister", {
|
apiWithDialog("sw/unregister", {
|
||||||
|
@ -149,11 +152,11 @@ async function unsubscribe() {
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pushSubscription.unsubscribe();
|
pushSubscription.value.unsubscribe();
|
||||||
apiWithDialog("sw/unregister", {
|
apiWithDialog("sw/unregister", {
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
pushSubscription = null;
|
pushSubscription.value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,20 +187,21 @@ if (navigator.serviceWorker == null) {
|
||||||
// TODO: よしなに?
|
// TODO: よしなに?
|
||||||
} else {
|
} else {
|
||||||
navigator.serviceWorker.ready.then(async (swr) => {
|
navigator.serviceWorker.ready.then(async (swr) => {
|
||||||
registration = swr;
|
registration.value = swr;
|
||||||
|
|
||||||
pushSubscription = await registration.pushManager.getSubscription();
|
pushSubscription.value =
|
||||||
|
await registration.value.pushManager.getSubscription();
|
||||||
|
|
||||||
if (instance.swPublickey && "PushManager" in window && $i && $i.token) {
|
if (instance.swPublickey && "PushManager" in window && $i && $i.token) {
|
||||||
supported = true;
|
supported.value = true;
|
||||||
|
|
||||||
if (pushSubscription) {
|
if (pushSubscription.value) {
|
||||||
const res = await api("sw/show-registration", {
|
const res = await api("sw/show-registration", {
|
||||||
endpoint: pushSubscription.endpoint,
|
endpoint: pushSubscription.value.endpoint,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
pushRegistrationInServer = res;
|
pushRegistrationInServer.value = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +209,6 @@ if (navigator.serviceWorker == null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
pushRegistrationInServer: $$(pushRegistrationInServer),
|
pushRegistrationInServer: pushRegistrationInServer,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, watch } from "vue";
|
import { onMounted, watch, ref } from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkReactionIcon from "@/components/MkReactionIcon.vue";
|
import MkReactionIcon from "@/components/MkReactionIcon.vue";
|
||||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||||
|
@ -46,28 +46,28 @@ const props = defineProps<{
|
||||||
noteId: misskey.entities.Note["id"];
|
noteId: misskey.entities.Note["id"];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let note = $ref<misskey.entities.Note>();
|
let note = ref<misskey.entities.Note>();
|
||||||
let tab = $ref<string>();
|
let tab = ref<string>();
|
||||||
let reactions = $ref<string[]>();
|
let reactions = ref<string[]>();
|
||||||
let users = $ref();
|
let users = ref();
|
||||||
|
|
||||||
watch($$(tab), async () => {
|
watch(tab, async () => {
|
||||||
const res = await os.api("notes/reactions", {
|
const res = await os.api("notes/reactions", {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
type: tab,
|
type: tab.value,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
});
|
});
|
||||||
|
|
||||||
users = res.map((x) => x.user);
|
users.value = res.map((x) => x.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.api("notes/show", {
|
os.api("notes/show", {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
reactions = Object.keys(res.reactions);
|
reactions.value = Object.keys(res.reactions);
|
||||||
tab = reactions[0];
|
tab.value = reactions.value[0];
|
||||||
note = res;
|
note.value = res;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -70,13 +70,13 @@ useTooltip(buttonRef, async (showing) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let hasRenotedBefore = $ref(false);
|
let hasRenotedBefore = ref(false);
|
||||||
os.api("notes/renotes", {
|
os.api("notes/renotes", {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
userId: $i.id,
|
userId: $i.id,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
hasRenotedBefore = res.length > 0;
|
hasRenotedBefore.value = res.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
|
@ -94,7 +94,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
renoteId: props.note.id,
|
renoteId: props.note.id,
|
||||||
visibility: "public",
|
visibility: "public",
|
||||||
});
|
});
|
||||||
hasRenotedBefore = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
ev &&
|
ev &&
|
||||||
((ev.currentTarget ?? ev.target) as
|
((ev.currentTarget ?? ev.target) as
|
||||||
|
@ -121,7 +121,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
renoteId: props.note.id,
|
renoteId: props.note.id,
|
||||||
visibility: "home",
|
visibility: "home",
|
||||||
});
|
});
|
||||||
hasRenotedBefore = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
ev &&
|
ev &&
|
||||||
((ev.currentTarget ?? ev.target) as
|
((ev.currentTarget ?? ev.target) as
|
||||||
|
@ -149,7 +149,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
visibility: "specified",
|
visibility: "specified",
|
||||||
visibleUserIds: props.note.visibleUserIds,
|
visibleUserIds: props.note.visibleUserIds,
|
||||||
});
|
});
|
||||||
hasRenotedBefore = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
ev &&
|
ev &&
|
||||||
((ev.currentTarget ?? ev.target) as
|
((ev.currentTarget ?? ev.target) as
|
||||||
|
@ -174,7 +174,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
renoteId: props.note.id,
|
renoteId: props.note.id,
|
||||||
visibility: "followers",
|
visibility: "followers",
|
||||||
});
|
});
|
||||||
hasRenotedBefore = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
ev &&
|
ev &&
|
||||||
((ev.currentTarget ?? ev.target) as
|
((ev.currentTarget ?? ev.target) as
|
||||||
|
@ -212,7 +212,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
localOnly: true,
|
localOnly: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
hasRenotedBefore = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
ev &&
|
ev &&
|
||||||
((ev.currentTarget ?? ev.target) as
|
((ev.currentTarget ?? ev.target) as
|
||||||
|
@ -242,7 +242,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasRenotedBefore) {
|
if (hasRenotedBefore.value) {
|
||||||
buttonActions.push({
|
buttonActions.push({
|
||||||
text: i18n.ts.unrenote,
|
text: i18n.ts.unrenote,
|
||||||
icon: "ph-trash ph-bold ph-lg",
|
icon: "ph-trash ph-bold ph-lg",
|
||||||
|
@ -251,7 +251,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
os.api("notes/unrenote", {
|
os.api("notes/unrenote", {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
});
|
});
|
||||||
hasRenotedBefore = false;
|
hasRenotedBefore.value = false;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref, computed } from "vue";
|
||||||
import { toUnicode } from "punycode/";
|
import { toUnicode } from "punycode/";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
|
@ -172,19 +172,19 @@ import { login } from "@/account";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let signing = $ref(false);
|
let signing = ref(false);
|
||||||
let user = $ref(null);
|
let user = ref(null);
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let password = $ref("");
|
let password = ref("");
|
||||||
let token = $ref("");
|
let token = ref("");
|
||||||
let host = $ref(toUnicode(configHost));
|
let host = ref(toUnicode(configHost));
|
||||||
let totpLogin = $ref(false);
|
let totpLogin = ref(false);
|
||||||
let challengeData = $ref(null);
|
let challengeData = ref(null);
|
||||||
let queryingKey = $ref(false);
|
let queryingKey = ref(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
let hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
let reCaptchaResponse = ref(null);
|
||||||
|
|
||||||
const meta = $computed(() => instance);
|
const meta = computed(() => instance);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "login", v: any): void;
|
(ev: "login", v: any): void;
|
||||||
|
@ -210,13 +210,13 @@ const props = defineProps({
|
||||||
|
|
||||||
function onUsernameChange() {
|
function onUsernameChange() {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
username: username,
|
username: username.value,
|
||||||
}).then(
|
}).then(
|
||||||
(userResponse) => {
|
(userResponse) => {
|
||||||
user = userResponse;
|
user.value = userResponse;
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
user = null;
|
user.value = null;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -228,38 +228,40 @@ function onLogin(res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryKey() {
|
function queryKey() {
|
||||||
queryingKey = true;
|
queryingKey.value = true;
|
||||||
return navigator.credentials
|
return navigator.credentials
|
||||||
.get({
|
.get({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
challenge: byteify(challengeData.challenge, "base64"),
|
challenge: byteify(challengeData.value.challenge, "base64"),
|
||||||
allowCredentials: challengeData.securityKeys.map((key) => ({
|
allowCredentials: challengeData.value.securityKeys.map(
|
||||||
id: byteify(key.id, "hex"),
|
(key) => ({
|
||||||
type: "public-key",
|
id: byteify(key.id, "hex"),
|
||||||
transports: ["usb", "nfc", "ble", "internal"],
|
type: "public-key",
|
||||||
})),
|
transports: ["usb", "nfc", "ble", "internal"],
|
||||||
|
}),
|
||||||
|
),
|
||||||
timeout: 60 * 1000,
|
timeout: 60 * 1000,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
queryingKey = false;
|
queryingKey.value = false;
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
})
|
})
|
||||||
.then((credential) => {
|
.then((credential) => {
|
||||||
queryingKey = false;
|
queryingKey.value = false;
|
||||||
signing = true;
|
signing.value = true;
|
||||||
return os.api("signin", {
|
return os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
signature: hexify(credential.response.signature),
|
signature: hexify(credential.response.signature),
|
||||||
authenticatorData: hexify(
|
authenticatorData: hexify(
|
||||||
credential.response.authenticatorData,
|
credential.response.authenticatorData,
|
||||||
),
|
),
|
||||||
clientDataJSON: hexify(credential.response.clientDataJSON),
|
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||||
credentialId: credential.id,
|
credentialId: credential.id,
|
||||||
challengeId: challengeData.challengeId,
|
challengeId: challengeData.value.challengeId,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -272,39 +274,42 @@ function queryKey() {
|
||||||
type: "error",
|
type: "error",
|
||||||
text: i18n.ts.signinFailed,
|
text: i18n.ts.signinFailed,
|
||||||
});
|
});
|
||||||
signing = false;
|
signing.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
signing = true;
|
signing.value = true;
|
||||||
console.log("submit");
|
console.log("submit");
|
||||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
||||||
if (window.PublicKeyCredential && user.securityKeys) {
|
if (window.PublicKeyCredential && user.value.securityKeys) {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
totpLogin = true;
|
totpLogin.value = true;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
challengeData = res;
|
challengeData.value = res;
|
||||||
return queryKey();
|
return queryKey();
|
||||||
})
|
})
|
||||||
.catch(loginFailed);
|
.catch(loginFailed);
|
||||||
} else {
|
} else {
|
||||||
totpLogin = true;
|
totpLogin.value = true;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
token: user && user.twoFactorEnabled ? token : undefined,
|
token:
|
||||||
|
user.value && user.value.twoFactorEnabled
|
||||||
|
? token.value
|
||||||
|
: undefined,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
emit("login", res);
|
emit("login", res);
|
||||||
|
@ -354,9 +359,9 @@ function loginFailed(err) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeData = null;
|
challengeData.value = null;
|
||||||
totpLogin = false;
|
totpLogin.value = false;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPassword() {
|
function resetPassword() {
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MkSignin from "@/components/MkSignin.vue";
|
import MkSignin from "@/components/MkSignin.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
|
@ -34,15 +36,15 @@ const emit = defineEmits<{
|
||||||
(ev: "cancelled"): void;
|
(ev: "cancelled"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit("cancelled");
|
emit("cancelled");
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLogin(res) {
|
function onLogin(res) {
|
||||||
emit("done", res);
|
emit("done", res);
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -284,6 +284,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import getPasswordStrength from "syuilo-password-strength";
|
import getPasswordStrength from "syuilo-password-strength";
|
||||||
import { toUnicode } from "punycode/";
|
import { toUnicode } from "punycode/";
|
||||||
import MkButton from "./MkButton.vue";
|
import MkButton from "./MkButton.vue";
|
||||||
|
@ -312,14 +314,14 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const host = toUnicode(config.host);
|
const host = toUnicode(config.host);
|
||||||
|
|
||||||
let hcaptcha = $ref();
|
let hcaptcha = ref();
|
||||||
let recaptcha = $ref();
|
let recaptcha = ref();
|
||||||
|
|
||||||
let username: string = $ref("");
|
let username: string = ref("");
|
||||||
let password: string = $ref("");
|
let password: string = ref("");
|
||||||
let retypedPassword: string = $ref("");
|
let retypedPassword: string = ref("");
|
||||||
let invitationCode: string = $ref("");
|
let invitationCode: string = ref("");
|
||||||
let email = $ref("");
|
let email = ref("");
|
||||||
let usernameState:
|
let usernameState:
|
||||||
| null
|
| null
|
||||||
| "wait"
|
| "wait"
|
||||||
|
@ -328,8 +330,8 @@ let usernameState:
|
||||||
| "error"
|
| "error"
|
||||||
| "invalid-format"
|
| "invalid-format"
|
||||||
| "min-range"
|
| "min-range"
|
||||||
| "max-range" = $ref(null);
|
| "max-range" = ref(null);
|
||||||
let invitationState: null | "entered" = $ref(null);
|
let invitationState: null | "entered" = ref(null);
|
||||||
let emailState:
|
let emailState:
|
||||||
| null
|
| null
|
||||||
| "wait"
|
| "wait"
|
||||||
|
@ -340,79 +342,79 @@ let emailState:
|
||||||
| "unavailable:mx"
|
| "unavailable:mx"
|
||||||
| "unavailable:smtp"
|
| "unavailable:smtp"
|
||||||
| "unavailable"
|
| "unavailable"
|
||||||
| "error" = $ref(null);
|
| "error" = ref(null);
|
||||||
let passwordStrength: "" | "low" | "medium" | "high" = $ref("");
|
let passwordStrength: "" | "low" | "medium" | "high" = ref("");
|
||||||
let passwordRetypeState: null | "match" | "not-match" = $ref(null);
|
let passwordRetypeState: null | "match" | "not-match" = ref(null);
|
||||||
let submitting: boolean = $ref(false);
|
let submitting: boolean = ref(false);
|
||||||
let ToSAgreement: boolean = $ref(false);
|
let ToSAgreement: boolean = ref(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
let hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
let reCaptchaResponse = ref(null);
|
||||||
|
|
||||||
const shouldDisableSubmitting = $computed((): boolean => {
|
const shouldDisableSubmitting = computed((): boolean => {
|
||||||
return (
|
return (
|
||||||
submitting ||
|
submitting.value ||
|
||||||
(instance.tosUrl && !ToSAgreement) ||
|
(instance.tosUrl && !ToSAgreement.value) ||
|
||||||
(instance.enableHcaptcha && !hCaptchaResponse) ||
|
(instance.enableHcaptcha && !hCaptchaResponse.value) ||
|
||||||
(instance.enableRecaptcha && !reCaptchaResponse) ||
|
(instance.enableRecaptcha && !reCaptchaResponse.value) ||
|
||||||
passwordRetypeState === "not-match"
|
passwordRetypeState.value === "not-match"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onChangeInvitationCode(): void {
|
function onChangeInvitationCode(): void {
|
||||||
if (invitationCode === "") {
|
if (invitationCode.value === "") {
|
||||||
invitationState = null;
|
invitationState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
invitationState = "entered";
|
invitationState.value = "entered";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeUsername(): void {
|
function onChangeUsername(): void {
|
||||||
if (username === "") {
|
if (username.value === "") {
|
||||||
usernameState = null;
|
usernameState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const err = !username.match(/^[a-zA-Z0-9_]+$/)
|
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
|
||||||
? "invalid-format"
|
? "invalid-format"
|
||||||
: username.length < 1
|
: username.value.length < 1
|
||||||
? "min-range"
|
? "min-range"
|
||||||
: username.length > 20
|
: username.value.length > 20
|
||||||
? "max-range"
|
? "max-range"
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
usernameState = err;
|
usernameState.value = err;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usernameState = "wait";
|
usernameState.value = "wait";
|
||||||
|
|
||||||
os.api("username/available", {
|
os.api("username/available", {
|
||||||
username,
|
username: username.value,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
usernameState = result.available ? "ok" : "unavailable";
|
usernameState.value = result.available ? "ok" : "unavailable";
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
usernameState = "error";
|
usernameState.value = "error";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeEmail(): void {
|
function onChangeEmail(): void {
|
||||||
if (email === "") {
|
if (email.value === "") {
|
||||||
emailState = null;
|
emailState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emailState = "wait";
|
emailState.value = "wait";
|
||||||
|
|
||||||
os.api("email-address/available", {
|
os.api("email-address/available", {
|
||||||
emailAddress: email,
|
emailAddress: email.value,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
emailState = result.available
|
emailState.value = result.available
|
||||||
? "ok"
|
? "ok"
|
||||||
: result.reason === "used"
|
: result.reason === "used"
|
||||||
? "unavailable:used"
|
? "unavailable:used"
|
||||||
|
@ -427,54 +429,55 @@ function onChangeEmail(): void {
|
||||||
: "unavailable";
|
: "unavailable";
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
emailState = "error";
|
emailState.value = "error";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePassword(): void {
|
function onChangePassword(): void {
|
||||||
if (password === "") {
|
if (password.value === "") {
|
||||||
passwordStrength = "";
|
passwordStrength.value = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const strength = getPasswordStrength(password);
|
const strength = getPasswordStrength(password.value);
|
||||||
passwordStrength =
|
passwordStrength.value =
|
||||||
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
|
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePasswordRetype(): void {
|
function onChangePasswordRetype(): void {
|
||||||
if (retypedPassword === "") {
|
if (retypedPassword.value === "") {
|
||||||
passwordRetypeState = null;
|
passwordRetypeState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordRetypeState = password === retypedPassword ? "match" : "not-match";
|
passwordRetypeState.value =
|
||||||
|
password.value === retypedPassword.value ? "match" : "not-match";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit(): void {
|
function onSubmit(): void {
|
||||||
if (submitting) return;
|
if (submitting.value) return;
|
||||||
submitting = true;
|
submitting.value = true;
|
||||||
|
|
||||||
os.api("signup", {
|
os.api("signup", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
emailAddress: email,
|
emailAddress: email.value,
|
||||||
invitationCode,
|
invitationCode: invitationCode.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (instance.emailRequiredForSignup) {
|
if (instance.emailRequiredForSignup) {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: i18n.ts._signup.almostThere,
|
title: i18n.ts._signup.almostThere,
|
||||||
text: i18n.t("_signup.emailSent", { email }),
|
text: i18n.t("_signup.emailSent", { email: email.value }),
|
||||||
});
|
});
|
||||||
emit("signupEmailPending");
|
emit("signupEmailPending");
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
emit("signup", res);
|
emit("signup", res);
|
||||||
|
|
||||||
|
@ -485,9 +488,9 @@ function onSubmit(): void {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
submitting = false;
|
submitting.value = false;
|
||||||
hcaptcha.reset?.();
|
hcaptcha.value.reset?.();
|
||||||
recaptcha.reset?.();
|
recaptcha.value.reset?.();
|
||||||
|
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import XSignup from "@/components/MkSignup.vue";
|
import XSignup from "@/components/MkSignup.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -38,14 +40,14 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
function onSignup(res) {
|
function onSignup(res) {
|
||||||
emit("done", res);
|
emit("done", res);
|
||||||
dialog?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSignupEmailPending() {
|
function onSignupEmailPending() {
|
||||||
dialog?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -217,23 +217,23 @@ const isLong =
|
||||||
(props.note.text.split("\n").length > 10 ||
|
(props.note.text.split("\n").length > 10 ||
|
||||||
props.note.text.length > 800)) ||
|
props.note.text.length > 800)) ||
|
||||||
props.note.files.length > 4);
|
props.note.files.length > 4);
|
||||||
const collapsed = $ref(props.note.cw == null && isLong);
|
const collapsed = ref(props.note.cw == null && isLong);
|
||||||
const urls = props.note.text
|
const urls = props.note.text
|
||||||
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let showContent = $ref(false);
|
let showContent = ref(false);
|
||||||
|
|
||||||
const mfms = props.note.text
|
const mfms = props.note.text
|
||||||
? extractMfmWithAnimation(mfm.parse(props.note.text))
|
? extractMfmWithAnimation(mfm.parse(props.note.text))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const hasMfm = $ref(mfms && mfms.length > 0);
|
const hasMfm = ref(mfms && mfms.length > 0);
|
||||||
|
|
||||||
let disableMfm = $ref(defaultStore.state.animatedMfm);
|
let disableMfm = ref(defaultStore.state.animatedMfm);
|
||||||
|
|
||||||
async function toggleMfm() {
|
async function toggleMfm() {
|
||||||
if (disableMfm) {
|
if (disableMfm.value) {
|
||||||
if (!defaultStore.state.animatedMfmWarnShown) {
|
if (!defaultStore.state.animatedMfmWarnShown) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
|
@ -244,9 +244,9 @@ async function toggleMfm() {
|
||||||
defaultStore.set("animatedMfmWarnShown", true);
|
defaultStore.set("animatedMfmWarnShown", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
disableMfm = false;
|
disableMfm.value = false;
|
||||||
} else {
|
} else {
|
||||||
disableMfm = true;
|
disableMfm.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, watch, onBeforeUnmount } from "vue";
|
import { onMounted, watch, onBeforeUnmount, ref } from "vue";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
|
||||||
const loaded = !!window.TagCanvas;
|
const loaded = !!window.TagCanvas;
|
||||||
|
@ -39,13 +39,13 @@ const idForTags = Array.from(Array(16))
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
let available = $ref(false);
|
let available = ref(false);
|
||||||
let rootEl = $ref<HTMLElement | null>(null);
|
let rootEl = ref<HTMLElement | null>(null);
|
||||||
let canvasEl = $ref<HTMLCanvasElement | null>(null);
|
let canvasEl = ref<HTMLCanvasElement | null>(null);
|
||||||
let tagsEl = $ref<HTMLElement | null>(null);
|
let tagsEl = ref<HTMLElement | null>(null);
|
||||||
let width = $ref(300);
|
let width = ref(300);
|
||||||
|
|
||||||
watch($$(available), () => {
|
watch(available, () => {
|
||||||
try {
|
try {
|
||||||
window.TagCanvas.Start(idForCanvas, idForTags, {
|
window.TagCanvas.Start(idForCanvas, idForTags, {
|
||||||
textColour: "#ffffff",
|
textColour: "#ffffff",
|
||||||
|
@ -70,10 +70,10 @@ watch($$(available), () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
width = rootEl.offsetWidth;
|
width.value = rootEl.value.offsetWidth;
|
||||||
|
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
available = true;
|
available.value = true;
|
||||||
} else {
|
} else {
|
||||||
document.head
|
document.head
|
||||||
.appendChild(
|
.appendChild(
|
||||||
|
@ -82,7 +82,7 @@ onMounted(() => {
|
||||||
src: "/client-assets/tagcanvas.min.js",
|
src: "/client-assets/tagcanvas.min.js",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.addEventListener("load", () => (available = true));
|
.addEventListener("load", () => (available.value = true));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, provide, onUnmounted } from "vue";
|
import { computed, provide, onUnmounted, ref } from "vue";
|
||||||
import XNotes from "@/components/MkNotes.vue";
|
import XNotes from "@/components/MkNotes.vue";
|
||||||
import MkInfo from "@/components/MkInfo.vue";
|
import MkInfo from "@/components/MkInfo.vue";
|
||||||
import { stream } from "@/stream";
|
import { stream } from "@/stream";
|
||||||
|
@ -45,7 +45,7 @@ const props = defineProps<{
|
||||||
sound?: boolean;
|
sound?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let queue = $ref(0);
|
let queue = ref(0);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "note"): void;
|
(ev: "note"): void;
|
||||||
|
@ -57,10 +57,10 @@ provide(
|
||||||
computed(() => props.src === "channel"),
|
computed(() => props.src === "channel"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const tlComponent: InstanceType<typeof XNotes> = $ref();
|
const tlComponent: InstanceType<typeof XNotes> = ref();
|
||||||
|
|
||||||
const prepend = (note) => {
|
const prepend = (note) => {
|
||||||
tlComponent.pagingComponent?.prepend(note);
|
tlComponent.value.pagingComponent?.prepend(note);
|
||||||
|
|
||||||
emit("note");
|
emit("note");
|
||||||
|
|
||||||
|
@ -70,16 +70,16 @@ const prepend = (note) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUserAdded = () => {
|
const onUserAdded = () => {
|
||||||
tlComponent.pagingComponent?.reload();
|
tlComponent.value.pagingComponent?.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUserRemoved = () => {
|
const onUserRemoved = () => {
|
||||||
tlComponent.pagingComponent?.reload();
|
tlComponent.value.pagingComponent?.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeFollowing = () => {
|
const onChangeFollowing = () => {
|
||||||
if (!tlComponent.pagingComponent?.backed) {
|
if (!tlComponent.value.pagingComponent?.backed) {
|
||||||
tlComponent.pagingComponent?.reload();
|
tlComponent.value.pagingComponent?.reload();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -30,11 +30,11 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("high");
|
const zIndex = os.claimZIndex("high");
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}, 4000);
|
}, 4000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import { permissions as kinds } from "firefish-js";
|
import { permissions as kinds } from "firefish-js";
|
||||||
import MkInput from "./form/input.vue";
|
import MkInput from "./form/input.vue";
|
||||||
|
@ -72,37 +74,39 @@ const emit = defineEmits<{
|
||||||
(ev: "done", result: { name: string | null; permissions: string[] }): void;
|
(ev: "done", result: { name: string | null; permissions: string[] }): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
let name = $ref(props.initialName);
|
let name = ref(props.initialName);
|
||||||
let permissions = $ref({});
|
let permissions = ref({});
|
||||||
|
|
||||||
if (props.initialPermissions) {
|
if (props.initialPermissions) {
|
||||||
for (const kind of props.initialPermissions) {
|
for (const kind of props.initialPermissions) {
|
||||||
permissions[kind] = true;
|
permissions.value[kind] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const kind of kinds) {
|
for (const kind of kinds) {
|
||||||
permissions[kind] = false;
|
permissions.value[kind] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ok(): void {
|
function ok(): void {
|
||||||
emit("done", {
|
emit("done", {
|
||||||
name: name,
|
name: name.value,
|
||||||
permissions: Object.keys(permissions).filter((p) => permissions[p]),
|
permissions: Object.keys(permissions.value).filter(
|
||||||
|
(p) => permissions.value[p],
|
||||||
|
),
|
||||||
});
|
});
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableAll(): void {
|
function disableAll(): void {
|
||||||
for (const p in permissions) {
|
for (const p in permissions.value) {
|
||||||
permissions[p] = false;
|
permissions.value[p] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAll(): void {
|
function enableAll(): void {
|
||||||
for (const p in permissions) {
|
for (const p in permissions.value) {
|
||||||
permissions[p] = true;
|
permissions.value[p] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -206,7 +206,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import XSettings from "@/pages/settings/profile.vue";
|
import XSettings from "@/pages/settings/profile.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -250,7 +250,7 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
const tutorial = computed({
|
const tutorial = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -278,7 +278,7 @@ const reduceAnimation = computed(
|
||||||
|
|
||||||
function close(res) {
|
function close(res) {
|
||||||
tutorial.value = -1;
|
tutorial.value = -1;
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { shallowRef } from "vue";
|
import { shallowRef, ref } from "vue";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import MkSparkle from "@/components/MkSparkle.vue";
|
import MkSparkle from "@/components/MkSparkle.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -43,18 +43,18 @@ import * as os from "@/os";
|
||||||
|
|
||||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
let newRelease = $ref(false);
|
let newRelease = ref(false);
|
||||||
let data = $ref(Object);
|
let data = ref(Object);
|
||||||
|
|
||||||
os.api("release").then((res) => {
|
os.api("release").then((res) => {
|
||||||
data = res;
|
data.value = res;
|
||||||
newRelease = version === data?.version;
|
newRelease.value = version === data.value?.version;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Version: ${version}`);
|
console.log(`Version: ${version}`);
|
||||||
console.log(`Data version: ${data.version}`);
|
console.log(`Data version: ${data.value.version}`);
|
||||||
console.log(newRelease);
|
console.log(newRelease.value);
|
||||||
console.log(data);
|
console.log(data.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted } from "vue";
|
import { onUnmounted, ref } from "vue";
|
||||||
import { url as local, lang } from "@/config";
|
import { url as local, lang } from "@/config";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -117,22 +117,22 @@ const props = withDefaults(
|
||||||
const self = props.url.startsWith(local);
|
const self = props.url.startsWith(local);
|
||||||
const attr = self ? "to" : "href";
|
const attr = self ? "to" : "href";
|
||||||
const target = self ? null : "_blank";
|
const target = self ? null : "_blank";
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
let title = $ref<string | null>(null);
|
let title = ref<string | null>(null);
|
||||||
let description = $ref<string | null>(null);
|
let description = ref<string | null>(null);
|
||||||
let thumbnail = $ref<string | null>(null);
|
let thumbnail = ref<string | null>(null);
|
||||||
let icon = $ref<string | null>(null);
|
let icon = ref<string | null>(null);
|
||||||
let sitename = $ref<string | null>(null);
|
let sitename = ref<string | null>(null);
|
||||||
let player = $ref({
|
let player = ref({
|
||||||
url: null,
|
url: null,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
});
|
});
|
||||||
let playerEnabled = $ref(false);
|
let playerEnabled = ref(false);
|
||||||
let tweetId = $ref<string | null>(null);
|
let tweetId = ref<string | null>(null);
|
||||||
let tweetExpanded = $ref(props.detail);
|
let tweetExpanded = ref(props.detail);
|
||||||
const embedId = `embed${Math.random().toString().replace(/\D/, "")}`;
|
const embedId = `embed${Math.random().toString().replace(/\D/, "")}`;
|
||||||
let tweetHeight = $ref(150);
|
let tweetHeight = ref(150);
|
||||||
|
|
||||||
const requestUrl = new URL(props.url);
|
const requestUrl = new URL(props.url);
|
||||||
if (!["http:", "https:"].includes(requestUrl.protocol))
|
if (!["http:", "https:"].includes(requestUrl.protocol))
|
||||||
|
@ -143,7 +143,7 @@ if (
|
||||||
requestUrl.hostname === "mobile.twitter.com"
|
requestUrl.hostname === "mobile.twitter.com"
|
||||||
) {
|
) {
|
||||||
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
|
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
|
||||||
if (m) tweetId = m[1];
|
if (m) tweetId.value = m[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -162,13 +162,13 @@ fetch(
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
res.json().then((info) => {
|
res.json().then((info) => {
|
||||||
if (info.url == null) return;
|
if (info.url == null) return;
|
||||||
title = info.title;
|
title.value = info.title;
|
||||||
description = info.description;
|
description.value = info.description;
|
||||||
thumbnail = info.thumbnail;
|
thumbnail.value = info.thumbnail;
|
||||||
icon = info.icon;
|
icon.value = info.icon;
|
||||||
sitename = info.sitename;
|
sitename.value = info.sitename;
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
player = info.player;
|
player.value = info.player;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ function adjustTweetHeight(message: any) {
|
||||||
if (embed?.method !== "twttr.private.resize") return;
|
if (embed?.method !== "twttr.private.resize") return;
|
||||||
if (embed?.id !== embedId) return;
|
if (embed?.id !== embedId) return;
|
||||||
const height = embed?.params[0]?.height;
|
const height = embed?.params[0]?.height;
|
||||||
if (height) tweetHeight = height;
|
if (height) tweetHeight.value = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any).addEventListener("message", adjustTweetHeight);
|
(window as any).addEventListener("message", adjustTweetHeight);
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("middle");
|
const zIndex = os.claimZIndex("middle");
|
||||||
let top = $ref(0);
|
let top = ref(0);
|
||||||
let left = $ref(0);
|
let left = ref(0);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const rect = props.source.getBoundingClientRect();
|
const rect = props.source.getBoundingClientRect();
|
||||||
|
@ -38,8 +38,8 @@ onMounted(() => {
|
||||||
window.pageXOffset;
|
window.pageXOffset;
|
||||||
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||||
|
|
||||||
top = y;
|
top.value = y;
|
||||||
left = x;
|
left.value = x;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -41,7 +43,7 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let chartValues = $ref<number[] | null>(null);
|
let chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
if (props.withChart) {
|
if (props.withChart) {
|
||||||
os.apiGet("charts/user/notes", {
|
os.apiGet("charts/user/notes", {
|
||||||
|
@ -51,7 +53,7 @@ if (props.withChart) {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||||
res.inc.splice(0, 1);
|
res.inc.splice(0, 1);
|
||||||
chartValues = res.inc;
|
chartValues.value = res.inc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -86,6 +86,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
|
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
|
||||||
|
@ -98,13 +100,13 @@ const props = defineProps<{
|
||||||
detailed?: boolean;
|
detailed?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let isLong = $ref(
|
let isLong = ref(
|
||||||
props.detailed &&
|
props.detailed &&
|
||||||
props.user.description &&
|
props.user.description &&
|
||||||
(props.user.description.split("\n").length > 9 ||
|
(props.user.description.split("\n").length > 9 ||
|
||||||
props.user.description.length > 400),
|
props.user.description.length > 400),
|
||||||
);
|
);
|
||||||
let collapsed = $ref(isLong);
|
let collapsed = ref(isLong.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ const props = defineProps<{
|
||||||
user: misskey.entities.User;
|
user: misskey.entities.User;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const text = $computed(() => {
|
const text = computed(() => {
|
||||||
switch (props.user.onlineStatus) {
|
switch (props.user.onlineStatus) {
|
||||||
case "online":
|
case "online":
|
||||||
return i18n.ts.online;
|
return i18n.ts.online;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import MkUserInfo from "@/components/MkUserInfo.vue";
|
import MkUserInfo from "@/components/MkUserInfo.vue";
|
||||||
import * as Acct from "firefish-js/built/acct";
|
import * as Acct from "firefish-js/built/acct";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
|
@ -47,13 +47,13 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("middle");
|
const zIndex = os.claimZIndex("middle");
|
||||||
let user = $ref<misskey.entities.UserDetailed | null>(null);
|
let user = ref<misskey.entities.UserDetailed | null>(null);
|
||||||
let top = $ref(0);
|
let top = ref(0);
|
||||||
let left = $ref(0);
|
let left = ref(0);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof props.q === "object") {
|
if (typeof props.q === "object") {
|
||||||
user = props.q;
|
user.value = props.q;
|
||||||
} else {
|
} else {
|
||||||
const query = props.q.startsWith("@")
|
const query = props.q.startsWith("@")
|
||||||
? Acct.parse(props.q.slice(1))
|
? Acct.parse(props.q.slice(1))
|
||||||
|
@ -61,7 +61,7 @@ onMounted(() => {
|
||||||
|
|
||||||
os.api("users/show", query).then((res) => {
|
os.api("users/show", query).then((res) => {
|
||||||
if (!props.showing) return;
|
if (!props.showing) return;
|
||||||
user = res;
|
user.value = res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ onMounted(() => {
|
||||||
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset;
|
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset;
|
||||||
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||||
|
|
||||||
top = y;
|
top.value = y;
|
||||||
left = x;
|
left.value = x;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import FormSplit from "@/components/form/split.vue";
|
import FormSplit from "@/components/form/split.vue";
|
||||||
|
@ -103,50 +103,50 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let host = $ref("");
|
let host = ref("");
|
||||||
let users: misskey.entities.UserDetailed[] = $ref([]);
|
let users: misskey.entities.UserDetailed[] = ref([]);
|
||||||
let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
|
let recentUsers: misskey.entities.UserDetailed[] = ref([]);
|
||||||
let selected: misskey.entities.UserDetailed | null = $ref(null);
|
let selected: misskey.entities.UserDetailed | null = ref(null);
|
||||||
let dialogEl = $ref();
|
let dialogEl = ref();
|
||||||
|
|
||||||
const search = () => {
|
const search = () => {
|
||||||
if (username === "" && host === "") {
|
if (username.value === "" && host.value === "") {
|
||||||
users = [];
|
users.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
os.api("users/search-by-username-and-host", {
|
os.api("users/search-by-username-and-host", {
|
||||||
username: username,
|
username: username.value,
|
||||||
host: host,
|
host: host.value,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
detail: false,
|
detail: false,
|
||||||
}).then((_users) => {
|
}).then((_users) => {
|
||||||
users = _users;
|
users.value = _users;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ok = () => {
|
const ok = () => {
|
||||||
if (selected == null) return;
|
if (selected.value == null) return;
|
||||||
emit("ok", selected);
|
emit("ok", selected.value);
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
|
|
||||||
// 最近使ったユーザー更新
|
// 最近使ったユーザー更新
|
||||||
let recents = defaultStore.state.recentlyUsedUsers;
|
let recents = defaultStore.state.recentlyUsedUsers;
|
||||||
recents = recents.filter((x) => x !== selected.id);
|
recents = recents.filter((x) => x !== selected.value.id);
|
||||||
recents.unshift(selected.id);
|
recents.unshift(selected.value.id);
|
||||||
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
|
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: defaultStore.state.recentlyUsedUsers,
|
userIds: defaultStore.state.recentlyUsedUsers,
|
||||||
}).then((users) => {
|
}).then((users) => {
|
||||||
recentUsers = users;
|
recentUsers.value = users;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import FormSplit from "@/components/form/split.vue";
|
import FormSplit from "@/components/form/split.vue";
|
||||||
|
@ -99,49 +99,49 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let users: misskey.entities.UserDetailed[] = $ref([]);
|
let users: misskey.entities.UserDetailed[] = ref([]);
|
||||||
let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
|
let recentUsers: misskey.entities.UserDetailed[] = ref([]);
|
||||||
let selected: misskey.entities.UserDetailed | null = $ref(null);
|
let selected: misskey.entities.UserDetailed | null = ref(null);
|
||||||
let dialogEl = $ref();
|
let dialogEl = ref();
|
||||||
|
|
||||||
const search = () => {
|
const search = () => {
|
||||||
if (username === "") {
|
if (username.value === "") {
|
||||||
users = [];
|
users.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
os.api("users/search", {
|
os.api("users/search", {
|
||||||
query: username,
|
query: username.value,
|
||||||
origin: "local",
|
origin: "local",
|
||||||
limit: 10,
|
limit: 10,
|
||||||
detail: false,
|
detail: false,
|
||||||
}).then((_users) => {
|
}).then((_users) => {
|
||||||
users = _users;
|
users.value = _users;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ok = () => {
|
const ok = () => {
|
||||||
if (selected == null) return;
|
if (selected.value == null) return;
|
||||||
emit("ok", selected);
|
emit("ok", selected.value);
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
|
|
||||||
// 最近使ったユーザー更新
|
// 最近使ったユーザー更新
|
||||||
let recents = defaultStore.state.recentlyUsedUsers;
|
let recents = defaultStore.state.recentlyUsedUsers;
|
||||||
recents = recents.filter((x) => x !== selected.id);
|
recents = recents.filter((x) => x !== selected.value.id);
|
||||||
recents.unshift(selected.id);
|
recents.unshift(selected.value.id);
|
||||||
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
|
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: defaultStore.state.recentlyUsedUsers,
|
userIds: defaultStore.state.recentlyUsedUsers,
|
||||||
}).then((users) => {
|
}).then((users) => {
|
||||||
recentUsers = users;
|
recentUsers.value = users;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import XDetails from "@/components/MkUsersTooltip.vue";
|
import XDetails from "@/components/MkUsersTooltip.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { useTooltip } from "@/scripts/use-tooltip";
|
import { useTooltip } from "@/scripts/use-tooltip";
|
||||||
|
@ -38,10 +40,10 @@ const props = defineProps<{
|
||||||
};
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const specified = $ref<HTMLElement>();
|
const specified = ref<HTMLElement>();
|
||||||
|
|
||||||
if (props.note.visibility === "specified") {
|
if (props.note.visibility === "specified") {
|
||||||
useTooltip($$(specified), async (showing) => {
|
useTooltip(specified, async (showing) => {
|
||||||
const users = await os.api("users/show", {
|
const users = await os.api("users/show", {
|
||||||
userIds: props.note.visibleUserIds,
|
userIds: props.note.visibleUserIds,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
@ -53,7 +55,7 @@ if (props.note.visibility === "specified") {
|
||||||
showing,
|
showing,
|
||||||
users,
|
users,
|
||||||
count: props.note.visibleUserIds.length,
|
count: props.note.visibleUserIds.length,
|
||||||
targetElement: specified,
|
targetElement: specified.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed",
|
"closed",
|
||||||
|
|
|
@ -122,12 +122,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, watch } from "vue";
|
import { nextTick, watch, shallowRef, ref } from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -147,18 +147,18 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let v = $ref(props.currentVisibility);
|
let v = ref(props.currentVisibility);
|
||||||
let localOnly = $ref(props.currentLocalOnly);
|
let localOnly = ref(props.currentLocalOnly);
|
||||||
|
|
||||||
watch($$(localOnly), () => {
|
watch(localOnly, () => {
|
||||||
emit("changeLocalOnly", localOnly);
|
emit("changeLocalOnly", localOnly.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
function choose(visibility: (typeof misskey.noteVisibilities)[number]): void {
|
function choose(visibility: (typeof misskey.noteVisibilities)[number]): void {
|
||||||
v = visibility;
|
v.value = visibility;
|
||||||
emit("changeVisibility", visibility);
|
emit("changeVisibility", visibility);
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
modal.close();
|
modal.value.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted, provide } from "vue";
|
import { onBeforeUnmount, onMounted, provide, ref } from "vue";
|
||||||
import contains from "@/scripts/contains";
|
import contains from "@/scripts/contains";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { MenuItem } from "@/types/menu";
|
import { MenuItem } from "@/types/menu";
|
||||||
|
@ -170,17 +170,17 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
provide("inWindow", true);
|
provide("inWindow", true);
|
||||||
|
|
||||||
let rootEl = $ref<HTMLElement | null>();
|
let rootEl = ref<HTMLElement | null>();
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
let beforeClickedAt = 0;
|
let beforeClickedAt = 0;
|
||||||
let maximized = $ref(false);
|
let maximized = ref(false);
|
||||||
let unMaximizedTop = "";
|
let unMaximizedTop = "";
|
||||||
let unMaximizedLeft = "";
|
let unMaximizedLeft = "";
|
||||||
let unMaximizedWidth = "";
|
let unMaximizedWidth = "";
|
||||||
let unMaximizedHeight = "";
|
let unMaximizedHeight = "";
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(evt) {
|
function onKeydown(evt) {
|
||||||
|
@ -200,29 +200,31 @@ function onContextmenu(ev: MouseEvent) {
|
||||||
|
|
||||||
// 最前面へ移動
|
// 最前面へ移動
|
||||||
function top() {
|
function top() {
|
||||||
if (rootEl) {
|
if (rootEl.value) {
|
||||||
rootEl.style.zIndex = os.claimZIndex(props.front ? "middle" : "low");
|
rootEl.value.style.zIndex = os.claimZIndex(
|
||||||
|
props.front ? "middle" : "low",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maximize() {
|
function maximize() {
|
||||||
maximized = true;
|
maximized.value = true;
|
||||||
unMaximizedTop = rootEl.style.top;
|
unMaximizedTop = rootEl.value.style.top;
|
||||||
unMaximizedLeft = rootEl.style.left;
|
unMaximizedLeft = rootEl.value.style.left;
|
||||||
unMaximizedWidth = rootEl.style.width;
|
unMaximizedWidth = rootEl.value.style.width;
|
||||||
unMaximizedHeight = rootEl.style.height;
|
unMaximizedHeight = rootEl.value.style.height;
|
||||||
rootEl.style.top = "0";
|
rootEl.value.style.top = "0";
|
||||||
rootEl.style.left = "0";
|
rootEl.value.style.left = "0";
|
||||||
rootEl.style.width = "100%";
|
rootEl.value.style.width = "100%";
|
||||||
rootEl.style.height = "100%";
|
rootEl.value.style.height = "100%";
|
||||||
}
|
}
|
||||||
|
|
||||||
function unMaximize() {
|
function unMaximize() {
|
||||||
maximized = false;
|
maximized.value = false;
|
||||||
rootEl.style.top = unMaximizedTop;
|
rootEl.value.style.top = unMaximizedTop;
|
||||||
rootEl.style.left = unMaximizedLeft;
|
rootEl.value.style.left = unMaximizedLeft;
|
||||||
rootEl.style.width = unMaximizedWidth;
|
rootEl.value.style.width = unMaximizedWidth;
|
||||||
rootEl.style.height = unMaximizedHeight;
|
rootEl.value.style.height = unMaximizedHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBodyMousedown() {
|
function onBodyMousedown() {
|
||||||
|
@ -239,7 +241,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||||
|
|
||||||
let beforeMaximized = false;
|
let beforeMaximized = false;
|
||||||
|
|
||||||
if (maximized) {
|
if (maximized.value) {
|
||||||
beforeMaximized = true;
|
beforeMaximized = true;
|
||||||
unMaximize();
|
unMaximize();
|
||||||
}
|
}
|
||||||
|
@ -253,7 +255,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||||
|
|
||||||
beforeClickedAt = Date.now();
|
beforeClickedAt = Date.now();
|
||||||
|
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
if (!contains(main, document.activeElement)) main.focus();
|
if (!contains(main, document.activeElement)) main.focus();
|
||||||
|
@ -295,8 +297,8 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||||
if (moveLeft + windowWidth > browserWidth)
|
if (moveLeft + windowWidth > browserWidth)
|
||||||
moveLeft = browserWidth - windowWidth;
|
moveLeft = browserWidth - windowWidth;
|
||||||
|
|
||||||
rootEl.style.left = moveLeft + "px";
|
rootEl.value.style.left = moveLeft + "px";
|
||||||
rootEl.style.top = moveTop + "px";
|
rootEl.value.style.top = moveTop + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beforeMaximized) {
|
if (beforeMaximized) {
|
||||||
|
@ -320,7 +322,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||||
|
|
||||||
// 上ハンドル掴み時
|
// 上ハンドル掴み時
|
||||||
function onTopHandleMousedown(evt) {
|
function onTopHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
|
|
||||||
const base = evt.clientY;
|
const base = evt.clientY;
|
||||||
const height = parseInt(getComputedStyle(main, "").height, 10);
|
const height = parseInt(getComputedStyle(main, "").height, 10);
|
||||||
|
@ -348,7 +350,7 @@ function onTopHandleMousedown(evt) {
|
||||||
|
|
||||||
// 右ハンドル掴み時
|
// 右ハンドル掴み時
|
||||||
function onRightHandleMousedown(evt) {
|
function onRightHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
|
|
||||||
const base = evt.clientX;
|
const base = evt.clientX;
|
||||||
const width = parseInt(getComputedStyle(main, "").width, 10);
|
const width = parseInt(getComputedStyle(main, "").width, 10);
|
||||||
|
@ -374,7 +376,7 @@ function onRightHandleMousedown(evt) {
|
||||||
|
|
||||||
// 下ハンドル掴み時
|
// 下ハンドル掴み時
|
||||||
function onBottomHandleMousedown(evt) {
|
function onBottomHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
|
|
||||||
const base = evt.clientY;
|
const base = evt.clientY;
|
||||||
const height = parseInt(getComputedStyle(main, "").height, 10);
|
const height = parseInt(getComputedStyle(main, "").height, 10);
|
||||||
|
@ -400,7 +402,7 @@ function onBottomHandleMousedown(evt) {
|
||||||
|
|
||||||
// 左ハンドル掴み時
|
// 左ハンドル掴み時
|
||||||
function onLeftHandleMousedown(evt) {
|
function onLeftHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
|
|
||||||
const base = evt.clientX;
|
const base = evt.clientX;
|
||||||
const width = parseInt(getComputedStyle(main, "").width, 10);
|
const width = parseInt(getComputedStyle(main, "").width, 10);
|
||||||
|
@ -453,27 +455,27 @@ function onBottomLeftHandleMousedown(evt) {
|
||||||
// 高さを適用
|
// 高さを適用
|
||||||
function applyTransformHeight(height) {
|
function applyTransformHeight(height) {
|
||||||
if (height > window.innerHeight) height = window.innerHeight;
|
if (height > window.innerHeight) height = window.innerHeight;
|
||||||
rootEl.style.height = height + "px";
|
rootEl.value.style.height = height + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 幅を適用
|
// 幅を適用
|
||||||
function applyTransformWidth(width) {
|
function applyTransformWidth(width) {
|
||||||
if (width > window.innerWidth) width = window.innerWidth;
|
if (width > window.innerWidth) width = window.innerWidth;
|
||||||
rootEl.style.width = width + "px";
|
rootEl.value.style.width = width + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y座標を適用
|
// Y座標を適用
|
||||||
function applyTransformTop(top) {
|
function applyTransformTop(top) {
|
||||||
rootEl.style.top = top + "px";
|
rootEl.value.style.top = top + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
// X座標を適用
|
// X座標を適用
|
||||||
function applyTransformLeft(left) {
|
function applyTransformLeft(left) {
|
||||||
rootEl.style.left = left + "px";
|
rootEl.value.style.left = left + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBrowserResize() {
|
function onBrowserResize() {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
const position = main.getBoundingClientRect();
|
const position = main.getBoundingClientRect();
|
||||||
const browserWidth = window.innerWidth;
|
const browserWidth = window.innerWidth;
|
||||||
const browserHeight = window.innerHeight;
|
const browserHeight = window.innerHeight;
|
||||||
|
@ -491,8 +493,8 @@ onMounted(() => {
|
||||||
if (props.initialWidth) applyTransformWidth(props.initialWidth);
|
if (props.initialWidth) applyTransformWidth(props.initialWidth);
|
||||||
if (props.initialHeight) applyTransformHeight(props.initialHeight);
|
if (props.initialHeight) applyTransformHeight(props.initialHeight);
|
||||||
|
|
||||||
applyTransformTop(window.innerHeight / 2 - rootEl.offsetHeight / 2);
|
applyTransformTop(window.innerHeight / 2 - rootEl.value.offsetHeight / 2);
|
||||||
applyTransformLeft(window.innerWidth / 2 - rootEl.offsetWidth / 2);
|
applyTransformLeft(window.innerWidth / 2 - rootEl.value.offsetWidth / 2);
|
||||||
|
|
||||||
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
||||||
top();
|
top();
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, Ref } from "vue";
|
import { toRefs, Ref, ref } from "vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import Ripple from "@/components/MkRipple.vue";
|
import Ripple from "@/components/MkRipple.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -38,16 +38,16 @@ const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", v: boolean): void;
|
(ev: "update:modelValue", v: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let button = $ref<HTMLElement>();
|
let button = ref<HTMLElement>();
|
||||||
const checked = toRefs(props).modelValue;
|
const checked = toRefs(props).modelValue;
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
emit("update:modelValue", !checked.value);
|
emit("update:modelValue", !checked.value);
|
||||||
|
|
||||||
if (!checked.value) {
|
if (!checked.value) {
|
||||||
const rect = button.getBoundingClientRect();
|
const rect = button.value.getBoundingClientRect();
|
||||||
const x = rect.left + button.offsetWidth / 2;
|
const x = rect.left + button.value.offsetWidth / 2;
|
||||||
const y = rect.top + button.offsetHeight / 2;
|
const y = rect.top + button.value.offsetHeight / 2;
|
||||||
os.popup(Ripple, { x, y, particle: false }, {}, "end");
|
os.popup(Ripple, { x, y, particle: false }, {}, "end");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: any;
|
modelValue: any;
|
||||||
value: any;
|
value: any;
|
||||||
|
@ -24,7 +26,7 @@ const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", value: any): void;
|
(ev: "update:modelValue", value: any): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const checked = $computed(() => props.modelValue === props.value);
|
const checked = computed(() => props.modelValue === props.value);
|
||||||
|
|
||||||
function toggle(x) {
|
function toggle(x) {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
|
|
|
@ -59,7 +59,7 @@ const props = withDefaults(
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputEl = ref<HTMLElement>();
|
const inputEl = ref<HTMLElement>();
|
||||||
const inputVal = $ref(props.modelValue);
|
const inputVal = ref(props.modelValue);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", value: number): void;
|
(ev: "update:modelValue", value: number): void;
|
||||||
|
@ -74,7 +74,7 @@ const steps = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onChange(x) {
|
function onChange(x) {
|
||||||
emit("update:modelValue", inputVal);
|
emit("update:modelValue", inputVal.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tooltipShowing = ref(false);
|
const tooltipShowing = ref(false);
|
||||||
|
@ -85,7 +85,7 @@ function tooltipShow() {
|
||||||
{
|
{
|
||||||
showing: tooltipShowing,
|
showing: tooltipShowing,
|
||||||
text: computed(() => {
|
text: computed(() => {
|
||||||
return props.textConverter(inputVal);
|
return props.textConverter(inputVal.value);
|
||||||
}),
|
}),
|
||||||
targetElement: inputEl,
|
targetElement: inputEl,
|
||||||
},
|
},
|
||||||
|
|
|
@ -156,7 +156,7 @@ export default defineComponent({
|
||||||
v.value = newValue;
|
v.value = newValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch($$(v), () => {
|
watch(v, () => {
|
||||||
if (!props.manualSave) {
|
if (!props.manualSave) {
|
||||||
if (props.debounce) {
|
if (props.debounce) {
|
||||||
debouncedUpdated();
|
debouncedUpdated();
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
||||||
import { url } from "@/config";
|
import { url } from "@/config";
|
||||||
|
@ -31,7 +33,7 @@ const props = withDefaults(
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const active = $computed(() => {
|
const active = computed(() => {
|
||||||
if (props.activeClass == null) return false;
|
if (props.activeClass == null) return false;
|
||||||
const resolved = router.resolve(props.to);
|
const resolved = router.resolve(props.to);
|
||||||
if (resolved == null) return false;
|
if (resolved == null) return false;
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from "vue";
|
import { watch, computed, ref } from "vue";
|
||||||
import type * as misskey from "firefish-js";
|
import type * as misskey from "firefish-js";
|
||||||
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
||||||
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
|
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
|
||||||
|
@ -64,7 +64,7 @@ const emit = defineEmits<{
|
||||||
(ev: "click", v: MouseEvent): void;
|
(ev: "click", v: MouseEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const url = $computed(() =>
|
const url = computed(() =>
|
||||||
defaultStore.state.disableShowingAnimatedImages
|
defaultStore.state.disableShowingAnimatedImages
|
||||||
? getStaticImageUrl(props.user.avatarUrl)
|
? getStaticImageUrl(props.user.avatarUrl)
|
||||||
: props.user.avatarUrl,
|
: props.user.avatarUrl,
|
||||||
|
@ -74,12 +74,12 @@ function onClick(ev: MouseEvent) {
|
||||||
emit("click", ev);
|
emit("click", ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = $ref();
|
let color = ref();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.user.avatarBlurhash,
|
() => props.user.avatarBlurhash,
|
||||||
() => {
|
() => {
|
||||||
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
|
|
@ -121,7 +121,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
import {
|
||||||
|
inject,
|
||||||
|
nextTick,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
computed,
|
||||||
|
} from "vue";
|
||||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
import { popupMenu } from "@/os";
|
import { popupMenu } from "@/os";
|
||||||
import { scrollToTop } from "@/scripts/scroll";
|
import { scrollToTop } from "@/scripts/scroll";
|
||||||
|
@ -165,16 +173,16 @@ const metadata = injectPageMetadata();
|
||||||
const hideTitle = inject("shouldOmitHeaderTitle", false);
|
const hideTitle = inject("shouldOmitHeaderTitle", false);
|
||||||
const thin_ = props.thin || inject("shouldHeaderThin", false);
|
const thin_ = props.thin || inject("shouldHeaderThin", false);
|
||||||
|
|
||||||
const el = $ref<HTMLElement | null>(null);
|
const el = ref<HTMLElement | null>(null);
|
||||||
const tabRefs = {};
|
const tabRefs = {};
|
||||||
const tabHighlightEl = $ref<HTMLElement | null>(null);
|
const tabHighlightEl = ref<HTMLElement | null>(null);
|
||||||
const tabsEl = $ref<HTMLElement | null>(null);
|
const tabsEl = ref<HTMLElement | null>(null);
|
||||||
const bg = ref(null);
|
const bg = ref(null);
|
||||||
let narrow = $ref(false);
|
let narrow = ref(false);
|
||||||
const hasTabs = $computed(() => props.tabs && props.tabs.length > 0);
|
const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
|
||||||
const hasActions = $computed(() => props.actions && props.actions.length > 0);
|
const hasActions = computed(() => props.actions && props.actions.length > 0);
|
||||||
const show = $computed(() => {
|
const show = computed(() => {
|
||||||
return !hideTitle || hasTabs || hasActions;
|
return !hideTitle || hasTabs.value || hasActions.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const openAccountMenu = (ev: MouseEvent) => {
|
const openAccountMenu = (ev: MouseEvent) => {
|
||||||
|
@ -187,8 +195,8 @@ const openAccountMenu = (ev: MouseEvent) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showTabsPopup = (ev: MouseEvent) => {
|
const showTabsPopup = (ev: MouseEvent) => {
|
||||||
if (!hasTabs) return;
|
if (!hasTabs.value) return;
|
||||||
if (!narrow) return;
|
if (!narrow.value) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const menu = props.tabs.map((tab) => ({
|
const menu = props.tabs.map((tab) => ({
|
||||||
|
@ -210,7 +218,7 @@ const onClick = () => {
|
||||||
if (props.to) {
|
if (props.to) {
|
||||||
location.href = props.to;
|
location.href = props.to;
|
||||||
} else {
|
} else {
|
||||||
scrollToTop(el, { behavior: "smooth" });
|
scrollToTop(el.value, { behavior: "smooth" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -244,7 +252,7 @@ onMounted(() => {
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const tabEl = tabRefs[props.tab];
|
const tabEl = tabRefs[props.tab];
|
||||||
if (tabEl && tabHighlightEl) {
|
if (tabEl && tabHighlightEl.value) {
|
||||||
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||||
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
||||||
const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding
|
const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding
|
||||||
|
@ -252,10 +260,10 @@ onMounted(() => {
|
||||||
tabEl.style = `--width: ${tabSizeX}px`;
|
tabEl.style = `--width: ${tabSizeX}px`;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tabHighlightEl.style.width = tabSizeX + "px";
|
tabHighlightEl.value.style.width = tabSizeX + "px";
|
||||||
tabHighlightEl.style.transform = `translateX(${tabEl.offsetLeft}px)`;
|
tabHighlightEl.value.style.transform = `translateX(${tabEl.offsetLeft}px)`;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
tabsEl?.scrollTo({
|
tabsEl.value?.scrollTo({
|
||||||
left: tabEl.offsetLeft - 60,
|
left: tabEl.offsetLeft - 60,
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
});
|
});
|
||||||
|
@ -269,14 +277,14 @@ onMounted(() => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (el && el.parentElement) {
|
if (el.value && el.value.parentElement) {
|
||||||
narrow = el.parentElement.offsetWidth < 500;
|
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||||
ro = new ResizeObserver((entries, observer) => {
|
ro = new ResizeObserver((entries, observer) => {
|
||||||
if (el.parentElement && document.body.contains(el)) {
|
if (el.value.parentElement && document.body.contains(el.value)) {
|
||||||
narrow = el.parentElement.offsetWidth < 500;
|
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ro.observe(el.parentElement);
|
ro.observe(el.value.parentElement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, onMounted, onUnmounted } from "vue";
|
import { inject, onMounted, onUnmounted, ref } from "vue";
|
||||||
import { deviceKind } from "@/scripts/device-kind";
|
import { deviceKind } from "@/scripts/device-kind";
|
||||||
import { ui } from "@/config";
|
import { ui } from "@/config";
|
||||||
|
|
||||||
|
@ -25,18 +25,18 @@ const props = withDefaults(
|
||||||
);
|
);
|
||||||
|
|
||||||
let ro: ResizeObserver,
|
let ro: ResizeObserver,
|
||||||
root = $ref<HTMLElement>(),
|
root = ref<HTMLElement>(),
|
||||||
content = $ref<HTMLElement>(),
|
content = ref<HTMLElement>(),
|
||||||
margin = $ref(0);
|
margin = ref(0);
|
||||||
const shouldSpacerMin = inject("shouldSpacerMin", false);
|
const shouldSpacerMin = inject("shouldSpacerMin", false);
|
||||||
|
|
||||||
const adjust = (rect: { width: number; height: number }) => {
|
const adjust = (rect: { width: number; height: number }) => {
|
||||||
if (shouldSpacerMin || deviceKind === "smartphone") {
|
if (shouldSpacerMin || deviceKind === "smartphone") {
|
||||||
margin = props.marginMin;
|
margin.value = props.marginMin;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ui === "classic") {
|
if (ui === "classic") {
|
||||||
margin = 12;
|
margin.value = 12;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ const adjust = (rect: { width: number; height: number }) => {
|
||||||
rect.width > (props.contentMax ?? 0) ||
|
rect.width > (props.contentMax ?? 0) ||
|
||||||
(rect.width > 360 && window.innerWidth > 400)
|
(rect.width > 360 && window.innerWidth > 400)
|
||||||
) {
|
) {
|
||||||
margin = props.marginMax;
|
margin.value = props.marginMax;
|
||||||
} else {
|
} else {
|
||||||
margin = props.marginMin;
|
margin.value = props.marginMin;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,14 +59,14 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
adjust({
|
adjust({
|
||||||
width: root!.offsetWidth,
|
width: root.value!.offsetWidth,
|
||||||
height: root!.offsetHeight,
|
height: root.value!.offsetHeight,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ro.observe(root!);
|
ro.observe(root.value!);
|
||||||
|
|
||||||
if (props.contentMax) {
|
if (props.contentMax) {
|
||||||
content!.style.maxWidth = `${props.contentMax}px`;
|
content.value!.style.maxWidth = `${props.contentMax}px`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,17 @@ const CURRENT_STICKY_TOP = "CURRENT_STICKY_TOP";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { inject, onMounted, onUnmounted, provide, ref, watch } from "vue";
|
import { inject, onMounted, onUnmounted, provide, ref, watch } from "vue";
|
||||||
|
|
||||||
const headerEl = $ref<HTMLElement>();
|
const headerEl = ref<HTMLElement>();
|
||||||
const bodyEl = $ref<HTMLElement>();
|
const bodyEl = ref<HTMLElement>();
|
||||||
|
|
||||||
let headerHeight = $ref<string | undefined>(),
|
let headerHeight = ref<string | undefined>(),
|
||||||
childStickyTop = $ref(0);
|
childStickyTop = ref(0);
|
||||||
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
|
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
|
||||||
provide(CURRENT_STICKY_TOP, $$(childStickyTop));
|
provide(CURRENT_STICKY_TOP, childStickyTop);
|
||||||
|
|
||||||
const calc = () => {
|
const calc = () => {
|
||||||
childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
|
childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
|
||||||
headerHeight = headerEl.offsetHeight.toString();
|
headerHeight.value = headerEl.value.offsetHeight.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const observer = new ResizeObserver(() => {
|
const observer = new ResizeObserver(() => {
|
||||||
|
@ -44,16 +44,19 @@ onMounted(() => {
|
||||||
watch(parentStickyTop, calc);
|
watch(parentStickyTop, calc);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
$$(childStickyTop),
|
childStickyTop,
|
||||||
() => {
|
() => {
|
||||||
bodyEl.style.setProperty("--stickyTop", `${childStickyTop}px`);
|
bodyEl.value.style.setProperty(
|
||||||
|
"--stickyTop",
|
||||||
|
`${childStickyTop.value}px`,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
observer.observe(headerEl);
|
observer.observe(headerEl.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted } from "vue";
|
import { onMounted, onUnmounted, ref, computed } from "vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { dateTimeFormat } from "@/scripts/intl-const";
|
import { dateTimeFormat } from "@/scripts/intl-const";
|
||||||
|
|
||||||
|
@ -37,12 +37,12 @@ const _time =
|
||||||
const invalid = Number.isNaN(_time);
|
const invalid = Number.isNaN(_time);
|
||||||
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
||||||
|
|
||||||
let now = $ref((props.origin ?? new Date()).getTime());
|
let now = ref((props.origin ?? new Date()).getTime());
|
||||||
const relative = $computed<string>(() => {
|
const relative = computed<string>(() => {
|
||||||
if (props.mode === "absolute") return ""; // absoluteではrelativeを使わないので計算しない
|
if (props.mode === "absolute") return ""; // absoluteではrelativeを使わないので計算しない
|
||||||
if (invalid) return i18n.ts._ago.invalid;
|
if (invalid) return i18n.ts._ago.invalid;
|
||||||
|
|
||||||
const ago = (now - _time) / 1000; /* ms */
|
const ago = (now.value - _time) / 1000; /* ms */
|
||||||
return ago >= 31536000
|
return ago >= 31536000
|
||||||
? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() })
|
? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() })
|
||||||
: ago >= 2592000
|
: ago >= 2592000
|
||||||
|
@ -66,11 +66,11 @@ let tickId: number;
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
const _now = new Date().getTime();
|
const _now = new Date().getTime();
|
||||||
const agoPrev = (now - _time) / 1000; /* ms */ // 現状のinterval
|
const agoPrev = (now.value - _time) / 1000; /* ms */ // 現状のinterval
|
||||||
|
|
||||||
now = _now;
|
now.value = _now;
|
||||||
|
|
||||||
const ago = (now - _time) / 1000; /* ms */ // 次のinterval
|
const ago = (now.value - _time) / 1000; /* ms */ // 次のinterval
|
||||||
const prev = agoPrev < 60 ? 10000 : agoPrev < 3600 ? 60000 : 180000;
|
const prev = agoPrev < 60 ? 10000 : agoPrev < 3600 ? 60000 : 180000;
|
||||||
const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
|
const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, onBeforeUnmount, provide } from "vue";
|
import { inject, onBeforeUnmount, provide, shallowRef, ref } from "vue";
|
||||||
import type { Resolved, Router } from "@/nirax";
|
import type { Resolved, Router } from "@/nirax";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
|
||||||
|
@ -48,18 +48,18 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
const current = resolveNested(router.current)!;
|
const current = resolveNested(router.current)!;
|
||||||
let currentPageComponent = $shallowRef(current.route.component),
|
let currentPageComponent = shallowRef(current.route.component),
|
||||||
currentPageProps = $ref(current.props),
|
currentPageProps = ref(current.props),
|
||||||
key = $ref(
|
key = ref(
|
||||||
current.route.path + JSON.stringify(Object.fromEntries(current.props)),
|
current.route.path + JSON.stringify(Object.fromEntries(current.props)),
|
||||||
);
|
);
|
||||||
|
|
||||||
function onChange({ resolved, key: newKey }) {
|
function onChange({ resolved, key: newKey }) {
|
||||||
const current = resolveNested(resolved);
|
const current = resolveNested(resolved);
|
||||||
if (current == null) return;
|
if (current == null) return;
|
||||||
currentPageComponent = current.route.component;
|
currentPageComponent.value = current.route.component;
|
||||||
currentPageProps = current.props;
|
currentPageProps.value = current.props;
|
||||||
key =
|
key.value =
|
||||||
current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from "vue";
|
import { watch, ref } from "vue";
|
||||||
import MkTextarea from "../form/textarea.vue";
|
import MkTextarea from "../form/textarea.vue";
|
||||||
import { TextBlock } from "@/scripts/hpml/block";
|
import { TextBlock } from "@/scripts/hpml/block";
|
||||||
import { Hpml } from "@/scripts/hpml/evaluator";
|
import { Hpml } from "@/scripts/hpml/evaluator";
|
||||||
|
@ -13,12 +13,12 @@ const props = defineProps<{
|
||||||
hpml: Hpml;
|
hpml: Hpml;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let text = $ref("");
|
let text = ref("");
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
props.hpml.vars,
|
props.hpml.vars,
|
||||||
() => {
|
() => {
|
||||||
text = props.hpml.interpolate(props.block.text) as string;
|
text.value = props.hpml.interpolate(props.block.text) as string;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import * as misskey from "firefish-js";
|
import * as misskey from "firefish-js";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -51,22 +53,22 @@ const props = withDefaults(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded = $ref(false);
|
let loaded = ref(false);
|
||||||
let serverIsDead = $ref(false);
|
let serverIsDead = ref(false);
|
||||||
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
let meta = ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
||||||
|
|
||||||
os.api("meta", {
|
os.api("meta", {
|
||||||
detail: false,
|
detail: false,
|
||||||
}).then(
|
}).then(
|
||||||
(res) => {
|
(res) => {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
serverIsDead = false;
|
serverIsDead.value = false;
|
||||||
meta = res;
|
meta.value = res;
|
||||||
localStorage.setItem("v", res.version);
|
localStorage.setItem("v", res.version);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
serverIsDead = true;
|
serverIsDead.value = true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -74,9 +76,9 @@ function reload() {
|
||||||
unisonReload();
|
unisonReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.error,
|
title: i18n.ts.error,
|
||||||
|
|
|
@ -203,7 +203,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onBeforeUnmount } from "vue";
|
import { nextTick, onBeforeUnmount, ref, computed } from "vue";
|
||||||
import { version } from "@/config";
|
import { version } from "@/config";
|
||||||
import FormLink from "@/components/form/link.vue";
|
import FormLink from "@/components/form/link.vue";
|
||||||
import FormSection from "@/components/form/section.vue";
|
import FormSection from "@/components/form/section.vue";
|
||||||
|
@ -225,15 +225,15 @@ sponsors = patronsResp.sponsors;
|
||||||
patrons = patrons.filter((patron) => !sponsors.includes(patron));
|
patrons = patrons.filter((patron) => !sponsors.includes(patron));
|
||||||
|
|
||||||
let easterEggReady = false;
|
let easterEggReady = false;
|
||||||
let easterEggEmojis = $ref([]);
|
let easterEggEmojis = ref([]);
|
||||||
let easterEggEngine = $ref(null);
|
let easterEggEngine = ref(null);
|
||||||
const containerEl = $ref<HTMLElement>();
|
const containerEl = ref<HTMLElement>();
|
||||||
|
|
||||||
function iconLoaded() {
|
function iconLoaded() {
|
||||||
const emojis = defaultStore.state.reactions;
|
const emojis = defaultStore.state.reactions;
|
||||||
const containerWidth = containerEl?.offsetWidth;
|
const containerWidth = containerEl.value?.offsetWidth;
|
||||||
for (let i = 0; i < 32; i++) {
|
for (let i = 0; i < 32; i++) {
|
||||||
easterEggEmojis.push({
|
easterEggEmojis.value.push({
|
||||||
id: i.toString(),
|
id: i.toString(),
|
||||||
top: -(128 + Math.random() * 256),
|
top: -(128 + Math.random() * 256),
|
||||||
left: Math.random() * containerWidth,
|
left: Math.random() * containerWidth,
|
||||||
|
@ -249,7 +249,7 @@ function iconLoaded() {
|
||||||
function gravity() {
|
function gravity() {
|
||||||
if (!easterEggReady) return;
|
if (!easterEggReady) return;
|
||||||
easterEggReady = false;
|
easterEggReady = false;
|
||||||
easterEggEngine = physics(containerEl);
|
easterEggEngine.value = physics(containerEl.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function iLoveMisskey() {
|
function iLoveMisskey() {
|
||||||
|
@ -260,14 +260,14 @@ function iLoveMisskey() {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (easterEggEngine) {
|
if (easterEggEngine.value) {
|
||||||
easterEggEngine.stop();
|
easterEggEngine.value.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.aboutFirefish,
|
title: i18n.ts.aboutFirefish,
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import MkSelect from "@/components/form/select.vue";
|
import MkSelect from "@/components/form/select.vue";
|
||||||
import MkPagination from "@/components/MkPagination.vue";
|
import MkPagination from "@/components/MkPagination.vue";
|
||||||
|
@ -113,29 +113,29 @@ import MkInstanceCardMini from "@/components/MkInstanceCardMini.vue";
|
||||||
import FormSplit from "@/components/form/split.vue";
|
import FormSplit from "@/components/form/split.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let host = $ref("");
|
let host = ref("");
|
||||||
let state = $ref("federating");
|
let state = ref("federating");
|
||||||
let sort = $ref("+pubSub");
|
let sort = ref("+pubSub");
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: "federation/instances" as const,
|
endpoint: "federation/instances" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offsetMode: true,
|
offsetMode: true,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
sort: sort,
|
sort: sort.value,
|
||||||
host: host !== "" ? host : null,
|
host: host.value !== "" ? host.value : null,
|
||||||
...(state === "federating"
|
...(state.value === "federating"
|
||||||
? { federating: true }
|
? { federating: true }
|
||||||
: state === "subscribing"
|
: state.value === "subscribing"
|
||||||
? { subscribing: true }
|
? { subscribing: true }
|
||||||
: state === "publishing"
|
: state.value === "publishing"
|
||||||
? { publishing: true }
|
? { publishing: true }
|
||||||
: state === "suspended"
|
: state.value === "suspended"
|
||||||
? { suspended: true }
|
? { suspended: true }
|
||||||
: state === "blocked"
|
: state.value === "blocked"
|
||||||
? { blocked: true }
|
? { blocked: true }
|
||||||
: state === "silenced"
|
: state.value === "silenced"
|
||||||
? { silenced: true }
|
? { silenced: true }
|
||||||
: state === "notResponding"
|
: state.value === "notResponding"
|
||||||
? { notResponding: true }
|
? { notResponding: true }
|
||||||
: {}),
|
: {}),
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -205,23 +205,23 @@ withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let stats = $ref(null);
|
let stats = ref(null);
|
||||||
let instanceIcon = $ref<HTMLImageElement>();
|
let instanceIcon = ref<HTMLImageElement>();
|
||||||
let iconClicks = 0;
|
let iconClicks = 0;
|
||||||
let iconSrc = ref(instance.iconUrl || instance.faviconUrl || "/favicon.ico");
|
let iconSrc = ref(instance.iconUrl || instance.faviconUrl || "/favicon.ico");
|
||||||
let instanceIconAnimation = ref("");
|
let instanceIconAnimation = ref("");
|
||||||
let tabs = ["overview", "emojis", "charts"];
|
let tabs = ["overview", "emojis", "charts"];
|
||||||
let tab = $ref(tabs[0]);
|
let tab = ref(tabs[0]);
|
||||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
|
||||||
|
|
||||||
if (iAmModerator) tabs.push("federation");
|
if (iAmModerator) tabs.push("federation");
|
||||||
|
|
||||||
const initStats = () =>
|
const initStats = () =>
|
||||||
os.api("stats", {}).then((res) => {
|
os.api("stats", {}).then((res) => {
|
||||||
stats = res;
|
stats.value = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
let theTabs = [
|
let theTabs = [
|
||||||
{
|
{
|
||||||
|
@ -249,7 +249,7 @@ if (iAmModerator) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let headerTabs = $computed(() => theTabs);
|
let headerTabs = computed(() => theTabs);
|
||||||
|
|
||||||
definePageMetadata(
|
definePageMetadata(
|
||||||
computed(() => ({
|
computed(() => ({
|
||||||
|
@ -261,7 +261,7 @@ definePageMetadata(
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (defaultStore.state.woozyMode === true) {
|
if (defaultStore.state.woozyMode === true) {
|
||||||
iconSrc.value = "/static-assets/woozy.png";
|
iconSrc.value = "/static-assets/woozy.png";
|
||||||
instanceIcon.src = iconSrc.value;
|
instanceIcon.value.src = iconSrc.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ function easterEgg() {
|
||||||
if (iconClicks % 3 === 0) {
|
if (iconClicks % 3 === 0) {
|
||||||
defaultStore.state.woozyMode = !defaultStore.state.woozyMode;
|
defaultStore.state.woozyMode = !defaultStore.state.woozyMode;
|
||||||
defaultStore.set("woozyMode", defaultStore.state.woozyMode);
|
defaultStore.set("woozyMode", defaultStore.state.woozyMode);
|
||||||
if (instanceIcon) {
|
if (instanceIcon.value) {
|
||||||
instanceIconAnimation.value = "spin";
|
instanceIconAnimation.value = "spin";
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (iconClicks % 6 === 0) {
|
if (iconClicks % 6 === 0) {
|
||||||
|
@ -281,7 +281,7 @@ function easterEgg() {
|
||||||
} else {
|
} else {
|
||||||
iconSrc.value = "/static-assets/woozy.png";
|
iconSrc.value = "/static-assets/woozy.png";
|
||||||
}
|
}
|
||||||
instanceIcon.src = iconSrc.value;
|
instanceIcon.value.src = iconSrc.value;
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,11 +299,11 @@ let swiperRef = null;
|
||||||
|
|
||||||
function setSwiperRef(swiper) {
|
function setSwiperRef(swiper) {
|
||||||
swiperRef = swiper;
|
swiperRef = swiper;
|
||||||
syncSlide(tabs.indexOf(tab));
|
syncSlide(tabs.indexOf(tab.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSlideChange() {
|
function onSlideChange() {
|
||||||
tab = tabs[swiperRef.activeIndex];
|
tab.value = tabs[swiperRef.activeIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncSlide(index) {
|
function syncSlide(index) {
|
||||||
|
|
|
@ -156,7 +156,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from "vue";
|
import { computed, watch, ref } from "vue";
|
||||||
import { Virtual } from "swiper/modules";
|
import { Virtual } from "swiper/modules";
|
||||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -181,21 +181,23 @@ import "swiper/scss/virtual";
|
||||||
let tabs = ["overview"];
|
let tabs = ["overview"];
|
||||||
if (iAmModerator) tabs.push("ip");
|
if (iAmModerator) tabs.push("ip");
|
||||||
tabs.push("raw");
|
tabs.push("raw");
|
||||||
let tab = $ref(tabs[0]);
|
let tab = ref(tabs[0]);
|
||||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
|
||||||
|
|
||||||
let file: any = $ref(null);
|
let file: any = ref(null);
|
||||||
let info: any = $ref(null);
|
let info: any = ref(null);
|
||||||
let isSensitive: boolean = $ref(false);
|
let isSensitive: boolean = ref(false);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fileId: string;
|
fileId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
file = await os.api("drive/files/show", { fileId: props.fileId });
|
file.value = await os.api("drive/files/show", { fileId: props.fileId });
|
||||||
info = await os.api("admin/drive/show-file", { fileId: props.fileId });
|
info.value = await os.api("admin/drive/show-file", {
|
||||||
isSensitive = file.isSensitive;
|
fileId: props.fileId,
|
||||||
|
});
|
||||||
|
isSensitive.value = file.value.isSensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch();
|
fetch();
|
||||||
|
@ -203,12 +205,12 @@ fetch();
|
||||||
async function del() {
|
async function del() {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
text: i18n.t("removeAreYouSure", { x: file.name }),
|
text: i18n.t("removeAreYouSure", { x: file.value.name }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
os.apiWithDialog("drive/files/delete", {
|
os.apiWithDialog("drive/files/delete", {
|
||||||
fileId: file.id,
|
fileId: file.value.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,20 +219,20 @@ async function toggleIsSensitive(v) {
|
||||||
fileId: props.fileId,
|
fileId: props.fileId,
|
||||||
isSensitive: v,
|
isSensitive: v,
|
||||||
});
|
});
|
||||||
isSensitive = v;
|
isSensitive.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
window.open(file.url, "_blank");
|
window.open(file.value.url, "_blank");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [
|
const headerTabs = computed(() => [
|
||||||
{
|
{
|
||||||
key: "overview",
|
key: "overview",
|
||||||
title: i18n.ts.overview,
|
title: i18n.ts.overview,
|
||||||
|
@ -252,7 +254,9 @@ const headerTabs = $computed(() => [
|
||||||
|
|
||||||
definePageMetadata(
|
definePageMetadata(
|
||||||
computed(() => ({
|
computed(() => ({
|
||||||
title: file ? i18n.ts.file + ": " + file.name : i18n.ts.file,
|
title: file.value
|
||||||
|
? i18n.ts.file + ": " + file.value.name
|
||||||
|
: i18n.ts.file,
|
||||||
icon: "ph-file ph-bold ph-lg",
|
icon: "ph-file ph-bold ph-lg",
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
@ -261,11 +265,11 @@ let swiperRef = null;
|
||||||
|
|
||||||
function setSwiperRef(swiper) {
|
function setSwiperRef(swiper) {
|
||||||
swiperRef = swiper;
|
swiperRef = swiper;
|
||||||
syncSlide(tabs.indexOf(tab));
|
syncSlide(tabs.indexOf(tab.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSlideChange() {
|
function onSlideChange() {
|
||||||
tab = tabs[swiperRef.activeIndex];
|
tab.value = tabs[swiperRef.activeIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncSlide(index) {
|
function syncSlide(index) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ const metadata = injectPageMetadata();
|
||||||
|
|
||||||
const el = ref<HTMLElement>(null);
|
const el = ref<HTMLElement>(null);
|
||||||
const tabRefs = {};
|
const tabRefs = {};
|
||||||
const tabHighlightEl = $ref<HTMLElement | null>(null);
|
const tabHighlightEl = ref<HTMLElement | null>(null);
|
||||||
const bg = ref(null);
|
const bg = ref(null);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
const hasTabs = computed(() => {
|
const hasTabs = computed(() => {
|
||||||
|
@ -172,14 +172,14 @@ onMounted(() => {
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const tabEl = tabRefs[props.tab];
|
const tabEl = tabRefs[props.tab];
|
||||||
if (tabEl && tabHighlightEl) {
|
if (tabEl && tabHighlightEl.value) {
|
||||||
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||||
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
||||||
const parentRect =
|
const parentRect =
|
||||||
tabEl.parentElement.getBoundingClientRect();
|
tabEl.parentElement.getBoundingClientRect();
|
||||||
const rect = tabEl.getBoundingClientRect();
|
const rect = tabEl.getBoundingClientRect();
|
||||||
tabHighlightEl.style.width = rect.width + "px";
|
tabHighlightEl.value.style.width = rect.width + "px";
|
||||||
tabHighlightEl.style.left =
|
tabHighlightEl.value.style.left =
|
||||||
rect.left - parentRect.left + "px";
|
rect.left - parentRect.left + "px";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import MkSelect from "@/components/form/select.vue";
|
import MkSelect from "@/components/form/select.vue";
|
||||||
|
@ -101,31 +101,31 @@ import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let reports = $ref<InstanceType<typeof MkPagination>>();
|
let reports = ref<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
let state = $ref("unresolved");
|
let state = ref("unresolved");
|
||||||
let reporterOrigin = $ref("combined");
|
let reporterOrigin = ref("combined");
|
||||||
let targetUserOrigin = $ref("combined");
|
let targetUserOrigin = ref("combined");
|
||||||
let searchUsername = $ref("");
|
let searchUsername = ref("");
|
||||||
let searchHost = $ref("");
|
let searchHost = ref("");
|
||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: "admin/abuse-user-reports" as const,
|
endpoint: "admin/abuse-user-reports" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
state,
|
state: state.value,
|
||||||
reporterOrigin,
|
reporterOrigin: reporterOrigin.value,
|
||||||
targetUserOrigin,
|
targetUserOrigin: targetUserOrigin.value,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
function resolved(reportId) {
|
function resolved(reportId) {
|
||||||
reports.removeItem((item) => item.id === reportId);
|
reports.value.removeItem((item) => item.id === reportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.abuseReports,
|
title: i18n.ts.abuseReports,
|
||||||
|
|
|
@ -65,6 +65,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
|
@ -74,14 +76,14 @@ import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let announcements: any[] = $ref([]);
|
let announcements: any[] = ref([]);
|
||||||
|
|
||||||
os.api("admin/announcements/list").then((announcementResponse) => {
|
os.api("admin/announcements/list").then((announcementResponse) => {
|
||||||
announcements = announcementResponse;
|
announcements.value = announcementResponse;
|
||||||
});
|
});
|
||||||
|
|
||||||
function add() {
|
function add() {
|
||||||
announcements.unshift({
|
announcements.value.unshift({
|
||||||
id: null,
|
id: null,
|
||||||
title: "",
|
title: "",
|
||||||
text: "",
|
text: "",
|
||||||
|
@ -97,7 +99,9 @@ function remove(announcement) {
|
||||||
text: i18n.t("removeAreYouSure", { x: announcement.title }),
|
text: i18n.t("removeAreYouSure", { x: announcement.title }),
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
announcements = announcements.filter((x) => x !== announcement);
|
announcements.value = announcements.value.filter(
|
||||||
|
(x) => x !== announcement,
|
||||||
|
);
|
||||||
os.api("admin/announcements/delete", announcement);
|
os.api("admin/announcements/delete", announcement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -134,7 +138,7 @@ function save(announcement) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: "ph-plus ph-bold ph-lg",
|
icon: "ph-plus ph-bold ph-lg",
|
||||||
|
@ -143,7 +147,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.announcements,
|
title: i18n.ts.announcements,
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref } from "vue";
|
||||||
import FormRadios from "@/components/form/radios.vue";
|
import FormRadios from "@/components/form/radios.vue";
|
||||||
import FormInput from "@/components/form/input.vue";
|
import FormInput from "@/components/form/input.vue";
|
||||||
import FormButton from "@/components/MkButton.vue";
|
import FormButton from "@/components/MkButton.vue";
|
||||||
|
@ -88,20 +88,20 @@ const MkCaptcha = defineAsyncComponent(
|
||||||
() => import("@/components/MkCaptcha.vue"),
|
() => import("@/components/MkCaptcha.vue"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let provider = $ref(null);
|
let provider = ref(null);
|
||||||
let hcaptchaSiteKey: string | null = $ref(null);
|
let hcaptchaSiteKey: string | null = ref(null);
|
||||||
let hcaptchaSecretKey: string | null = $ref(null);
|
let hcaptchaSecretKey: string | null = ref(null);
|
||||||
let recaptchaSiteKey: string | null = $ref(null);
|
let recaptchaSiteKey: string | null = ref(null);
|
||||||
let recaptchaSecretKey: string | null = $ref(null);
|
let recaptchaSecretKey: string | null = ref(null);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
hcaptchaSiteKey = meta.hcaptchaSiteKey;
|
hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
|
||||||
hcaptchaSecretKey = meta.hcaptchaSecretKey;
|
hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
|
||||||
recaptchaSiteKey = meta.recaptchaSiteKey;
|
recaptchaSiteKey.value = meta.recaptchaSiteKey;
|
||||||
recaptchaSecretKey = meta.recaptchaSecretKey;
|
recaptchaSecretKey.value = meta.recaptchaSecretKey;
|
||||||
|
|
||||||
provider = meta.enableHcaptcha
|
provider.value = meta.enableHcaptcha
|
||||||
? "hcaptcha"
|
? "hcaptcha"
|
||||||
: meta.enableRecaptcha
|
: meta.enableRecaptcha
|
||||||
? "recaptcha"
|
? "recaptcha"
|
||||||
|
@ -110,12 +110,12 @@ async function init() {
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
enableHcaptcha: provider === "hcaptcha",
|
enableHcaptcha: provider.value === "hcaptcha",
|
||||||
hcaptchaSiteKey,
|
hcaptchaSiteKey: hcaptchaSiteKey.value,
|
||||||
hcaptchaSecretKey,
|
hcaptchaSecretKey: hcaptchaSecretKey.value,
|
||||||
enableRecaptcha: provider === "recaptcha",
|
enableRecaptcha: provider.value === "recaptcha",
|
||||||
recaptchaSiteKey,
|
recaptchaSiteKey: recaptchaSiteKey.value,
|
||||||
recaptchaSecretKey,
|
recaptchaSecretKey: recaptchaSecretKey.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch, computed } from "vue";
|
||||||
import FormTextarea from "@/components/form/textarea.vue";
|
import FormTextarea from "@/components/form/textarea.vue";
|
||||||
import FormInfo from "@/components/MkInfo.vue";
|
import FormInfo from "@/components/MkInfo.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -45,9 +45,9 @@ watch(globalCustomCss, async () => {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.customCss,
|
title: i18n.ts.customCss,
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSuspense from "@/components/form/suspense.vue";
|
import FormSuspense from "@/components/form/suspense.vue";
|
||||||
import FormButton from "@/components/MkButton.vue";
|
import FormButton from "@/components/MkButton.vue";
|
||||||
|
@ -52,9 +54,9 @@ const databasePromiseFactory = () =>
|
||||||
Object.entries(res).sort((a, b) => b[1].size - a[1].size),
|
Object.entries(res).sort((a, b) => b[1].size - a[1].size),
|
||||||
);
|
);
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.database,
|
title: i18n.ts.database,
|
||||||
|
|
|
@ -91,6 +91,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSwitch from "@/components/form/switch.vue";
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
import FormInput from "@/components/form/input.vue";
|
import FormInput from "@/components/form/input.vue";
|
||||||
|
@ -103,23 +105,23 @@ import { fetchInstance, instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let enableEmail: boolean = $ref(false);
|
let enableEmail: boolean = ref(false);
|
||||||
let email: any = $ref(null);
|
let email: any = ref(null);
|
||||||
let smtpSecure: boolean = $ref(false);
|
let smtpSecure: boolean = ref(false);
|
||||||
let smtpHost: string = $ref("");
|
let smtpHost: string = ref("");
|
||||||
let smtpPort: number = $ref(0);
|
let smtpPort: number = ref(0);
|
||||||
let smtpUser: string = $ref("");
|
let smtpUser: string = ref("");
|
||||||
let smtpPass: string = $ref("");
|
let smtpPass: string = ref("");
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
enableEmail = meta.enableEmail;
|
enableEmail.value = meta.enableEmail;
|
||||||
email = meta.email;
|
email.value = meta.email;
|
||||||
smtpSecure = meta.smtpSecure;
|
smtpSecure.value = meta.smtpSecure;
|
||||||
smtpHost = meta.smtpHost;
|
smtpHost.value = meta.smtpHost;
|
||||||
smtpPort = meta.smtpPort;
|
smtpPort.value = meta.smtpPort;
|
||||||
smtpUser = meta.smtpUser;
|
smtpUser.value = meta.smtpUser;
|
||||||
smtpPass = meta.smtpPass;
|
smtpPass.value = meta.smtpPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testEmail() {
|
async function testEmail() {
|
||||||
|
@ -138,19 +140,19 @@ async function testEmail() {
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
enableEmail,
|
enableEmail: enableEmail.value,
|
||||||
email,
|
email: email.value,
|
||||||
smtpSecure,
|
smtpSecure: smtpSecure.value,
|
||||||
smtpHost,
|
smtpHost: smtpHost.value,
|
||||||
smtpPort,
|
smtpPort: smtpPort.value,
|
||||||
smtpUser,
|
smtpUser: smtpUser.value,
|
||||||
smtpPass,
|
smtpPass: smtpPass.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: "ph-test-tube ph-bold ph-lg",
|
icon: "ph-test-tube ph-bold ph-lg",
|
||||||
|
@ -165,7 +167,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.emailServer,
|
title: i18n.ts.emailServer,
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -55,12 +57,12 @@ const props = defineProps<{
|
||||||
emoji: any;
|
emoji: any;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog = $ref(null);
|
let dialog = ref(null);
|
||||||
let name: string = $ref(props.emoji.name);
|
let name: string = ref(props.emoji.name);
|
||||||
let category: string = $ref(props.emoji.category);
|
let category: string = ref(props.emoji.category);
|
||||||
let aliases: string = $ref(props.emoji.aliases.join(" "));
|
let aliases: string = ref(props.emoji.aliases.join(" "));
|
||||||
let categories: string[] = $ref(emojiCategories);
|
let categories: string[] = ref(emojiCategories);
|
||||||
let license: string = $ref(props.emoji.license ?? "");
|
let license: string = ref(props.emoji.license ?? "");
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "done", v: { deleted?: boolean; updated?: any }): void;
|
(ev: "done", v: { deleted?: boolean; updated?: any }): void;
|
||||||
|
@ -74,29 +76,29 @@ function ok() {
|
||||||
async function update() {
|
async function update() {
|
||||||
await os.apiWithDialog("admin/emoji/update", {
|
await os.apiWithDialog("admin/emoji/update", {
|
||||||
id: props.emoji.id,
|
id: props.emoji.id,
|
||||||
name,
|
name: name.value,
|
||||||
category,
|
category: category.value,
|
||||||
aliases: aliases.split(" "),
|
aliases: aliases.value.split(" "),
|
||||||
license: license === "" ? null : license,
|
license: license.value === "" ? null : license.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
emit("done", {
|
emit("done", {
|
||||||
updated: {
|
updated: {
|
||||||
id: props.emoji.id,
|
id: props.emoji.id,
|
||||||
name,
|
name: name.value,
|
||||||
category,
|
category: category.value,
|
||||||
aliases: aliases.split(" "),
|
aliases: aliases.value.split(" "),
|
||||||
license: license === "" ? null : license,
|
license: license.value === "" ? null : license.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function del() {
|
async function del() {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
text: i18n.t("removeAreYouSure", { x: name }),
|
text: i18n.t("removeAreYouSure", { x: name.value }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ async function del() {
|
||||||
emit("done", {
|
emit("done", {
|
||||||
deleted: true,
|
deleted: true,
|
||||||
});
|
});
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -422,7 +422,7 @@ const delBulk = async () => {
|
||||||
emojisPaginationComponent.value.reload();
|
emojisPaginationComponent.value.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: "ph-plus ph-bold ph-lg",
|
icon: "ph-plus ph-bold ph-lg",
|
||||||
|
@ -435,7 +435,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [
|
const headerTabs = computed(() => [
|
||||||
{
|
{
|
||||||
key: "local",
|
key: "local",
|
||||||
icon: "ph-hand-fist ph-bold ph-lg",
|
icon: "ph-hand-fist ph-bold ph-lg",
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
|
import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
|
||||||
import FormSuspense from "@/components/form/suspense.vue";
|
import FormSuspense from "@/components/form/suspense.vue";
|
||||||
|
@ -36,8 +38,8 @@ import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let enablePostImports = $ref(false);
|
let enablePostImports = ref(false);
|
||||||
let meta = $ref<MetaExperiments | null>(null);
|
let meta = ref<MetaExperiments | null>(null);
|
||||||
|
|
||||||
type MetaExperiments = {
|
type MetaExperiments = {
|
||||||
experimentalFeatures?: {
|
experimentalFeatures?: {
|
||||||
|
@ -46,16 +48,17 @@ type MetaExperiments = {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
meta = (await os.api("admin/meta")) as MetaExperiments;
|
meta.value = (await os.api("admin/meta")) as MetaExperiments;
|
||||||
if (!meta) return;
|
if (!meta.value) return;
|
||||||
|
|
||||||
enablePostImports = meta.experimentalFeatures?.postImports ?? false;
|
enablePostImports.value =
|
||||||
|
meta.value.experimentalFeatures?.postImports ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
const experiments: MetaExperiments = {
|
const experiments: MetaExperiments = {
|
||||||
experimentalFeatures: {
|
experimentalFeatures: {
|
||||||
postImports: enablePostImports,
|
postImports: enablePostImports.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
os.apiWithDialog("admin/update-meta", experiments).then(() => {
|
os.apiWithDialog("admin/update-meta", experiments).then(() => {
|
||||||
|
@ -63,9 +66,9 @@ function save() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts._experiments.title,
|
title: i18n.ts._experiments.title,
|
||||||
|
|
|
@ -20,9 +20,9 @@ import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import XFederation from "@/pages/about.federation.vue";
|
import XFederation from "@/pages/about.federation.vue";
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(
|
definePageMetadata(
|
||||||
computed(() => ({
|
computed(() => ({
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent } from "vue";
|
import { computed, defineAsyncComponent, ref } from "vue";
|
||||||
import * as Acct from "firefish-js/built/acct";
|
import * as Acct from "firefish-js/built/acct";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
|
@ -91,19 +91,22 @@ import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let origin = $ref("local");
|
let origin = ref("local");
|
||||||
let type = $ref(null);
|
let type = ref(null);
|
||||||
let searchHost = $ref("");
|
let searchHost = ref("");
|
||||||
let userId = $ref("");
|
let userId = ref("");
|
||||||
let viewMode = $ref("grid");
|
let viewMode = ref("grid");
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: "admin/drive/files" as const,
|
endpoint: "admin/drive/files" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
type: type && type !== "" ? type : null,
|
type: type.value && type.value !== "" ? type.value : null,
|
||||||
userId: userId && userId !== "" ? userId : null,
|
userId: userId.value && userId.value !== "" ? userId.value : null,
|
||||||
origin: origin,
|
origin: origin.value,
|
||||||
hostname: searchHost && searchHost !== "" ? searchHost : null,
|
hostname:
|
||||||
|
searchHost.value && searchHost.value !== ""
|
||||||
|
? searchHost.value
|
||||||
|
: null,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,7 +121,7 @@ function clear() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
text: i18n.ts.lookup,
|
text: i18n.ts.lookup,
|
||||||
icon: "ph-magnifying-glass ph-bold ph-lg",
|
icon: "ph-magnifying-glass ph-bold ph-lg",
|
||||||
|
@ -131,7 +134,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(
|
definePageMetadata(
|
||||||
computed(() => ({
|
computed(() => ({
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormButton from "@/components/MkButton.vue";
|
import FormButton from "@/components/MkButton.vue";
|
||||||
import FormTextarea from "@/components/form/textarea.vue";
|
import FormTextarea from "@/components/form/textarea.vue";
|
||||||
|
@ -35,24 +37,25 @@ import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let hiddenTags: string = $ref("");
|
let hiddenTags: string = ref("");
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
hiddenTags = meta.hiddenTags.join("\n");
|
hiddenTags.value = meta.hiddenTags.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
hiddenTags: hiddenTags.split("\n").map((h: string) => h.trim()) || [],
|
hiddenTags:
|
||||||
|
hiddenTags.value.split("\n").map((h: string) => h.trim()) || [],
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.hiddenTags,
|
title: i18n.ts.hiddenTags,
|
||||||
|
|
|
@ -59,17 +59,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, onActivated, provide, watch, ref, computed } from 'vue';
|
||||||
defineAsyncComponent,
|
|
||||||
inject,
|
|
||||||
nextTick,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
onActivated,
|
|
||||||
provide,
|
|
||||||
watch,
|
|
||||||
ref,
|
|
||||||
} from "vue";
|
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import MkSuperMenu from "@/components/MkSuperMenu.vue";
|
import MkSuperMenu from "@/components/MkSuperMenu.vue";
|
||||||
import MkInfo from "@/components/MkInfo.vue";
|
import MkInfo from "@/components/MkInfo.vue";
|
||||||
|
@ -102,11 +92,11 @@ const indexInfo = {
|
||||||
|
|
||||||
provide("shouldOmitHeaderTitle", false);
|
provide("shouldOmitHeaderTitle", false);
|
||||||
|
|
||||||
let INFO = $ref(indexInfo);
|
let INFO = ref(indexInfo);
|
||||||
let childInfo = $ref(null);
|
let childInfo = ref(null);
|
||||||
let narrow = $ref(false);
|
let narrow = ref(false);
|
||||||
let view = $ref(null);
|
let view = ref(null);
|
||||||
let pageProps = $ref({});
|
let pageProps = ref({});
|
||||||
let noMaintainerInformation =
|
let noMaintainerInformation =
|
||||||
isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
||||||
let noBotProtection =
|
let noBotProtection =
|
||||||
|
@ -114,15 +104,15 @@ let noBotProtection =
|
||||||
!instance.enableHcaptcha &&
|
!instance.enableHcaptcha &&
|
||||||
!instance.enableRecaptcha;
|
!instance.enableRecaptcha;
|
||||||
let noEmailServer = !instance.enableEmail;
|
let noEmailServer = !instance.enableEmail;
|
||||||
let thereIsUnresolvedAbuseReport = $ref(false);
|
let thereIsUnresolvedAbuseReport = ref(false);
|
||||||
let updateAvailable = $ref(false);
|
let updateAvailable = ref(false);
|
||||||
let currentPage = $computed(() => router.currentRef.value.child);
|
let currentPage = computed(() => router.currentRef.value.child);
|
||||||
|
|
||||||
os.api("admin/abuse-user-reports", {
|
os.api("admin/abuse-user-reports", {
|
||||||
state: "unresolved",
|
state: "unresolved",
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}).then((reports) => {
|
}).then((reports) => {
|
||||||
if (reports?.length > 0) thereIsUnresolvedAbuseReport = true;
|
if (reports?.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (defaultStore.state.showAdminUpdates) {
|
if (defaultStore.state.showAdminUpdates) {
|
||||||
|
@ -130,7 +120,7 @@ if (defaultStore.state.showAdminUpdates) {
|
||||||
const cleanRes = parseInt(res?.tag_name.replace(/[^0-9]/g, ""));
|
const cleanRes = parseInt(res?.tag_name.replace(/[^0-9]/g, ""));
|
||||||
const cleanVersion = parseInt(version.replace(/[^0-9]/g, ""));
|
const cleanVersion = parseInt(version.replace(/[^0-9]/g, ""));
|
||||||
if (cleanRes > cleanVersion) {
|
if (cleanRes > cleanVersion) {
|
||||||
updateAvailable = true;
|
updateAvailable.value = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -138,10 +128,10 @@ if (defaultStore.state.showAdminUpdates) {
|
||||||
const NARROW_THRESHOLD = 600;
|
const NARROW_THRESHOLD = 600;
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
if (entries.length === 0) return;
|
if (entries.length === 0) return;
|
||||||
narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
|
narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuDef = $computed(() => [
|
const menuDef = computed(() => [
|
||||||
{
|
{
|
||||||
title: i18n.ts.quickAction,
|
title: i18n.ts.quickAction,
|
||||||
items: [
|
items: [
|
||||||
|
@ -180,55 +170,55 @@ const menuDef = $computed(() => [
|
||||||
icon: "ph-gauge ph-bold ph-lg",
|
icon: "ph-gauge ph-bold ph-lg",
|
||||||
text: i18n.ts.dashboard,
|
text: i18n.ts.dashboard,
|
||||||
to: "/admin/overview",
|
to: "/admin/overview",
|
||||||
active: currentPage?.route.name === "overview",
|
active: currentPage.value?.route.name === "overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-users ph-bold ph-lg",
|
icon: "ph-users ph-bold ph-lg",
|
||||||
text: i18n.ts.users,
|
text: i18n.ts.users,
|
||||||
to: "/admin/users",
|
to: "/admin/users",
|
||||||
active: currentPage?.route.name === "users",
|
active: currentPage.value?.route.name === "users",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-smiley ph-bold ph-lg",
|
icon: "ph-smiley ph-bold ph-lg",
|
||||||
text: i18n.ts.customEmojis,
|
text: i18n.ts.customEmojis,
|
||||||
to: "/admin/emojis",
|
to: "/admin/emojis",
|
||||||
active: currentPage?.route.name === "emojis",
|
active: currentPage.value?.route.name === "emojis",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-planet ph-bold ph-lg",
|
icon: "ph-planet ph-bold ph-lg",
|
||||||
text: i18n.ts.federation,
|
text: i18n.ts.federation,
|
||||||
to: "/admin/federation",
|
to: "/admin/federation",
|
||||||
active: currentPage?.route.name === "federation",
|
active: currentPage.value?.route.name === "federation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-queue ph-bold ph-lg",
|
icon: "ph-queue ph-bold ph-lg",
|
||||||
text: i18n.ts.jobQueue,
|
text: i18n.ts.jobQueue,
|
||||||
to: "/admin/queue",
|
to: "/admin/queue",
|
||||||
active: currentPage?.route.name === "queue",
|
active: currentPage.value?.route.name === "queue",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-cloud ph-bold ph-lg",
|
icon: "ph-cloud ph-bold ph-lg",
|
||||||
text: i18n.ts.files,
|
text: i18n.ts.files,
|
||||||
to: "/admin/files",
|
to: "/admin/files",
|
||||||
active: currentPage?.route.name === "files",
|
active: currentPage.value?.route.name === "files",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-megaphone-simple ph-bold ph-lg",
|
icon: "ph-megaphone-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.announcements,
|
text: i18n.ts.announcements,
|
||||||
to: "/admin/announcements",
|
to: "/admin/announcements",
|
||||||
active: currentPage?.route.name === "announcements",
|
active: currentPage.value?.route.name === "announcements",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-money ph-bold ph-lg",
|
icon: "ph-money ph-bold ph-lg",
|
||||||
text: i18n.ts.ads,
|
text: i18n.ts.ads,
|
||||||
to: "/admin/ads",
|
to: "/admin/ads",
|
||||||
active: currentPage?.route.name === "ads",
|
active: currentPage.value?.route.name === "ads",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-warning-circle ph-bold ph-lg",
|
icon: "ph-warning-circle ph-bold ph-lg",
|
||||||
text: i18n.ts.abuseReports,
|
text: i18n.ts.abuseReports,
|
||||||
to: "/admin/abuses",
|
to: "/admin/abuses",
|
||||||
active: currentPage?.route.name === "abuses",
|
active: currentPage.value?.route.name === "abuses",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -241,70 +231,70 @@ const menuDef = $computed(() => [
|
||||||
icon: "ph-gear-six ph-bold ph-lg",
|
icon: "ph-gear-six ph-bold ph-lg",
|
||||||
text: i18n.ts.general,
|
text: i18n.ts.general,
|
||||||
to: "/admin/settings",
|
to: "/admin/settings",
|
||||||
active: currentPage?.route.name === "settings",
|
active: currentPage.value?.route.name === "settings",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-envelope-simple-open ph-bold ph-lg",
|
icon: "ph-envelope-simple-open ph-bold ph-lg",
|
||||||
text: i18n.ts.emailServer,
|
text: i18n.ts.emailServer,
|
||||||
to: "/admin/email-settings",
|
to: "/admin/email-settings",
|
||||||
active:
|
active:
|
||||||
currentPage?.route.name === "email-settings",
|
currentPage.value?.route.name === "email-settings",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-cloud ph-bold ph-lg",
|
icon: "ph-cloud ph-bold ph-lg",
|
||||||
text: i18n.ts.objectStorage,
|
text: i18n.ts.objectStorage,
|
||||||
to: "/admin/object-storage",
|
to: "/admin/object-storage",
|
||||||
active:
|
active:
|
||||||
currentPage?.route.name === "object-storage",
|
currentPage.value?.route.name === "object-storage",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-lock ph-bold ph-lg",
|
icon: "ph-lock ph-bold ph-lg",
|
||||||
text: i18n.ts.security,
|
text: i18n.ts.security,
|
||||||
to: "/admin/security",
|
to: "/admin/security",
|
||||||
active: currentPage?.route.name === "security",
|
active: currentPage.value?.route.name === "security",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-merge ph-bold ph-lg",
|
icon: "ph-arrows-merge ph-bold ph-lg",
|
||||||
text: i18n.ts.relays,
|
text: i18n.ts.relays,
|
||||||
to: "/admin/relays",
|
to: "/admin/relays",
|
||||||
active: currentPage?.route.name === "relays",
|
active: currentPage.value?.route.name === "relays",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-plug ph-bold ph-lg",
|
icon: "ph-plug ph-bold ph-lg",
|
||||||
text: i18n.ts.integration,
|
text: i18n.ts.integration,
|
||||||
to: "/admin/integrations",
|
to: "/admin/integrations",
|
||||||
active: currentPage?.route.name === "integrations",
|
active: currentPage.value?.route.name === "integrations",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-prohibit ph-bold ph-lg",
|
icon: "ph-prohibit ph-bold ph-lg",
|
||||||
text: i18n.ts.instanceBlocking,
|
text: i18n.ts.instanceBlocking,
|
||||||
to: "/admin/instance-block",
|
to: "/admin/instance-block",
|
||||||
active:
|
active:
|
||||||
currentPage?.route.name === "instance-block",
|
currentPage.value?.route.name === "instance-block",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-hash ph-bold ph-lg",
|
icon: "ph-hash ph-bold ph-lg",
|
||||||
text: i18n.ts.hiddenTags,
|
text: i18n.ts.hiddenTags,
|
||||||
to: "/admin/hashtags",
|
to: "/admin/hashtags",
|
||||||
active: currentPage?.route.name === "hashtags",
|
active: currentPage.value?.route.name === "hashtags",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-ghost ph-bold ph-lg",
|
icon: "ph-ghost ph-bold ph-lg",
|
||||||
text: i18n.ts.proxyAccount,
|
text: i18n.ts.proxyAccount,
|
||||||
to: "/admin/proxy-account",
|
to: "/admin/proxy-account",
|
||||||
active: currentPage?.route.name === "proxy-account",
|
active: currentPage.value?.route.name === "proxy-account",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-database ph-bold ph-lg",
|
icon: "ph-database ph-bold ph-lg",
|
||||||
text: i18n.ts.database,
|
text: i18n.ts.database,
|
||||||
to: "/admin/database",
|
to: "/admin/database",
|
||||||
active: currentPage?.route.name === "database",
|
active: currentPage.value?.route.name === "database",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-flask ph-bold ph-lg",
|
icon: "ph-flask ph-bold ph-lg",
|
||||||
text: i18n.ts._experiments.title,
|
text: i18n.ts._experiments.title,
|
||||||
to: "/admin/experiments",
|
to: "/admin/experiments",
|
||||||
active: currentPage?.route.name === "experiments",
|
active: currentPage.value?.route.name === "experiments",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -312,8 +302,8 @@ const menuDef = $computed(() => [
|
||||||
: []),
|
: []),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
watch(narrow, () => {
|
watch(narrow.value, () => {
|
||||||
if (currentPage?.route.name == null && !narrow) {
|
if (currentPage.value?.route.name == null && !narrow.value) {
|
||||||
router.push("/admin/overview");
|
router.push("/admin/overview");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -321,16 +311,16 @@ watch(narrow, () => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
ro.observe(el.value);
|
ro.observe(el.value);
|
||||||
|
|
||||||
narrow = el.value.offsetWidth < NARROW_THRESHOLD;
|
narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
|
||||||
if (currentPage?.route.name == null && !narrow) {
|
if (currentPage.value?.route.name == null && !narrow.value) {
|
||||||
router.push("/admin/overview");
|
router.push("/admin/overview");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
narrow = el.value.offsetWidth < NARROW_THRESHOLD;
|
narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
|
||||||
|
|
||||||
if (!narrow && currentPage?.route.name == null) {
|
if (!narrow.value && currentPage.value?.route.name == null) {
|
||||||
router.replace("/admin/overview");
|
router.replace("/admin/overview");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -340,16 +330,16 @@ onUnmounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(router.currentRef, (to) => {
|
watch(router.currentRef, (to) => {
|
||||||
if (to.route.path === "/admin" && to.child?.route.name == null && !narrow) {
|
if (to.route.path === "/admin" && to.child?.route.name == null && !narrow.value) {
|
||||||
router.replace("/admin/overview");
|
router.replace("/admin/overview");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
childInfo = null;
|
childInfo.value = null;
|
||||||
} else {
|
} else {
|
||||||
childInfo = info;
|
childInfo.value = info;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -430,11 +420,11 @@ const lookup = (ev) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(INFO);
|
definePageMetadata(INFO.value);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
header: {
|
header: {
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormButton from "@/components/MkButton.vue";
|
import FormButton from "@/components/MkButton.vue";
|
||||||
import FormTextarea from "@/components/form/textarea.vue";
|
import FormTextarea from "@/components/form/textarea.vue";
|
||||||
|
@ -53,30 +55,31 @@ import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let blockedHosts: string = $ref("");
|
let blockedHosts: string = ref("");
|
||||||
let silencedHosts: string = $ref("");
|
let silencedHosts: string = ref("");
|
||||||
let tab = $ref("block");
|
let tab = ref("block");
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
if (meta) {
|
if (meta) {
|
||||||
blockedHosts = meta.blockedHosts.join("\n");
|
blockedHosts.value = meta.blockedHosts.join("\n");
|
||||||
silencedHosts = meta.silencedHosts.join("\n");
|
silencedHosts.value = meta.silencedHosts.join("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
blockedHosts: blockedHosts.split("\n").map((h) => h.trim()) || [],
|
blockedHosts: blockedHosts.value.split("\n").map((h) => h.trim()) || [],
|
||||||
silencedHosts: silencedHosts.split("\n").map((h) => h.trim()) || [],
|
silencedHosts:
|
||||||
|
silencedHosts.value.split("\n").map((h) => h.trim()) || [],
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.instanceBlocking,
|
title: i18n.ts.instanceBlocking,
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSwitch from "@/components/form/switch.vue";
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
import FormInput from "@/components/form/input.vue";
|
import FormInput from "@/components/form/input.vue";
|
||||||
|
@ -44,24 +46,24 @@ import * as os from "@/os";
|
||||||
import { fetchInstance } from "@/instance";
|
import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let uri: string = $ref("");
|
let uri: string = ref("");
|
||||||
let enableDiscordIntegration: boolean = $ref(false);
|
let enableDiscordIntegration: boolean = ref(false);
|
||||||
let discordClientId: string | null = $ref(null);
|
let discordClientId: string | null = ref(null);
|
||||||
let discordClientSecret: string | null = $ref(null);
|
let discordClientSecret: string | null = ref(null);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
uri = meta.uri;
|
uri.value = meta.uri;
|
||||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
enableDiscordIntegration.value = meta.enableDiscordIntegration;
|
||||||
discordClientId = meta.discordClientId;
|
discordClientId.value = meta.discordClientId;
|
||||||
discordClientSecret = meta.discordClientSecret;
|
discordClientSecret.value = meta.discordClientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
enableDiscordIntegration,
|
enableDiscordIntegration: enableDiscordIntegration.value,
|
||||||
discordClientId,
|
discordClientId: discordClientId.value,
|
||||||
discordClientSecret,
|
discordClientSecret: discordClientSecret.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSwitch from "@/components/form/switch.vue";
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
import FormInput from "@/components/form/input.vue";
|
import FormInput from "@/components/form/input.vue";
|
||||||
|
@ -44,24 +46,24 @@ import * as os from "@/os";
|
||||||
import { fetchInstance } from "@/instance";
|
import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let uri: string = $ref("");
|
let uri: string = ref("");
|
||||||
let enableGithubIntegration: boolean = $ref(false);
|
let enableGithubIntegration: boolean = ref(false);
|
||||||
let githubClientId: string | null = $ref(null);
|
let githubClientId: string | null = ref(null);
|
||||||
let githubClientSecret: string | null = $ref(null);
|
let githubClientSecret: string | null = ref(null);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
uri = meta.uri;
|
uri.value = meta.uri;
|
||||||
enableGithubIntegration = meta.enableGithubIntegration;
|
enableGithubIntegration.value = meta.enableGithubIntegration;
|
||||||
githubClientId = meta.githubClientId;
|
githubClientId.value = meta.githubClientId;
|
||||||
githubClientSecret = meta.githubClientSecret;
|
githubClientSecret.value = meta.githubClientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
enableGithubIntegration,
|
enableGithubIntegration: enableGithubIntegration.value,
|
||||||
githubClientId,
|
githubClientId: githubClientId.value,
|
||||||
githubClientSecret,
|
githubClientSecret: githubClientSecret.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import XGithub from "./integrations.github.vue";
|
import XGithub from "./integrations.github.vue";
|
||||||
import XDiscord from "./integrations.discord.vue";
|
import XDiscord from "./integrations.discord.vue";
|
||||||
|
@ -47,20 +49,20 @@ import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let enableTwitterIntegration: boolean = $ref(false);
|
let enableTwitterIntegration: boolean = ref(false);
|
||||||
let enableGithubIntegration: boolean = $ref(false);
|
let enableGithubIntegration: boolean = ref(false);
|
||||||
let enableDiscordIntegration: boolean = $ref(false);
|
let enableDiscordIntegration: boolean = ref(false);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
enableTwitterIntegration.value = meta.enableTwitterIntegration;
|
||||||
enableGithubIntegration = meta.enableGithubIntegration;
|
enableGithubIntegration.value = meta.enableGithubIntegration;
|
||||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
enableDiscordIntegration.value = meta.enableDiscordIntegration;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.integration,
|
title: i18n.ts.integration,
|
||||||
|
|
|
@ -149,6 +149,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSwitch from "@/components/form/switch.vue";
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
import FormInput from "@/components/form/input.vue";
|
import FormInput from "@/components/form/input.vue";
|
||||||
|
@ -159,58 +161,58 @@ import { fetchInstance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let useObjectStorage: boolean = $ref(false);
|
let useObjectStorage: boolean = ref(false);
|
||||||
let objectStorageBaseUrl: string | null = $ref(null);
|
let objectStorageBaseUrl: string | null = ref(null);
|
||||||
let objectStorageBucket: string | null = $ref(null);
|
let objectStorageBucket: string | null = ref(null);
|
||||||
let objectStoragePrefix: string | null = $ref(null);
|
let objectStoragePrefix: string | null = ref(null);
|
||||||
let objectStorageEndpoint: string | null = $ref(null);
|
let objectStorageEndpoint: string | null = ref(null);
|
||||||
let objectStorageRegion: string | null = $ref(null);
|
let objectStorageRegion: string | null = ref(null);
|
||||||
let objectStoragePort: number | null = $ref(null);
|
let objectStoragePort: number | null = ref(null);
|
||||||
let objectStorageAccessKey: string | null = $ref(null);
|
let objectStorageAccessKey: string | null = ref(null);
|
||||||
let objectStorageSecretKey: string | null = $ref(null);
|
let objectStorageSecretKey: string | null = ref(null);
|
||||||
let objectStorageUseSSL: boolean = $ref(false);
|
let objectStorageUseSSL: boolean = ref(false);
|
||||||
let objectStorageUseProxy: boolean = $ref(false);
|
let objectStorageUseProxy: boolean = ref(false);
|
||||||
let objectStorageSetPublicRead: boolean = $ref(false);
|
let objectStorageSetPublicRead: boolean = ref(false);
|
||||||
let objectStorageS3ForcePathStyle: boolean = $ref(true);
|
let objectStorageS3ForcePathStyle: boolean = ref(true);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
useObjectStorage = meta.useObjectStorage;
|
useObjectStorage.value = meta.useObjectStorage;
|
||||||
objectStorageBaseUrl = meta.objectStorageBaseUrl;
|
objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
|
||||||
objectStorageBucket = meta.objectStorageBucket;
|
objectStorageBucket.value = meta.objectStorageBucket;
|
||||||
objectStoragePrefix = meta.objectStoragePrefix;
|
objectStoragePrefix.value = meta.objectStoragePrefix;
|
||||||
objectStorageEndpoint = meta.objectStorageEndpoint;
|
objectStorageEndpoint.value = meta.objectStorageEndpoint;
|
||||||
objectStorageRegion = meta.objectStorageRegion;
|
objectStorageRegion.value = meta.objectStorageRegion;
|
||||||
objectStoragePort = meta.objectStoragePort;
|
objectStoragePort.value = meta.objectStoragePort;
|
||||||
objectStorageAccessKey = meta.objectStorageAccessKey;
|
objectStorageAccessKey.value = meta.objectStorageAccessKey;
|
||||||
objectStorageSecretKey = meta.objectStorageSecretKey;
|
objectStorageSecretKey.value = meta.objectStorageSecretKey;
|
||||||
objectStorageUseSSL = meta.objectStorageUseSSL;
|
objectStorageUseSSL.value = meta.objectStorageUseSSL;
|
||||||
objectStorageUseProxy = meta.objectStorageUseProxy;
|
objectStorageUseProxy.value = meta.objectStorageUseProxy;
|
||||||
objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
|
objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
|
||||||
objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
|
objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
useObjectStorage,
|
useObjectStorage: useObjectStorage.value,
|
||||||
objectStorageBaseUrl,
|
objectStorageBaseUrl: objectStorageBaseUrl.value,
|
||||||
objectStorageBucket,
|
objectStorageBucket: objectStorageBucket.value,
|
||||||
objectStoragePrefix,
|
objectStoragePrefix: objectStoragePrefix.value,
|
||||||
objectStorageEndpoint,
|
objectStorageEndpoint: objectStorageEndpoint.value,
|
||||||
objectStorageRegion,
|
objectStorageRegion: objectStorageRegion.value,
|
||||||
objectStoragePort,
|
objectStoragePort: objectStoragePort.value,
|
||||||
objectStorageAccessKey,
|
objectStorageAccessKey: objectStorageAccessKey.value,
|
||||||
objectStorageSecretKey,
|
objectStorageSecretKey: objectStorageSecretKey.value,
|
||||||
objectStorageUseSSL,
|
objectStorageUseSSL: objectStorageUseSSL.value,
|
||||||
objectStorageUseProxy,
|
objectStorageUseProxy: objectStorageUseProxy.value,
|
||||||
objectStorageSetPublicRead,
|
objectStorageSetPublicRead: objectStorageSetPublicRead.value,
|
||||||
objectStorageS3ForcePathStyle,
|
objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: "ph-check ph-bold ph-lg",
|
icon: "ph-check ph-bold ph-lg",
|
||||||
|
@ -219,7 +221,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.objectStorage,
|
title: i18n.ts.objectStorage,
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import FormSuspense from "@/components/form/suspense.vue";
|
import FormSuspense from "@/components/form/suspense.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -30,7 +32,7 @@ function save() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [
|
const headerActions = computed(() => [
|
||||||
{
|
{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: "ph-check ph-bold ph-lg",
|
icon: "ph-check ph-bold ph-lg",
|
||||||
|
@ -39,7 +41,7 @@ const headerActions = $computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.other,
|
title: i18n.ts.other,
|
||||||
|
|
|
@ -8,13 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, shallowRef, ref } from 'vue';
|
||||||
markRaw,
|
|
||||||
version as vueVersion,
|
|
||||||
onMounted,
|
|
||||||
onBeforeUnmount,
|
|
||||||
nextTick,
|
|
||||||
} from "vue";
|
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import gradient from "chartjs-plugin-gradient";
|
import gradient from "chartjs-plugin-gradient";
|
||||||
|
@ -27,11 +21,11 @@ import { initChart } from "@/scripts/init-chart";
|
||||||
|
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart = null;
|
||||||
const chartLimit = 7;
|
const chartLimit = 7;
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
|
@ -69,7 +63,7 @@ async function renderChart() {
|
||||||
|
|
||||||
const max = Math.max(...raw.read);
|
const max = Math.max(...raw.read);
|
||||||
|
|
||||||
chartInstance = new Chart(chartEl, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
|
@ -166,7 +160,7 @@ async function renderChart() {
|
||||||
plugins: [chartVLine(vLineColor)],
|
plugins: [chartVLine(vLineColor)],
|
||||||
});
|
});
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import gradient from "chartjs-plugin-gradient";
|
import gradient from "chartjs-plugin-gradient";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
@ -33,9 +33,9 @@ import { initChart } from "@/scripts/init-chart";
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const chartLimit = 50;
|
const chartLimit = 50;
|
||||||
const chartEl = $shallowRef<HTMLCanvasElement>();
|
const chartEl = shallowRef<HTMLCanvasElement>();
|
||||||
const chartEl2 = $shallowRef<HTMLCanvasElement>();
|
const chartEl2 = shallowRef<HTMLCanvasElement>();
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
const { handler: externalTooltipHandler2 } = useChartTooltip();
|
const { handler: externalTooltipHandler2 } = useChartTooltip();
|
||||||
|
@ -79,7 +79,7 @@ onMounted(async () => {
|
||||||
const succMax = Math.max(...raw.deliverSucceeded);
|
const succMax = Math.max(...raw.deliverSucceeded);
|
||||||
const failMax = Math.max(...raw.deliverFailed);
|
const failMax = Math.max(...raw.deliverFailed);
|
||||||
|
|
||||||
new Chart(chartEl, {
|
new Chart(chartEl.value, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
|
@ -187,7 +187,7 @@ onMounted(async () => {
|
||||||
plugins: [chartVLine(vLineColor)],
|
plugins: [chartVLine(vLineColor)],
|
||||||
});
|
});
|
||||||
|
|
||||||
new Chart(chartEl2, {
|
new Chart(chartEl2.value, {
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
|
@ -276,7 +276,7 @@ onMounted(async () => {
|
||||||
plugins: [chartVLine(vLineColor)],
|
plugins: [chartVLine(vLineColor)],
|
||||||
});
|
});
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -65,13 +65,13 @@ import MkNumberDiff from "@/components/MkNumberDiff.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { useChartTooltip } from "@/scripts/use-chart-tooltip";
|
import { useChartTooltip } from "@/scripts/use-chart-tooltip";
|
||||||
|
|
||||||
let topSubInstancesForPie: any = $ref(null);
|
let topSubInstancesForPie: any = ref(null);
|
||||||
let topPubInstancesForPie: any = $ref(null);
|
let topPubInstancesForPie: any = ref(null);
|
||||||
let federationPubActive = $ref<number | null>(null);
|
let federationPubActive = ref<number | null>(null);
|
||||||
let federationPubActiveDiff = $ref<number | null>(null);
|
let federationPubActiveDiff = ref<number | null>(null);
|
||||||
let federationSubActive = $ref<number | null>(null);
|
let federationSubActive = ref<number | null>(null);
|
||||||
let federationSubActiveDiff = $ref<number | null>(null);
|
let federationSubActiveDiff = ref<number | null>(null);
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
|
@ -80,13 +80,13 @@ onMounted(async () => {
|
||||||
limit: 2,
|
limit: 2,
|
||||||
span: "day",
|
span: "day",
|
||||||
});
|
});
|
||||||
federationPubActive = chart.pubActive[0];
|
federationPubActive.value = chart.pubActive[0];
|
||||||
federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
|
federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
|
||||||
federationSubActive = chart.subActive[0];
|
federationSubActive.value = chart.subActive[0];
|
||||||
federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
|
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
|
||||||
|
|
||||||
os.apiGet("federation/stats", { limit: 10 }).then((res) => {
|
os.apiGet("federation/stats", { limit: 10 }).then((res) => {
|
||||||
topSubInstancesForPie = res.topSubInstances
|
topSubInstancesForPie.value = res.topSubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
name: x.host,
|
name: x.host,
|
||||||
color: x.themeColor,
|
color: x.themeColor,
|
||||||
|
@ -102,7 +102,7 @@ onMounted(async () => {
|
||||||
value: res.otherFollowersCount,
|
value: res.otherFollowersCount,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
topPubInstancesForPie = res.topPubInstances
|
topPubInstancesForPie.value = res.topPubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
name: x.host,
|
name: x.host,
|
||||||
color: x.themeColor,
|
color: x.themeColor,
|
||||||
|
@ -120,7 +120,7 @@ onMounted(async () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue