refactor(frontend): 非推奨となったReactivity Transformを使わないように (#12539)
* refactor(frontend): 非推奨となったReactivity Transformを使わないように * refactor: 不要な括弧を除去 * fix: 不要なアノテーションを除去 * fix: Refの配列をrefしている部分の対応 * refactor: 不要な括弧を除去 * fix: lint * refactor: Ref、ShallowRef、ComputedRefの変数の宣言をletからconstに置換 * fix: type error * chore: drop reactivity transform from eslint configuration * refactor: remove unnecessary import * fix: 対応漏れ
This commit is contained in:
parent
e42c91dee7
commit
406b4bdbe7
@ -69,12 +69,6 @@ module.exports = {
|
|||||||
'require': false,
|
'require': false,
|
||||||
'__dirname': false,
|
'__dirname': false,
|
||||||
|
|
||||||
// Vue
|
|
||||||
'$$': false,
|
|
||||||
'$ref': false,
|
|
||||||
'$shallowRef': false,
|
|
||||||
'$computed': false,
|
|
||||||
|
|
||||||
// Misskey
|
// Misskey
|
||||||
'_DEV_': false,
|
'_DEV_': false,
|
||||||
'_LANGS_': false,
|
'_LANGS_': false,
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
"@syuilo/aiscript": "0.16.0",
|
"@syuilo/aiscript": "0.16.0",
|
||||||
"@tabler/icons-webfont": "2.37.0",
|
"@tabler/icons-webfont": "2.37.0",
|
||||||
"@vitejs/plugin-vue": "4.5.1",
|
"@vitejs/plugin-vue": "4.5.1",
|
||||||
"@vue-macros/reactivity-transform": "0.4.0",
|
|
||||||
"@vue/compiler-sfc": "3.3.9",
|
"@vue/compiler-sfc": "3.3.9",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"autosize": "6.0.1",
|
"autosize": "6.0.1",
|
||||||
|
@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
@ -56,11 +57,11 @@ const emit = defineEmits<{
|
|||||||
(ev: 'resolved', reportId: string): void;
|
(ev: 'resolved', reportId: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let 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: forward.value,
|
||||||
reportId: props.report.id,
|
reportId: props.report.id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
emit('resolved', props.report.id);
|
emit('resolved', props.report.id);
|
||||||
|
@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref, computed } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
|
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
|
||||||
@ -67,15 +67,15 @@ const props = withDefaults(defineProps<{
|
|||||||
withDescription: true,
|
withDescription: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let achievements = $ref();
|
const achievements = ref();
|
||||||
const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x)));
|
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
|
||||||
|
|
||||||
function fetch() {
|
function fetch() {
|
||||||
os.api('users/achievements', { userId: props.user.id }).then(res => {
|
os.api('users/achievements', { userId: props.user.id }).then(res => {
|
||||||
achievements = [];
|
achievements.value = [];
|
||||||
for (const t of ACHIEVEMENT_TYPES) {
|
for (const t of ACHIEVEMENT_TYPES) {
|
||||||
const a = res.find(x => x.name === t);
|
const a = res.find(x => x.name === t);
|
||||||
if (a) achievements.push(a);
|
if (a) achievements.value.push(a);
|
||||||
}
|
}
|
||||||
//achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
|
//achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
|
||||||
});
|
});
|
||||||
|
@ -138,45 +138,45 @@ const texts = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let enabled = true;
|
let enabled = true;
|
||||||
let majorGraduationColor = $ref<string>();
|
const majorGraduationColor = ref<string>();
|
||||||
//let minorGraduationColor = $ref<string>();
|
//let minorGraduationColor = $ref<string>();
|
||||||
let sHandColor = $ref<string>();
|
const sHandColor = ref<string>();
|
||||||
let mHandColor = $ref<string>();
|
const mHandColor = ref<string>();
|
||||||
let hHandColor = $ref<string>();
|
const hHandColor = ref<string>();
|
||||||
let nowColor = $ref<string>();
|
const nowColor = ref<string>();
|
||||||
let h = $ref<number>(0);
|
const h = ref<number>(0);
|
||||||
let m = $ref<number>(0);
|
const m = ref<number>(0);
|
||||||
let s = $ref<number>(0);
|
const s = ref<number>(0);
|
||||||
let hAngle = $ref<number>(0);
|
const hAngle = ref<number>(0);
|
||||||
let mAngle = $ref<number>(0);
|
const mAngle = ref<number>(0);
|
||||||
let sAngle = $ref<number>(0);
|
const sAngle = ref<number>(0);
|
||||||
let disableSAnimate = $ref(false);
|
const disableSAnimate = ref(false);
|
||||||
let sOneRound = false;
|
let sOneRound = false;
|
||||||
const sLine = ref<SVGPathElement>();
|
const sLine = ref<SVGPathElement>();
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
const now = props.now();
|
const now = props.now();
|
||||||
now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
|
now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
|
||||||
const previousS = s;
|
const previousS = s.value;
|
||||||
const previousM = m;
|
const previousM = m.value;
|
||||||
const previousH = h;
|
const previousH = h.value;
|
||||||
s = now.getSeconds();
|
s.value = now.getSeconds();
|
||||||
m = now.getMinutes();
|
m.value = now.getMinutes();
|
||||||
h = now.getHours();
|
h.value = now.getHours();
|
||||||
if (previousS === s && previousM === m && previousH === h) {
|
if (previousS === s.value && previousM === m.value && previousH === h.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
|
hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||||
mAngle = Math.PI * (m + s / 60) / 30;
|
mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
|
||||||
if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||||
sAngle = Math.PI * 60 / 30;
|
sAngle.value = Math.PI * 60 / 30;
|
||||||
defaultIdlingRenderScheduler.delete(tick);
|
defaultIdlingRenderScheduler.delete(tick);
|
||||||
sLine.value.addEventListener('transitionend', () => {
|
sLine.value.addEventListener('transitionend', () => {
|
||||||
disableSAnimate = true;
|
disableSAnimate.value = true;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
sAngle = 0;
|
sAngle.value = 0;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
disableSAnimate = false;
|
disableSAnimate.value = false;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
defaultIdlingRenderScheduler.add(tick);
|
defaultIdlingRenderScheduler.add(tick);
|
||||||
}
|
}
|
||||||
@ -184,9 +184,9 @@ function tick() {
|
|||||||
});
|
});
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
} else {
|
} else {
|
||||||
sAngle = Math.PI * s / 30;
|
sAngle.value = Math.PI * s.value / 30;
|
||||||
}
|
}
|
||||||
sOneRound = s === 59;
|
sOneRound = s.value === 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
@ -195,12 +195,12 @@ function calcColors() {
|
|||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
|
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
|
||||||
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
|
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
|
||||||
majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||||
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
//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(computedStyle.getPropertyValue('--fg')).toHexString();
|
mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
|
||||||
hHandColor = accent;
|
hHandColor.value = accent;
|
||||||
nowColor = accent;
|
nowColor.value = accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcColors();
|
calcColors();
|
||||||
|
@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Ref } from 'vue';
|
import { Ref, ref } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -87,10 +87,10 @@ function g(id) {
|
|||||||
return props.components.find(x => x.value.id === id).value;
|
return props.components.find(x => x.value.id === id).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
let valueForSwitch = $ref(c.default ?? false);
|
const valueForSwitch = ref(c.default ?? false);
|
||||||
|
|
||||||
function onSwitchUpdate(v) {
|
function onSwitchUpdate(v) {
|
||||||
valueForSwitch = v;
|
valueForSwitch.value = v;
|
||||||
if (c.onChange) c.onChange(v);
|
if (c.onChange) c.onChange(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted } from 'vue';
|
import { nextTick, onMounted, shallowRef } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type?: 'button' | 'submit' | 'reset';
|
type?: 'button' | 'submit' | 'reset';
|
||||||
@ -59,13 +59,13 @@ const emit = defineEmits<{
|
|||||||
(ev: 'click', payload: MouseEvent): void;
|
(ev: 'click', payload: MouseEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let el = $shallowRef<HTMLElement | null>(null);
|
const el = shallowRef<HTMLElement | null>(null);
|
||||||
let ripples = $shallowRef<HTMLElement | null>(null);
|
const ripples = shallowRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.autofocus) {
|
if (props.autofocus) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
el!.focus();
|
el.value!.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -88,11 +88,11 @@ function onMousedown(evt: MouseEvent): void {
|
|||||||
const rect = target.getBoundingClientRect();
|
const rect = target.getBoundingClientRect();
|
||||||
|
|
||||||
const ripple = document.createElement('div');
|
const ripple = document.createElement('div');
|
||||||
ripple.classList.add(ripples!.dataset.childrenClass!);
|
ripple.classList.add(ripples.value!.dataset.childrenClass!);
|
||||||
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;
|
||||||
@ -107,7 +107,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>
|
||||||
|
@ -74,7 +74,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
|
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
|
||||||
|
|
||||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||||
const negate = arr => arr.map(x => -x);
|
const negate = arr => arr.map(x => -x);
|
||||||
@ -268,7 +268,7 @@ const render = () => {
|
|||||||
gradient,
|
gradient,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl)] : [])],
|
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,29 +13,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from 'vue';
|
||||||
import { Chart, LegendItem } from 'chart.js';
|
import { Chart, LegendItem } from 'chart.js';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
let chart = $shallowRef<Chart>();
|
const chart = shallowRef<Chart>();
|
||||||
let items = $shallowRef<LegendItem[]>([]);
|
const items = shallowRef<LegendItem[]>([]);
|
||||||
|
|
||||||
function update(_chart: Chart, _items: LegendItem[]) {
|
function update(_chart: Chart, _items: LegendItem[]) {
|
||||||
chart = _chart,
|
chart.value = _chart,
|
||||||
items = _items;
|
items.value = _items;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(item: LegendItem) {
|
function onClick(item: LegendItem) {
|
||||||
if (chart == null) return;
|
if (chart.value == null) return;
|
||||||
const { type } = chart.config;
|
const { type } = chart.value.config;
|
||||||
if (type === 'pie' || type === 'doughnut') {
|
if (type === 'pie' || type === 'doughnut') {
|
||||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||||
chart.toggleDataVisibility(item.index);
|
chart.value.toggleDataVisibility(item.index);
|
||||||
} else {
|
} else {
|
||||||
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
|
chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex));
|
||||||
}
|
}
|
||||||
chart.update();
|
chart.value.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
|
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useInterval } from '@/scripts/use-interval.js';
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
@ -29,8 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
|
|||||||
|
|
||||||
const saveData = game.saveData;
|
const saveData = game.saveData;
|
||||||
const cookies = computed(() => saveData.value?.cookies);
|
const cookies = computed(() => saveData.value?.cookies);
|
||||||
let cps = $ref(0);
|
const cps = ref(0);
|
||||||
let prevCookies = $ref(0);
|
const prevCookies = ref(0);
|
||||||
|
|
||||||
function onClick(ev: MouseEvent) {
|
function onClick(ev: MouseEvent) {
|
||||||
const x = ev.clientX;
|
const x = ev.clientX;
|
||||||
@ -48,9 +48,9 @@ function onClick(ev: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
const diff = saveData.value!.cookies - prevCookies;
|
const diff = saveData.value!.cookies - prevCookies.value;
|
||||||
cps = diff;
|
cps.value = diff;
|
||||||
prevCookies = saveData.value!.cookies;
|
prevCookies.value = saveData.value!.cookies;
|
||||||
}, 1000, {
|
}, 1000, {
|
||||||
immediate: false,
|
immediate: false,
|
||||||
afterMounted: true,
|
afterMounted: true,
|
||||||
@ -63,7 +63,7 @@ useInterval(game.save, 1000 * 5, {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await game.load();
|
await game.load();
|
||||||
prevCookies = saveData.value!.cookies;
|
prevCookies.value = saveData.value!.cookies;
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onBeforeUnmount } from 'vue';
|
import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
|
||||||
import MkMenu from './MkMenu.vue';
|
import MkMenu from './MkMenu.vue';
|
||||||
import { MenuItem } from './types/menu.vue';
|
import { MenuItem } from './types/menu.vue';
|
||||||
import contains from '@/scripts/contains.js';
|
import contains from '@/scripts/contains.js';
|
||||||
@ -34,9 +34,9 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let rootEl = $shallowRef<HTMLDivElement>();
|
const rootEl = shallowRef<HTMLDivElement>();
|
||||||
|
|
||||||
let zIndex = $ref<number>(os.claimZIndex('high'));
|
const zIndex = ref<number>(os.claimZIndex('high'));
|
||||||
|
|
||||||
const SCROLLBAR_THICKNESS = 16;
|
const SCROLLBAR_THICKNESS = 16;
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ onMounted(() => {
|
|||||||
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let 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 - SCROLLBAR_THICKNESS)) {
|
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
||||||
@ -63,8 +63,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);
|
||||||
});
|
});
|
||||||
@ -74,7 +74,7 @@ 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>
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, shallowRef, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import Cropper from 'cropperjs';
|
import Cropper from 'cropperjs';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
@ -56,10 +56,10 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
|
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
|
||||||
let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
let imgEl = $shallowRef<HTMLImageElement>();
|
const imgEl = shallowRef<HTMLImageElement>();
|
||||||
let cropper: Cropper | null = null;
|
let cropper: Cropper | null = null;
|
||||||
let loading = $ref(true);
|
const 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) => {
|
||||||
@ -94,16 +94,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');
|
||||||
@ -112,7 +112,7 @@ const onImageLoad = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
cropper = new Cropper(imgEl!, {
|
cropper = new Cropper(imgEl.value!, {
|
||||||
});
|
});
|
||||||
|
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
|
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
|
||||||
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
|
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
|
||||||
<template #caption>
|
<template #caption>
|
||||||
<span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
|
<span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
|
||||||
<span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
|
<span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
|
||||||
</template>
|
</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
||||||
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
||||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
||||||
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="actions" :class="$style.buttons">
|
<div v-if="actions" :class="$style.buttons">
|
||||||
@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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 MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -122,24 +122,21 @@ 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'>(null);
|
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
||||||
const okButtonDisabled = $computed<boolean>(() => {
|
|
||||||
if (props.input) {
|
if (props.input) {
|
||||||
if (props.input.minLength) {
|
if (props.input.minLength) {
|
||||||
if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
|
if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
|
||||||
disabledReason = 'charactersBelow';
|
return 'charactersBelow';
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (props.input.maxLength) {
|
if (props.input.maxLength) {
|
||||||
if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
|
if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
|
||||||
disabledReason = 'charactersExceeded';
|
return 'charactersExceeded';
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
function done(canceled: boolean, result?) {
|
function done(canceled: boolean, result?) {
|
||||||
|
@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
|
import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -54,23 +55,23 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>();
|
const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
|
||||||
|
|
||||||
function chosen(emoji: any) {
|
function chosen(emoji: any) {
|
||||||
emit('done', emoji);
|
emit('done', emoji);
|
||||||
if (props.choseAndClose) {
|
if (props.choseAndClose) {
|
||||||
modal?.close();
|
modal.value?.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function opening() {
|
function opening() {
|
||||||
picker?.reset();
|
picker.value?.reset();
|
||||||
picker?.focus();
|
picker.value?.focus();
|
||||||
|
|
||||||
// 何故かちょっと待たないとフォーカスされない
|
// 何故かちょっと待たないとフォーカスされない
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
picker?.focus();
|
picker.value?.focus();
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
@ -42,12 +42,12 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
|
|
||||||
let caption = $ref(props.default);
|
const caption = ref(props.default);
|
||||||
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
emit('done', caption);
|
emit('done', caption.value);
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted } from 'vue';
|
import { nextTick, onMounted, shallowRef, ref } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
@ -70,10 +70,10 @@ const getBgColor = (el: HTMLElement) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let rootEl = $shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
let bgSame = $ref(false);
|
const bgSame = ref(false);
|
||||||
let opened = $ref(props.defaultOpen);
|
const opened = ref(props.defaultOpen);
|
||||||
let openedAtLeastOnce = $ref(props.defaultOpen);
|
const openedAtLeastOnce = ref(props.defaultOpen);
|
||||||
|
|
||||||
function enter(el) {
|
function enter(el) {
|
||||||
const elementHeight = el.getBoundingClientRect().height;
|
const elementHeight = el.getBoundingClientRect().height;
|
||||||
@ -98,20 +98,20 @@ function afterLeave(el) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (!opened) {
|
if (!opened.value) {
|
||||||
openedAtLeastOnce = true;
|
openedAtLeastOnce.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
opened = !opened;
|
opened.value = !opened.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
const parentBg = getBgColor(rootEl.parentElement);
|
const parentBg = getBgColor(rootEl.value.parentElement);
|
||||||
const myBg = computedStyle.getPropertyValue('--panel');
|
const myBg = computedStyle.getPropertyValue('--panel');
|
||||||
bgSame = parentBg === myBg;
|
bgSame.value = parentBg === myBg;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted } from 'vue';
|
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
@ -57,9 +57,9 @@ const emit = defineEmits<{
|
|||||||
(_: 'update:user', value: Misskey.entities.UserDetailed): void
|
(_: 'update:user', value: Misskey.entities.UserDetailed): void
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let isFollowing = $ref(props.user.isFollowing);
|
const isFollowing = ref(props.user.isFollowing);
|
||||||
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
|
const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
|
||||||
let wait = $ref(false);
|
const wait = ref(false);
|
||||||
const connection = useStream().useChannel('main');
|
const connection = useStream().useChannel('main');
|
||||||
|
|
||||||
if (props.user.isFollowing == null) {
|
if (props.user.isFollowing == null) {
|
||||||
@ -71,16 +71,16 @@ 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 (isFollowing) {
|
if (isFollowing.value) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
|
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
|
||||||
@ -92,11 +92,11 @@ 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,
|
||||||
@ -106,7 +106,7 @@ async function onClick() {
|
|||||||
...props.user,
|
...props.user,
|
||||||
withReplies: defaultStore.state.defaultWithReplies
|
withReplies: defaultStore.state.defaultWithReplies
|
||||||
});
|
});
|
||||||
hasPendingFollowRequestFromYou = true;
|
hasPendingFollowRequestFromYou.value = true;
|
||||||
|
|
||||||
claimAchievement('following1');
|
claimAchievement('following1');
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ async function onClick() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
wait = false;
|
wait.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -53,19 +53,19 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog: InstanceType<typeof MkModalWindow> = $ref();
|
const dialog = ref<InstanceType<typeof MkModalWindow>>();
|
||||||
|
|
||||||
let username = $ref('');
|
const username = ref('');
|
||||||
let email = $ref('');
|
const email = ref('');
|
||||||
let processing = $ref(false);
|
const 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>
|
||||||
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -27,11 +27,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);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||||
position: 'middle',
|
position: 'middle',
|
||||||
@ -42,8 +42,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;
|
||||||
@ -88,7 +88,7 @@ async function renderChart() {
|
|||||||
values = raw.deliverFailed;
|
values = raw.deliverFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
@ -101,7 +101,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: [{
|
||||||
@ -210,7 +210,7 @@ async function renderChart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.src, () => {
|
watch(() => props.src, () => {
|
||||||
fetching = true;
|
fetching.value = true;
|
||||||
renderChart();
|
renderChart();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { $ref } from 'vue/macros';
|
|
||||||
import DrawBlurhash from '@/workers/draw-blurhash?worker';
|
import DrawBlurhash from '@/workers/draw-blurhash?worker';
|
||||||
import TestWebGL2 from '@/workers/test-webgl2?worker';
|
import TestWebGL2 from '@/workers/test-webgl2?worker';
|
||||||
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
|
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
|
||||||
@ -58,7 +57,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
|
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch, ref } from 'vue';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { render } from 'buraha';
|
import { render } from 'buraha';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -98,41 +97,41 @@ const viewId = uuid();
|
|||||||
const canvas = shallowRef<HTMLCanvasElement>();
|
const canvas = shallowRef<HTMLCanvasElement>();
|
||||||
const root = shallowRef<HTMLDivElement>();
|
const root = shallowRef<HTMLDivElement>();
|
||||||
const img = shallowRef<HTMLImageElement>();
|
const img = shallowRef<HTMLImageElement>();
|
||||||
let loaded = $ref(false);
|
const loaded = ref(false);
|
||||||
let canvasWidth = $ref(64);
|
const canvasWidth = ref(64);
|
||||||
let canvasHeight = $ref(64);
|
const canvasHeight = ref(64);
|
||||||
let imgWidth = $ref(props.width);
|
const imgWidth = ref(props.width);
|
||||||
let imgHeight = $ref(props.height);
|
const imgHeight = ref(props.height);
|
||||||
let bitmapTmp = $ref<CanvasImageSource | undefined>();
|
const bitmapTmp = ref<CanvasImageSource | undefined>();
|
||||||
const hide = computed(() => !loaded || props.forceBlurhash);
|
const hide = computed(() => !loaded.value || props.forceBlurhash);
|
||||||
|
|
||||||
function waitForDecode() {
|
function waitForDecode() {
|
||||||
if (props.src != null && props.src !== '') {
|
if (props.src != null && props.src !== '') {
|
||||||
nextTick()
|
nextTick()
|
||||||
.then(() => img.value?.decode())
|
.then(() => img.value?.decode())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
}, error => {
|
}, error => {
|
||||||
console.log('Error occurred during decoding image', img.value, error);
|
console.log('Error occurred during decoding image', img.value, error);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
loaded = false;
|
loaded.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch([() => props.width, () => props.height, root], () => {
|
watch([() => props.width, () => props.height, root], () => {
|
||||||
const ratio = props.width / props.height;
|
const ratio = props.width / props.height;
|
||||||
if (ratio > 1) {
|
if (ratio > 1) {
|
||||||
canvasWidth = Math.round(64 * ratio);
|
canvasWidth.value = Math.round(64 * ratio);
|
||||||
canvasHeight = 64;
|
canvasHeight.value = 64;
|
||||||
} else {
|
} else {
|
||||||
canvasWidth = 64;
|
canvasWidth.value = 64;
|
||||||
canvasHeight = Math.round(64 / ratio);
|
canvasHeight.value = Math.round(64 / ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientWidth = root.value?.clientWidth ?? 300;
|
const clientWidth = root.value?.clientWidth ?? 300;
|
||||||
imgWidth = clientWidth;
|
imgWidth.value = clientWidth;
|
||||||
imgHeight = Math.round(clientWidth / ratio);
|
imgHeight.value = Math.round(clientWidth / ratio);
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
@ -140,15 +139,15 @@ watch([() => props.width, () => props.height, root], () => {
|
|||||||
function drawImage(bitmap: CanvasImageSource) {
|
function drawImage(bitmap: CanvasImageSource) {
|
||||||
// canvasがない(mountedされていない)場合はTmpに保存しておく
|
// canvasがない(mountedされていない)場合はTmpに保存しておく
|
||||||
if (!canvas.value) {
|
if (!canvas.value) {
|
||||||
bitmapTmp = bitmap;
|
bitmapTmp.value = bitmap;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// canvasがあれば描画する
|
// canvasがあれば描画する
|
||||||
bitmapTmp = undefined;
|
bitmapTmp.value = undefined;
|
||||||
const ctx = canvas.value.getContext('2d');
|
const ctx = canvas.value.getContext('2d');
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
ctx.drawImage(bitmap, 0, 0, canvasWidth.value, canvasHeight.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawAvg() {
|
function drawAvg() {
|
||||||
@ -160,7 +159,7 @@ function drawAvg() {
|
|||||||
// avgColorでお茶をにごす
|
// avgColorでお茶をにごす
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
|
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
|
||||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function draw() {
|
async function draw() {
|
||||||
@ -212,8 +211,8 @@ watch(() => props.hash, () => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// drawImageがmountedより先に呼ばれている場合はここで描画する
|
// drawImageがmountedより先に呼ばれている場合はここで描画する
|
||||||
if (bitmapTmp) {
|
if (bitmapTmp.value) {
|
||||||
drawImage(bitmapTmp);
|
drawImage(bitmapTmp.value);
|
||||||
}
|
}
|
||||||
waitForDecode();
|
waitForDecode();
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
@ -24,12 +25,12 @@ const props = defineProps<{
|
|||||||
instance: Misskey.entities.FederationInstance;
|
instance: Misskey.entities.FederationInstance;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let chartValues = $ref<number[] | null>(null);
|
const chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
|
os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).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 {
|
||||||
|
@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkChart from '@/components/MkChart.vue';
|
import MkChart from '@/components/MkChart.vue';
|
||||||
@ -100,11 +100,11 @@ import { initChart } from '@/scripts/init-chart.js';
|
|||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const chartLimit = 500;
|
const chartLimit = 500;
|
||||||
let chartSpan = $ref<'hour' | 'day'>('hour');
|
const chartSpan = ref<'hour' | 'day'>('hour');
|
||||||
let chartSrc = $ref('active-users');
|
const chartSrc = ref('active-users');
|
||||||
let heatmapSrc = $ref('active-users');
|
const heatmapSrc = ref('active-users');
|
||||||
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
|
|
||||||
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
||||||
position: 'middle',
|
position: 'middle',
|
||||||
@ -163,7 +163,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(subDoughnutEl, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
|
createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
|
||||||
name: x.host,
|
name: x.host,
|
||||||
color: x.themeColor,
|
color: x.themeColor,
|
||||||
value: x.followersCount,
|
value: x.followersCount,
|
||||||
@ -172,7 +172,7 @@ onMounted(() => {
|
|||||||
},
|
},
|
||||||
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
|
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
|
||||||
|
|
||||||
createDoughnut(pubDoughnutEl, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
|
createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
|
||||||
name: x.host,
|
name: x.host,
|
||||||
color: x.themeColor,
|
color: x.themeColor,
|
||||||
value: x.followingCount,
|
value: x.followingCount,
|
||||||
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { instanceName } from '@/config.js';
|
import { instanceName } from '@/config.js';
|
||||||
import { instance as Instance } from '@/instance.js';
|
import { instance as Instance } from '@/instance.js';
|
||||||
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
|
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
|
||||||
@ -30,7 +30,7 @@ const instance = props.instance ?? {
|
|||||||
themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
|
themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
|
||||||
};
|
};
|
||||||
|
|
||||||
const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
|
const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
|
||||||
|
|
||||||
const themeColor = instance.themeColor ?? '#777777';
|
const themeColor = instance.themeColor ?? '#777777';
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import { navbarItemDef } from '@/navbar.js';
|
import { navbarItemDef } from '@/navbar.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
|
|||||||
deviceKind === 'smartphone' ? 'drawer' :
|
deviceKind === 'smartphone' ? 'drawer' :
|
||||||
'dialog';
|
'dialog';
|
||||||
|
|
||||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const menu = defaultStore.state.menu;
|
const menu = defaultStore.state.menu;
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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.js';
|
import { url as local } from '@/config.js';
|
||||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
@ -29,13 +29,13 @@ 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(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
||||||
showing,
|
showing,
|
||||||
url: props.url,
|
url: props.url,
|
||||||
source: el,
|
source: el.value,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, shallowRef, watch } from 'vue';
|
import { onMounted, shallowRef, watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ const props = withDefaults(defineProps<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
const audioEl = shallowRef<HTMLAudioElement>();
|
const audioEl = shallowRef<HTMLAudioElement>();
|
||||||
let hide = $ref(true);
|
const hide = ref(true);
|
||||||
|
|
||||||
watch(audioEl, () => {
|
watch(audioEl, () => {
|
||||||
if (audioEl.value) {
|
if (audioEl.value) {
|
||||||
|
@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
@ -73,10 +73,10 @@ const props = withDefaults(defineProps<{
|
|||||||
controls: true,
|
controls: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let hide = $ref(true);
|
const hide = ref(true);
|
||||||
let darkMode: boolean = $ref(defaultStore.state.darkMode);
|
const darkMode = ref<boolean>(defaultStore.state.darkMode);
|
||||||
|
|
||||||
const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
|
const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
|
||||||
? props.image.url
|
? props.image.url
|
||||||
: defaultStore.state.disableShowingAnimatedImages
|
: defaultStore.state.disableShowingAnimatedImages
|
||||||
? getStaticImageUrl(props.image.url)
|
? getStaticImageUrl(props.image.url)
|
||||||
@ -87,14 +87,14 @@ function onclick() {
|
|||||||
if (!props.controls) {
|
if (!props.controls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (hide) {
|
if (hide.value) {
|
||||||
hide = false;
|
hide.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
||||||
watch(() => props.image, () => {
|
watch(() => props.image, () => {
|
||||||
hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
||||||
}, {
|
}, {
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@ -105,7 +105,7 @@ function showMenu(ev: MouseEvent) {
|
|||||||
text: i18n.ts.hide,
|
text: i18n.ts.hide,
|
||||||
icon: 'ti ti-eye-off',
|
icon: 'ti ti-eye-off',
|
||||||
action: () => {
|
action: () => {
|
||||||
hide = true;
|
hide.value = true;
|
||||||
},
|
},
|
||||||
}, ...(iAmModerator ? [{
|
}, ...(iAmModerator ? [{
|
||||||
text: i18n.ts.markAsSensitive,
|
text: i18n.ts.markAsSensitive,
|
||||||
@ -126,7 +126,7 @@ function showMenu(ev: MouseEvent) {
|
|||||||
|
|
||||||
.sensitive {
|
.sensitive {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -63,7 +63,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||||
import PhotoSwipe from 'photoswipe';
|
import PhotoSwipe from 'photoswipe';
|
||||||
@ -86,7 +86,7 @@ const container = shallowRef<HTMLElement | null | undefined>(undefined);
|
|||||||
const gallery = shallowRef<HTMLDivElement>();
|
const gallery = shallowRef<HTMLDivElement>();
|
||||||
const pswpZIndex = os.claimZIndex('middle');
|
const pswpZIndex = os.claimZIndex('middle');
|
||||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||||
const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
|
const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
|
||||||
let lightbox: PhotoSwipeLightbox | null;
|
let lightbox: PhotoSwipeLightbox | null;
|
||||||
|
|
||||||
const popstateHandler = (): void => {
|
const popstateHandler = (): void => {
|
||||||
|
@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||||
import MkSwitchButton from '@/components/MkSwitch.button.vue';
|
import MkSwitchButton from '@/components/MkSwitch.button.vue';
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
|
||||||
@ -90,19 +90,19 @@ const emit = defineEmits<{
|
|||||||
(ev: 'hide'): void;
|
(ev: 'hide'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let itemsEl = $shallowRef<HTMLDivElement>();
|
const itemsEl = shallowRef<HTMLDivElement>();
|
||||||
|
|
||||||
let items2: InnerMenuItem[] = $ref([]);
|
const items2 = ref<InnerMenuItem[]>([]);
|
||||||
|
|
||||||
let child = $shallowRef<InstanceType<typeof XChild>>();
|
const child = shallowRef<InstanceType<typeof XChild>>();
|
||||||
|
|
||||||
let keymap = $computed(() => ({
|
const keymap = computed(() => ({
|
||||||
'up|k|shift+tab': focusUp,
|
'up|k|shift+tab': focusUp,
|
||||||
'down|j|tab': focusDown,
|
'down|j|tab': focusDown,
|
||||||
'esc': close,
|
'esc': close,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let childShowingItem = $ref<MenuItem | null>();
|
const childShowingItem = ref<MenuItem | null>();
|
||||||
|
|
||||||
let preferClick = isTouchUsing || props.asDrawer;
|
let preferClick = isTouchUsing || props.asDrawer;
|
||||||
|
|
||||||
@ -115,22 +115,22 @@ watch(() => props.items, () => {
|
|||||||
if (item && 'then' in item) { // if item is Promise
|
if (item && 'then' in item) { // 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,
|
||||||
});
|
});
|
||||||
|
|
||||||
const childMenu = ref<MenuItem[] | null>();
|
const childMenu = ref<MenuItem[] | null>();
|
||||||
let childTarget = $shallowRef<HTMLElement | null>();
|
const childTarget = shallowRef<HTMLElement | null>();
|
||||||
|
|
||||||
function closeChild() {
|
function closeChild() {
|
||||||
childMenu.value = null;
|
childMenu.value = null;
|
||||||
childShowingItem = null;
|
childShowingItem.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function childActioned() {
|
function childActioned() {
|
||||||
@ -139,8 +139,8 @@ function childActioned() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onGlobalMousedown = (event: MouseEvent) => {
|
const onGlobalMousedown = (event: MouseEvent) => {
|
||||||
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return;
|
if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
|
||||||
if (child && child.checkHit(event)) return;
|
if (child.value && child.value.checkHit(event)) return;
|
||||||
closeChild();
|
closeChild();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,10 +177,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
|
|||||||
});
|
});
|
||||||
emit('hide');
|
emit('hide');
|
||||||
} else {
|
} else {
|
||||||
childTarget = ev.currentTarget ?? ev.target;
|
childTarget.value = ev.currentTarget ?? ev.target;
|
||||||
// これでもリアクティビティは保たれる
|
// これでもリアクティビティは保たれる
|
||||||
childMenu.value = children;
|
childMenu.value = children;
|
||||||
childShowingItem = item;
|
childShowingItem.value = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ function switchItem(item: MenuSwitch & { ref: any }) {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.viaKeyboard) {
|
if (props.viaKeyboard) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (itemsEl) focusNext(itemsEl.children[0], true, false);
|
if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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.js';
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
@ -43,11 +43,11 @@ const props = defineProps<{
|
|||||||
const viewBoxX = 50;
|
const viewBoxX = 50;
|
||||||
const viewBoxY = 50;
|
const viewBoxY = 50;
|
||||||
const gradientId = uuid();
|
const gradientId = uuid();
|
||||||
let polylinePoints = $ref('');
|
const polylinePoints = ref('');
|
||||||
let polygonPoints = $ref('');
|
const polygonPoints = ref('');
|
||||||
let headX = $ref<number | null>(null);
|
const headX = ref<number | null>(null);
|
||||||
let headY = $ref<number | null>(null);
|
const headY = ref<number | null>(null);
|
||||||
let clock = $ref<number | null>(null);
|
const clock = ref<number | null>(null);
|
||||||
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
|
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
|
||||||
const color = accent.toRgbString();
|
const color = accent.toRgbString();
|
||||||
|
|
||||||
@ -60,12 +60,12 @@ 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.at(-1)![0];
|
headX.value = _polylinePoints.at(-1)![0];
|
||||||
headY = _polylinePoints.at(-1)![1];
|
headY.value = _polylinePoints.at(-1)![1];
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.src, draw, { immediate: true });
|
watch(() => props.src, draw, { immediate: true });
|
||||||
|
@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue';
|
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, shallowRef, computed } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { isTouchUsing } from '@/scripts/touch.js';
|
import { isTouchUsing } from '@/scripts/touch.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -89,14 +89,14 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
provide('modal', true);
|
provide('modal', true);
|
||||||
|
|
||||||
let maxHeight = $ref<number>();
|
const maxHeight = ref<number>();
|
||||||
let fixed = $ref(false);
|
const fixed = ref(false);
|
||||||
let transformOrigin = $ref('center');
|
const transformOrigin = ref('center');
|
||||||
let showing = $ref(true);
|
const showing = ref(true);
|
||||||
let content = $shallowRef<HTMLElement>();
|
const content = shallowRef<HTMLElement>();
|
||||||
const zIndex = os.claimZIndex(props.zPriority);
|
const zIndex = os.claimZIndex(props.zPriority);
|
||||||
let useSendAnime = $ref(false);
|
const useSendAnime = ref(false);
|
||||||
const type = $computed<ModalTypes>(() => {
|
const type = computed<ModalTypes>(() => {
|
||||||
if (props.preferType === 'auto') {
|
if (props.preferType === 'auto') {
|
||||||
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
||||||
return 'drawer';
|
return 'drawer';
|
||||||
@ -107,26 +107,26 @@ const type = $computed<ModalTypes>(() => {
|
|||||||
return props.preferType!;
|
return props.preferType!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup'));
|
const isEnableBgTransparent = computed(() => props.transparentBg && (type.value === 'popup'));
|
||||||
let transitionName = $computed((() =>
|
const 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((() =>
|
const 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
|
||||||
));
|
));
|
||||||
@ -135,12 +135,12 @@ let contentClicking = false;
|
|||||||
|
|
||||||
function close(opts: { useSendAnimation?: boolean } = {}) {
|
function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||||
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,8 +149,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 = {
|
||||||
@ -162,21 +162,21 @@ const SCROLLBAR_THICKNESS = 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);
|
||||||
@ -194,7 +194,7 @@ const align = () => {
|
|||||||
top = y + props.src.offsetHeight;
|
top = y + props.src.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixed) {
|
if (fixed.value) {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
|
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
|
||||||
@ -207,16 +207,16 @@ const align = () => {
|
|||||||
if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - 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 - SCROLLBAR_THICKNESS) - MARGIN) - height;
|
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
@ -231,16 +231,16 @@ const align = () => {
|
|||||||
if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - 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.pageYOffset + ((upperSpace + MARGIN) - height);
|
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
|
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,29 +255,29 @@ const align = () => {
|
|||||||
let transformOriginX = 'center';
|
let transformOriginX = 'center';
|
||||||
let transformOriginY = 'center';
|
let transformOriginY = 'center';
|
||||||
|
|
||||||
if (top >= srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)) {
|
if (top >= 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 (left >= srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)) {
|
if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
|
||||||
transformOriginX = 'left';
|
transformOriginX = 'left';
|
||||||
} else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) {
|
} else if ((left + width) <= 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('mousedown', ev => {
|
el.addEventListener('mousedown', ev => {
|
||||||
contentClicking = true;
|
contentClicking = true;
|
||||||
window.addEventListener('mouseup', ev => {
|
window.addEventListener('mouseup', ev => {
|
||||||
@ -299,7 +299,7 @@ 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();
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ onMounted(() => {
|
|||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
alignObserver.observe(content!);
|
alignObserver.observe(content.value!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
|
||||||
import MkModal from './MkModal.vue';
|
import MkModal from './MkModal.vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
@ -44,14 +44,14 @@ const emit = defineEmits<{
|
|||||||
(event: 'ok'): void;
|
(event: 'ok'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let rootEl = $shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
let headerEl = $shallowRef<HTMLElement>();
|
const headerEl = shallowRef<HTMLElement>();
|
||||||
let bodyWidth = $ref(0);
|
const bodyWidth = ref(0);
|
||||||
let bodyHeight = $ref(0);
|
const bodyHeight = ref(0);
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
modal.close();
|
modal.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBgClick = () => {
|
const onBgClick = () => {
|
||||||
@ -67,14 +67,14 @@ const onKeydown = (evt) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
bodyWidth = rootEl.offsetWidth;
|
bodyWidth.value = rootEl.value.offsetWidth;
|
||||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
bodyWidth = rootEl.offsetWidth;
|
bodyWidth.value = rootEl.value.offsetWidth;
|
||||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||||
ro.observe(rootEl);
|
ro.observe(rootEl.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -205,12 +205,12 @@ const emit = defineEmits<{
|
|||||||
const inChannel = inject('inChannel', null);
|
const inChannel = inject('inChannel', null);
|
||||||
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
||||||
|
|
||||||
let note = $ref(deepClone(props.note));
|
const note = ref(deepClone(props.note));
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result: Misskey.entities.Note | null = deepClone(note);
|
let result: Misskey.entities.Note | null = deepClone(note.value);
|
||||||
for (const interruptor of noteViewInterruptors) {
|
for (const interruptor of noteViewInterruptors) {
|
||||||
try {
|
try {
|
||||||
result = await interruptor.handler(result);
|
result = await interruptor.handler(result);
|
||||||
@ -222,15 +222,15 @@ if (noteViewInterruptors.length > 0) {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 = shallowRef<HTMLElement>();
|
const el = shallowRef<HTMLElement>();
|
||||||
@ -239,21 +239,21 @@ const renoteButton = shallowRef<HTMLElement>();
|
|||||||
const renoteTime = shallowRef<HTMLElement>();
|
const renoteTime = shallowRef<HTMLElement>();
|
||||||
const reactButton = shallowRef<HTMLElement>();
|
const reactButton = shallowRef<HTMLElement>();
|
||||||
const clipButton = shallowRef<HTMLElement>();
|
const clipButton = shallowRef<HTMLElement>();
|
||||||
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
|
const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
|
||||||
const isMyRenote = $i && ($i.id === note.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
|
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
||||||
const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null);
|
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null);
|
||||||
const isLong = shouldCollapsed(appearNote, urls ?? []);
|
const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
||||||
const collapsed = ref(appearNote.cw == null && isLong);
|
const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(checkMute(appearNote, $i?.mutedWords));
|
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
||||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords));
|
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
|
||||||
const translation = ref<any>(null);
|
const translation = ref<any>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
|
||||||
let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
|
const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null)));
|
||||||
|
|
||||||
function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
|
function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
|
||||||
if (mutedWords == null) return false;
|
if (mutedWords == null) return false;
|
||||||
@ -277,20 +277,20 @@ const keymap = {
|
|||||||
|
|
||||||
provide('react', (reaction: string) => {
|
provide('react', (reaction: string) => {
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
watch(() => props.note, (to) => {
|
watch(() => props.note, (to) => {
|
||||||
note = deepClone(to);
|
note.value = deepClone(to);
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
} else {
|
} else {
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
pureNote: $$(note),
|
pureNote: note,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -298,7 +298,7 @@ if (props.mock) {
|
|||||||
if (!props.mock) {
|
if (!props.mock) {
|
||||||
useTooltip(renoteButton, async (showing) => {
|
useTooltip(renoteButton, async (showing) => {
|
||||||
const renotes = await os.api('notes/renotes', {
|
const renotes = await os.api('notes/renotes', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 11,
|
limit: 11,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ if (!props.mock) {
|
|||||||
os.popup(MkUsersTooltip, {
|
os.popup(MkUsersTooltip, {
|
||||||
showing,
|
showing,
|
||||||
users,
|
users,
|
||||||
count: appearNote.renoteCount,
|
count: appearNote.value.renoteCount,
|
||||||
targetElement: renoteButton.value,
|
targetElement: renoteButton.value,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
});
|
});
|
||||||
@ -319,7 +319,7 @@ function renote(viaKeyboard = false) {
|
|||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
|
const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock });
|
||||||
os.popupMenu(menu, renoteButton.value, {
|
os.popupMenu(menu, renoteButton.value, {
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
});
|
});
|
||||||
@ -331,8 +331,8 @@ function reply(viaKeyboard = false): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote,
|
reply: appearNote.value,
|
||||||
channel: appearNote.channel,
|
channel: appearNote.value.channel,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}, () => {
|
}, () => {
|
||||||
focus();
|
focus();
|
||||||
@ -342,7 +342,7 @@ function reply(viaKeyboard = false): void {
|
|||||||
function react(viaKeyboard = false): void {
|
function react(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
if (appearNote.reactionAcceptance === 'likeOnly') {
|
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||||
sound.play('reaction');
|
sound.play('reaction');
|
||||||
|
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
@ -350,7 +350,7 @@ function react(viaKeyboard = false): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: '❤️',
|
reaction: '❤️',
|
||||||
});
|
});
|
||||||
const el = reactButton.value as HTMLElement | null | undefined;
|
const el = reactButton.value as HTMLElement | null | undefined;
|
||||||
@ -371,10 +371,10 @@ function react(viaKeyboard = false): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
|
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
||||||
claimAchievement('reactWithoutRead');
|
claimAchievement('reactWithoutRead');
|
||||||
}
|
}
|
||||||
}, () => {
|
}, () => {
|
||||||
@ -417,7 +417,7 @@ function onContextmenu(ev: MouseEvent): void {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
|
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,7 +427,7 @@ function menu(viaKeyboard = false): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
|
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
|
||||||
os.popupMenu(menu, menuButton.value, {
|
os.popupMenu(menu, menuButton.value, {
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
}).then(focus).finally(cleanup);
|
}).then(focus).finally(cleanup);
|
||||||
@ -438,7 +438,7 @@ async function clip() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
|
os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRenoteMenu(viaKeyboard = false): void {
|
function showRenoteMenu(viaKeyboard = false): void {
|
||||||
@ -453,7 +453,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;
|
||||||
},
|
},
|
||||||
@ -463,7 +463,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
|||||||
if (isMyRenote) {
|
if (isMyRenote) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
os.popupMenu([
|
os.popupMenu([
|
||||||
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
|
getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
|
||||||
null,
|
null,
|
||||||
getUnrenote(),
|
getUnrenote(),
|
||||||
], renoteTime.value, {
|
], renoteTime.value, {
|
||||||
@ -471,9 +471,9 @@ function showRenoteMenu(viaKeyboard = false): void {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu([
|
os.popupMenu([
|
||||||
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
|
getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
|
||||||
null,
|
null,
|
||||||
getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),
|
getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
|
||||||
$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
|
$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
|
||||||
], renoteTime.value, {
|
], renoteTime.value, {
|
||||||
viaKeyboard: viaKeyboard,
|
viaKeyboard: viaKeyboard,
|
||||||
@ -499,7 +499,7 @@ function focusAfter() {
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -235,12 +235,12 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const inChannel = inject('inChannel', null);
|
const inChannel = inject('inChannel', null);
|
||||||
|
|
||||||
let note = $ref(deepClone(props.note));
|
const note = ref(deepClone(props.note));
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result: Misskey.entities.Note | null = deepClone(note);
|
let result: Misskey.entities.Note | null = deepClone(note.value);
|
||||||
for (const interruptor of noteViewInterruptors) {
|
for (const interruptor of noteViewInterruptors) {
|
||||||
try {
|
try {
|
||||||
result = await interruptor.handler(result);
|
result = await interruptor.handler(result);
|
||||||
@ -252,15 +252,15 @@ if (noteViewInterruptors.length > 0) {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 = shallowRef<HTMLElement>();
|
const el = shallowRef<HTMLElement>();
|
||||||
@ -269,19 +269,19 @@ const renoteButton = shallowRef<HTMLElement>();
|
|||||||
const renoteTime = shallowRef<HTMLElement>();
|
const renoteTime = shallowRef<HTMLElement>();
|
||||||
const reactButton = shallowRef<HTMLElement>();
|
const reactButton = shallowRef<HTMLElement>();
|
||||||
const clipButton = shallowRef<HTMLElement>();
|
const clipButton = shallowRef<HTMLElement>();
|
||||||
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
|
const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
|
||||||
const isMyRenote = $i && ($i.id === note.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
|
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const parsed = appearNote.text ? mfm.parse(appearNote.text) : null;
|
const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
|
||||||
const urls = parsed ? extractUrlFromMfm(parsed) : null;
|
const urls = parsed ? extractUrlFromMfm(parsed) : null;
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||||
const conversation = ref<Misskey.entities.Note[]>([]);
|
const conversation = ref<Misskey.entities.Note[]>([]);
|
||||||
const replies = ref<Misskey.entities.Note[]>([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i.id);
|
||||||
|
|
||||||
const keymap = {
|
const keymap = {
|
||||||
'r': () => reply(true),
|
'r': () => reply(true),
|
||||||
@ -294,41 +294,41 @@ const keymap = {
|
|||||||
|
|
||||||
provide('react', (reaction: string) => {
|
provide('react', (reaction: string) => {
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let tab = $ref('replies');
|
const tab = ref('replies');
|
||||||
let reactionTabType = $ref(null);
|
const reactionTabType = ref(null);
|
||||||
|
|
||||||
const renotesPagination = $computed(() => ({
|
const renotesPagination = computed(() => ({
|
||||||
endpoint: 'notes/renotes',
|
endpoint: 'notes/renotes',
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: {
|
params: {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const reactionsPagination = $computed(() => ({
|
const reactionsPagination = computed(() => ({
|
||||||
endpoint: 'notes/reactions',
|
endpoint: 'notes/reactions',
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: {
|
params: {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
type: reactionTabType,
|
type: reactionTabType.value,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
pureNote: $$(note),
|
pureNote: note,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
useTooltip(renoteButton, async (showing) => {
|
useTooltip(renoteButton, async (showing) => {
|
||||||
const renotes = await os.api('notes/renotes', {
|
const renotes = await os.api('notes/renotes', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 11,
|
limit: 11,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ useTooltip(renoteButton, async (showing) => {
|
|||||||
os.popup(MkUsersTooltip, {
|
os.popup(MkUsersTooltip, {
|
||||||
showing,
|
showing,
|
||||||
users,
|
users,
|
||||||
count: appearNote.renoteCount,
|
count: appearNote.value.renoteCount,
|
||||||
targetElement: renoteButton.value,
|
targetElement: renoteButton.value,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
});
|
});
|
||||||
@ -348,7 +348,7 @@ function renote(viaKeyboard = false) {
|
|||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
const { menu } = getRenoteMenu({ note: note, renoteButton });
|
const { menu } = getRenoteMenu({ note: note.value, renoteButton });
|
||||||
os.popupMenu(menu, renoteButton.value, {
|
os.popupMenu(menu, renoteButton.value, {
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
});
|
});
|
||||||
@ -358,8 +358,8 @@ function reply(viaKeyboard = false): void {
|
|||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote,
|
reply: appearNote.value,
|
||||||
channel: appearNote.channel,
|
channel: appearNote.value.channel,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}, () => {
|
}, () => {
|
||||||
focus();
|
focus();
|
||||||
@ -369,11 +369,11 @@ function reply(viaKeyboard = false): void {
|
|||||||
function react(viaKeyboard = false): void {
|
function react(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
if (appearNote.reactionAcceptance === 'likeOnly') {
|
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||||
sound.play('reaction');
|
sound.play('reaction');
|
||||||
|
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: '❤️',
|
reaction: '❤️',
|
||||||
});
|
});
|
||||||
const el = reactButton.value as HTMLElement | null | undefined;
|
const el = reactButton.value as HTMLElement | null | undefined;
|
||||||
@ -389,10 +389,10 @@ function react(viaKeyboard = false): void {
|
|||||||
sound.play('reaction');
|
sound.play('reaction');
|
||||||
|
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
});
|
});
|
||||||
if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
|
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
||||||
claimAchievement('reactWithoutRead');
|
claimAchievement('reactWithoutRead');
|
||||||
}
|
}
|
||||||
}, () => {
|
}, () => {
|
||||||
@ -423,20 +423,20 @@ function onContextmenu(ev: MouseEvent): void {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
|
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
|
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted });
|
||||||
os.popupMenu(menu, menuButton.value, {
|
os.popupMenu(menu, menuButton.value, {
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
}).then(focus).finally(cleanup);
|
}).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clip() {
|
async function clip() {
|
||||||
os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
|
os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted }), clipButton.value).then(focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRenoteMenu(viaKeyboard = false): void {
|
function showRenoteMenu(viaKeyboard = false): void {
|
||||||
@ -448,7 +448,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;
|
||||||
},
|
},
|
||||||
@ -470,7 +470,7 @@ const repliesLoaded = ref(false);
|
|||||||
function loadReplies() {
|
function loadReplies() {
|
||||||
repliesLoaded.value = true;
|
repliesLoaded.value = true;
|
||||||
os.api('notes/children', {
|
os.api('notes/children', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
replies.value = res;
|
replies.value = res;
|
||||||
@ -482,7 +482,7 @@ const conversationLoaded = ref(false);
|
|||||||
function loadConversation() {
|
function loadConversation() {
|
||||||
conversationLoaded.value = true;
|
conversationLoaded.value = true;
|
||||||
os.api('notes/conversation', {
|
os.api('notes/conversation', {
|
||||||
noteId: appearNote.replyId,
|
noteId: appearNote.value.replyId,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
conversation.value = res.reverse();
|
conversation.value = res.reverse();
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||||
@ -33,7 +33,7 @@ const props = defineProps<{
|
|||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showContent = $ref(false);
|
const showContent = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
@ -65,15 +65,15 @@ const props = withDefaults(defineProps<{
|
|||||||
|
|
||||||
const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
|
const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
|
||||||
|
|
||||||
let showContent = $ref(false);
|
const showContent = ref(false);
|
||||||
let replies: Misskey.entities.Note[] = $ref([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
|
|
||||||
if (props.detail) {
|
if (props.detail) {
|
||||||
os.api('notes/children', {
|
os.api('notes/children', {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
limit: 5,
|
limit: 5,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
replies = res;
|
replies.value = res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, Ref } from 'vue';
|
import { ref, Ref, shallowRef } from 'vue';
|
||||||
import MkSwitch from './MkSwitch.vue';
|
import MkSwitch from './MkSwitch.vue';
|
||||||
import MkInfo from './MkInfo.vue';
|
import MkInfo from './MkInfo.vue';
|
||||||
import MkButton from './MkButton.vue';
|
import MkButton from './MkButton.vue';
|
||||||
@ -51,7 +51,7 @@ const props = withDefaults(defineProps<{
|
|||||||
excludeTypes: () => [],
|
excludeTypes: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
|
|
||||||
const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
|
const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ function ok() {
|
|||||||
.filter(type => !typesMap[type].value),
|
.filter(type => !typesMap[type].value),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dialog) dialog.close();
|
if (dialog.value) dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableAll() {
|
function disableAll() {
|
||||||
|
@ -43,7 +43,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
let pagination = $computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
|
const pagination = computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
|
||||||
endpoint: 'i/notifications-grouped' as const,
|
endpoint: 'i/notifications-grouped' as const,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
@ -22,13 +22,13 @@ const props = withDefaults(defineProps<{
|
|||||||
maxHeight: 200,
|
maxHeight: 200,
|
||||||
});
|
});
|
||||||
|
|
||||||
let content = $shallowRef<HTMLElement>();
|
const content = shallowRef<HTMLElement>();
|
||||||
let omitted = $ref(false);
|
const omitted = ref(false);
|
||||||
let ignoreOmit = $ref(false);
|
const ignoreOmit = ref(false);
|
||||||
|
|
||||||
const calcOmit = () => {
|
const calcOmit = () => {
|
||||||
if (omitted || ignoreOmit) return;
|
if (omitted.value || ignoreOmit.value) return;
|
||||||
omitted = content.offsetHeight > props.maxHeight;
|
omitted.value = content.value.offsetHeight > props.maxHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
const omitObserver = new ResizeObserver((entries, observer) => {
|
const omitObserver = new ResizeObserver((entries, observer) => {
|
||||||
@ -37,7 +37,7 @@ const omitObserver = new ResizeObserver((entries, observer) => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
calcOmit();
|
calcOmit();
|
||||||
omitObserver.observe(content);
|
omitObserver.observe(content.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ComputedRef, onMounted, onUnmounted, provide, shallowRef } from 'vue';
|
import { ComputedRef, onMounted, onUnmounted, provide, shallowRef, ref, computed } from 'vue';
|
||||||
import RouterView from '@/components/global/RouterView.vue';
|
import RouterView from '@/components/global/RouterView.vue';
|
||||||
import MkWindow from '@/components/MkWindow.vue';
|
import MkWindow from '@/components/MkWindow.vue';
|
||||||
import { popout as _popout } from '@/scripts/popout.js';
|
import { popout as _popout } from '@/scripts/popout.js';
|
||||||
@ -55,16 +55,16 @@ defineEmits<{
|
|||||||
const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue')));
|
const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue')));
|
||||||
|
|
||||||
const contents = shallowRef<HTMLElement>();
|
const contents = shallowRef<HTMLElement>();
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||||
let windowEl = $shallowRef<InstanceType<typeof MkWindow>>();
|
const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
|
||||||
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: 'ti ti-arrow-left',
|
icon: 'ti ti-arrow-left',
|
||||||
onClick: back,
|
onClick: back,
|
||||||
@ -73,7 +73,7 @@ const buttonsLeft = $computed(() => {
|
|||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
});
|
});
|
||||||
const buttonsRight = $computed(() => {
|
const buttonsRight = computed(() => {
|
||||||
const buttons = [{
|
const buttons = [{
|
||||||
icon: 'ti ti-reload',
|
icon: 'ti ti-reload',
|
||||||
title: i18n.ts.reload,
|
title: i18n.ts.reload,
|
||||||
@ -86,21 +86,21 @@ const buttonsRight = $computed(() => {
|
|||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
});
|
});
|
||||||
let reloadCount = $ref(0);
|
const reloadCount = ref(0);
|
||||||
|
|
||||||
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('shouldHeaderThin', true);
|
provide('shouldHeaderThin', true);
|
||||||
provide('forceSpacerMin', true);
|
provide('forceSpacerMin', true);
|
||||||
|
|
||||||
const contextmenu = $computed(() => ([{
|
const contextmenu = computed(() => ([{
|
||||||
icon: 'ti ti-player-eject',
|
icon: 'ti ti-player-eject',
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
action: expand,
|
action: expand,
|
||||||
@ -113,7 +113,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();
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-link',
|
icon: 'ti ti-link',
|
||||||
@ -124,26 +124,26 @@ const contextmenu = $computed(() => ([{
|
|||||||
}]));
|
}]));
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
history.pop();
|
history.value.pop();
|
||||||
router.replace(history.at(-1)!.path, history.at(-1)!.key);
|
router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
reloadCount++;
|
reloadCount.value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
useScrollPositionManager(() => getScrollContainer(contents.value), router);
|
useScrollPositionManager(() => getScrollContainer(contents.value), router);
|
||||||
|
@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, watch } from 'vue';
|
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
|
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
|
||||||
@ -105,12 +105,12 @@ const emit = defineEmits<{
|
|||||||
(ev: 'status', error: boolean): void;
|
(ev: 'status', error: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let rootEl = $shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
// 遡り中かどうか
|
// 遡り中かどうか
|
||||||
let backed = $ref(false);
|
const backed = ref(false);
|
||||||
|
|
||||||
let scrollRemove = $ref<(() => void) | null>(null);
|
const scrollRemove = ref<(() => void) | null>(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表示するアイテムのソース
|
* 表示するアイテムのソース
|
||||||
@ -142,8 +142,8 @@ const {
|
|||||||
enableInfiniteScroll,
|
enableInfiniteScroll,
|
||||||
} = defaultStore.reactiveState;
|
} = defaultStore.reactiveState;
|
||||||
|
|
||||||
const contentEl = $computed(() => props.pagination.pageEl ?? rootEl);
|
const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
|
||||||
const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body);
|
const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body);
|
||||||
|
|
||||||
const visibility = useDocumentVisibility();
|
const visibility = useDocumentVisibility();
|
||||||
|
|
||||||
@ -153,35 +153,35 @@ const BACKGROUND_PAUSE_WAIT_SEC = 10;
|
|||||||
|
|
||||||
// 先頭が表示されているかどうかを検出
|
// 先頭が表示されているかどうかを検出
|
||||||
// https://qiita.com/mkataigi/items/0154aefd2223ce23398e
|
// https://qiita.com/mkataigi/items/0154aefd2223ce23398e
|
||||||
let scrollObserver = $ref<IntersectionObserver>();
|
const scrollObserver = ref<IntersectionObserver>();
|
||||||
|
|
||||||
watch([() => props.pagination.reversed, $$(scrollableElement)], () => {
|
watch([() => props.pagination.reversed, scrollableElement], () => {
|
||||||
if (scrollObserver) scrollObserver.disconnect();
|
if (scrollObserver.value) scrollObserver.value.disconnect();
|
||||||
|
|
||||||
scrollObserver = new IntersectionObserver(entries => {
|
scrollObserver.value = new IntersectionObserver(entries => {
|
||||||
backed = entries[0].isIntersecting;
|
backed.value = entries[0].isIntersecting;
|
||||||
}, {
|
}, {
|
||||||
root: scrollableElement,
|
root: scrollableElement.value,
|
||||||
rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px',
|
rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px',
|
||||||
threshold: 0.01,
|
threshold: 0.01,
|
||||||
});
|
});
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
watch($$(rootEl), () => {
|
watch(rootEl, () => {
|
||||||
scrollObserver?.disconnect();
|
scrollObserver.value?.disconnect();
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (rootEl) scrollObserver?.observe(rootEl);
|
if (rootEl.value) scrollObserver.value?.observe(rootEl.value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch([$$(backed), $$(contentEl)], () => {
|
watch([backed, contentEl], () => {
|
||||||
if (!backed) {
|
if (!backed.value) {
|
||||||
if (!contentEl) return;
|
if (!contentEl.value) return;
|
||||||
|
|
||||||
scrollRemove = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl, executeQueue, TOLERANCE);
|
scrollRemove.value = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl.value, executeQueue, TOLERANCE);
|
||||||
} else {
|
} else {
|
||||||
if (scrollRemove) scrollRemove();
|
if (scrollRemove.value) scrollRemove.value();
|
||||||
scrollRemove = null;
|
scrollRemove.value = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -254,14 +254,14 @@ const fetchMore = async (): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reverseConcat = _res => {
|
const reverseConcat = _res => {
|
||||||
const oldHeight = scrollableElement ? scrollableElement.scrollHeight : getBodyScrollHeight();
|
const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight();
|
||||||
const oldScroll = scrollableElement ? scrollableElement.scrollTop : window.scrollY;
|
const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY;
|
||||||
|
|
||||||
items.value = concatMapWithArray(items.value, _res);
|
items.value = concatMapWithArray(items.value, _res);
|
||||||
|
|
||||||
return nextTick(() => {
|
return nextTick(() => {
|
||||||
if (scrollableElement) {
|
if (scrollableElement.value) {
|
||||||
scroll(scrollableElement, { top: oldScroll + (scrollableElement.scrollHeight - oldHeight), behavior: 'instant' });
|
scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
|
||||||
} else {
|
} else {
|
||||||
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ const appearFetchMoreAhead = async (): Promise<void> => {
|
|||||||
fetchMoreAppearTimeout();
|
fetchMoreAppearTimeout();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE);
|
const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE);
|
||||||
|
|
||||||
watch(visibility, () => {
|
watch(visibility, () => {
|
||||||
if (visibility.value === 'hidden') {
|
if (visibility.value === 'hidden') {
|
||||||
@ -445,11 +445,11 @@ onActivated(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onDeactivated(() => {
|
onDeactivated(() => {
|
||||||
isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl ? rootEl.scrollHeight - window.innerHeight : 0) : window.scrollY === 0;
|
isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl.value ? rootEl.value.scrollHeight - window.innerHeight : 0) : window.scrollY === 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
function toBottom() {
|
function toBottom() {
|
||||||
scrollToBottom(contentEl!);
|
scrollToBottom(contentEl.value!);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
@ -477,13 +477,13 @@ onBeforeUnmount(() => {
|
|||||||
clearTimeout(preventAppearFetchMoreTimer.value);
|
clearTimeout(preventAppearFetchMoreTimer.value);
|
||||||
preventAppearFetchMoreTimer.value = null;
|
preventAppearFetchMoreTimer.value = null;
|
||||||
}
|
}
|
||||||
scrollObserver?.disconnect();
|
scrollObserver.value?.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
items,
|
items,
|
||||||
queue,
|
queue,
|
||||||
backed,
|
backed: backed.value,
|
||||||
more,
|
more,
|
||||||
reload,
|
reload,
|
||||||
prepend,
|
prepend,
|
||||||
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, shallowRef, ref } from 'vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
@ -49,22 +49,22 @@ const emit = defineEmits<{
|
|||||||
(ev: 'cancelled'): void;
|
(ev: 'cancelled'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
const passwordInput = $shallowRef<InstanceType<typeof MkInput>>();
|
const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
|
||||||
const password = $ref('');
|
const password = ref('');
|
||||||
const token = $ref(null);
|
const token = ref(null);
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit('cancelled');
|
emit('cancelled');
|
||||||
if (dialog) dialog.close();
|
if (dialog.value) dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function done(res) {
|
function done(res) {
|
||||||
emit('done', { password, token });
|
emit('done', { password: password.value, token: token.value });
|
||||||
if (dialog) dialog.close();
|
if (dialog.value) dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (passwordInput) passwordInput.focus();
|
if (passwordInput.value) passwordInput.value.focus();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
@ -23,13 +23,13 @@ const emit = defineEmits<{
|
|||||||
(ev: 'end'): void;
|
(ev: 'end'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let up = $ref(false);
|
const up = ref(false);
|
||||||
const zIndex = os.claimZIndex('middle');
|
const zIndex = os.claimZIndex('middle');
|
||||||
const angle = (45 - (Math.random() * 90)) + 'deg';
|
const angle = (45 - (Math.random() * 90)) + 'deg';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
up = true;
|
up.value = true;
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref, shallowRef } 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';
|
||||||
@ -28,7 +28,7 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closing'): void;
|
(ev: 'closing'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const manualShowing = ref(true);
|
const manualShowing = ref(true);
|
||||||
const hiding = ref(false);
|
const hiding = ref(false);
|
||||||
|
|
||||||
@ -60,14 +60,14 @@ function hide() {
|
|||||||
hiding.value = true;
|
hiding.value = true;
|
||||||
|
|
||||||
// closeは呼ぶ必要がある
|
// closeは呼ぶ必要がある
|
||||||
modal?.close();
|
modal.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
manualShowing.value = false;
|
manualShowing.value = false;
|
||||||
|
|
||||||
// closeは呼ぶ必要がある
|
// closeは呼ぶ必要がある
|
||||||
modal?.close();
|
modal.value?.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, ref } from 'vue';
|
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||||
@ -162,42 +162,42 @@ const emit = defineEmits<{
|
|||||||
(ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
|
(ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null);
|
const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
|
||||||
const cwInputEl = $shallowRef<HTMLInputElement | null>(null);
|
const cwInputEl = shallowRef<HTMLInputElement | null>(null);
|
||||||
const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null);
|
const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
|
||||||
const visibilityButton = $shallowRef<HTMLElement | null>(null);
|
const visibilityButton = shallowRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
let posting = $ref(false);
|
const posting = ref(false);
|
||||||
let posted = $ref(false);
|
const posted = ref(false);
|
||||||
let text = $ref(props.initialText ?? '');
|
const text = ref(props.initialText ?? '');
|
||||||
let files = $ref(props.initialFiles ?? []);
|
const files = ref(props.initialFiles ?? []);
|
||||||
let poll = $ref<{
|
const 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);
|
const useCw = ref(false);
|
||||||
let showPreview = $ref(defaultStore.state.showPreview);
|
const showPreview = ref(defaultStore.state.showPreview);
|
||||||
watch($$(showPreview), () => defaultStore.set('showPreview', showPreview));
|
watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
|
||||||
let cw = $ref<string | null>(null);
|
const cw = ref<string | null>(null);
|
||||||
let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
||||||
let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
|
const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
|
||||||
let visibleUsers = $ref([]);
|
const visibleUsers = ref([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance);
|
const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
|
||||||
let autocomplete = $ref(null);
|
const autocomplete = ref(null);
|
||||||
let draghover = $ref(false);
|
const draghover = ref(false);
|
||||||
let quoteId = $ref(null);
|
const quoteId = ref(null);
|
||||||
let hasNotSpecifiedMentions = $ref(false);
|
const hasNotSpecifiedMentions = ref(false);
|
||||||
let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
|
const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
|
||||||
let imeText = $ref('');
|
const imeText = ref('');
|
||||||
let showingOptions = $ref(false);
|
const showingOptions = ref(false);
|
||||||
const textAreaReadOnly = ref(false);
|
const textAreaReadOnly = ref(false);
|
||||||
|
|
||||||
const draftKey = $computed((): string => {
|
const draftKey = computed((): string => {
|
||||||
let key = props.channel ? `channel:${props.channel.id}` : '';
|
let key = props.channel ? `channel:${props.channel.id}` : '';
|
||||||
|
|
||||||
if (props.renote) {
|
if (props.renote) {
|
||||||
@ -211,7 +211,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) {
|
||||||
@ -231,7 +231,7 @@ const placeholder = $computed((): string => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitText = $computed((): string => {
|
const submitText = computed((): string => {
|
||||||
return props.renote
|
return props.renote
|
||||||
? i18n.ts.quote
|
? i18n.ts.quote
|
||||||
: props.reply
|
: props.reply
|
||||||
@ -239,45 +239,45 @@ const submitText = $computed((): string => {
|
|||||||
: i18n.ts.note;
|
: i18n.ts.note;
|
||||||
});
|
});
|
||||||
|
|
||||||
const textLength = $computed((): number => {
|
const textLength = computed((): number => {
|
||||||
return (text + imeText).trim().length;
|
return (text.value + imeText.value).trim().length;
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxTextLength = $computed((): number => {
|
const maxTextLength = computed((): number => {
|
||||||
return instance ? instance.maxNoteTextLength : 1000;
|
return instance ? instance.maxNoteTextLength : 1000;
|
||||||
});
|
});
|
||||||
|
|
||||||
const canPost = $computed((): boolean => {
|
const canPost = computed((): boolean => {
|
||||||
return !props.mock && !posting && !posted &&
|
return !props.mock && !posting.value && !posted.value &&
|
||||||
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
|
(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
|
||||||
(textLength <= maxTextLength) &&
|
(textLength.value <= maxTextLength.value) &&
|
||||||
(!poll || poll.choices.length >= 2);
|
(!poll.value || poll.value.choices.length >= 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
|
const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
|
||||||
const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags'));
|
const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
|
||||||
|
|
||||||
watch($$(text), () => {
|
watch(text, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
watch($$(visibility), () => {
|
watch(visibility, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
watch($$(visibleUsers), () => {
|
watch(visibleUsers, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
}, {
|
}, {
|
||||||
deep: true,
|
deep: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.mention) {
|
if (props.mention) {
|
||||||
text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
|
text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
|
||||||
text += ' ';
|
text.value += ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
|
if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
|
||||||
text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
|
text.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.reply && props.reply.text != null) {
|
if (props.reply && props.reply.text != null) {
|
||||||
@ -295,32 +295,32 @@ if (props.reply && props.reply.text != null) {
|
|||||||
if ($i.username === x.username && (x.host == null || x.host === host)) continue;
|
if ($i.username === x.username && (x.host == null || x.host === host)) continue;
|
||||||
|
|
||||||
// 重複は除外
|
// 重複は除外
|
||||||
if (text.includes(`${mention} `)) continue;
|
if (text.value.includes(`${mention} `)) continue;
|
||||||
|
|
||||||
text += `${mention} `;
|
text.value += `${mention} `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($i?.isSilenced && visibility === 'public') {
|
if ($i?.isSilenced && visibility.value === 'public') {
|
||||||
visibility = 'home';
|
visibility.value = 'home';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
visibility = 'public';
|
visibility.value = 'public';
|
||||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||||
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
|
if (props.reply && ['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 (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') {
|
} else if (['home', 'followers'].includes(props.reply.visibility) && 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(uid => uid !== $i.id && uid !== props.reply.userId),
|
userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
|
||||||
@ -338,57 +338,57 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (props.specified) {
|
if (props.specified) {
|
||||||
visibility = 'specified';
|
visibility.value = 'specified';
|
||||||
pushVisibleUser(props.specified);
|
pushVisibleUser(props.specified);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 = props.reply.cw;
|
cw.value = 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 (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
|
if (!visibleUsers.value.some(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 (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
|
if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
|
||||||
os.api('users/show', { username: x.username, host: x.host }).then(user => {
|
os.api('users/show', { username: x.username, host: x.host }).then(user => {
|
||||||
visibleUsers.push(user);
|
visibleUsers.value.push(user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -398,13 +398,13 @@ 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.length, textareaEl.value.length);
|
textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,55 +413,55 @@ function chooseFileFrom(ev) {
|
|||||||
|
|
||||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
|
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(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 updateFileSensitive(file, sensitive) {
|
function updateFileSensitive(file, sensitive) {
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
emit('fileChangeSensitive', file.id, sensitive);
|
emit('fileChangeSensitive', file.id, 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 replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
|
function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
|
||||||
files[files.findIndex(x => x.id === file.id)] = newFile;
|
files.value[files.value.findIndex(x => x.id === file.id)] = newFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
function upload(file: File, name?: string): void {
|
function upload(file: File, name?: string): void {
|
||||||
if (props.mock) return;
|
if (props.mock) return;
|
||||||
|
|
||||||
uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
|
uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
|
||||||
files.push(res);
|
files.value.push(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVisibility() {
|
function setVisibility() {
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
visibility = 'public';
|
visibility.value = 'public';
|
||||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
|
||||||
currentVisibility: visibility,
|
currentVisibility: visibility.value,
|
||||||
isSilenced: $i?.isSilenced,
|
isSilenced: $i?.isSilenced,
|
||||||
localOnly: localOnly,
|
localOnly: 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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
@ -469,14 +469,14 @@ function setVisibility() {
|
|||||||
|
|
||||||
async function toggleLocalOnly() {
|
async function toggleLocalOnly() {
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
visibility = 'public';
|
visibility.value = 'public';
|
||||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
|
const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
|
||||||
|
|
||||||
if (!localOnly && neverShowInfo !== 'true') {
|
if (!localOnly.value && neverShowInfo !== 'true') {
|
||||||
const confirm = await os.actions({
|
const confirm = await os.actions({
|
||||||
type: 'question',
|
type: 'question',
|
||||||
title: i18n.ts.disableFederationConfirm,
|
title: i18n.ts.disableFederationConfirm,
|
||||||
@ -506,7 +506,7 @@ async function toggleLocalOnly() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localOnly = !localOnly;
|
localOnly.value = !localOnly.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleReactionAcceptance() {
|
async function toggleReactionAcceptance() {
|
||||||
@ -519,15 +519,15 @@ async function toggleReactionAcceptance() {
|
|||||||
{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
|
{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
|
||||||
{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
|
{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
|
||||||
],
|
],
|
||||||
default: reactionAcceptance,
|
default: reactionAcceptance.value,
|
||||||
});
|
});
|
||||||
if (select.canceled) return;
|
if (select.canceled) return;
|
||||||
reactionAcceptance = select.result;
|
reactionAcceptance.value = select.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushVisibleUser(user) {
|
function pushVisibleUser(user) {
|
||||||
if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) {
|
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
|
||||||
visibleUsers.push(user);
|
visibleUsers.value.push(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,34 +535,34 @@ function addVisibleUser() {
|
|||||||
os.selectUser().then(user => {
|
os.selectUser().then(user => {
|
||||||
pushVisibleUser(user);
|
pushVisibleUser(user);
|
||||||
|
|
||||||
if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
|
if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
|
||||||
text = `@${Misskey.acct.toString(user)} ${text}`;
|
text.value = `@${Misskey.acct.toString(user)} ${text.value}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post();
|
if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
|
||||||
if (ev.key === 'Escape') emit('esc');
|
if (ev.key === 'Escape') emit('esc');
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionUpdate(ev: CompositionEvent) {
|
function onCompositionUpdate(ev: CompositionEvent) {
|
||||||
imeText = ev.data;
|
imeText.value = ev.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionEnd(ev: CompositionEvent) {
|
function onCompositionEnd(ev: CompositionEvent) {
|
||||||
imeText = '';
|
imeText.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPaste(ev: ClipboardEvent) {
|
async function onPaste(ev: ClipboardEvent) {
|
||||||
@ -580,7 +580,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.confirm({
|
os.confirm({
|
||||||
@ -588,11 +588,11 @@ 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.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
|
quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -603,7 +603,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':
|
||||||
@ -624,15 +624,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) {
|
||||||
@ -645,7 +645,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
|
||||||
@ -656,16 +656,16 @@ function saveDraft() {
|
|||||||
|
|
||||||
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
|
const draftData = JSON.parse(miLocalStorage.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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -675,13 +675,13 @@ function saveDraft() {
|
|||||||
function deleteDraft() {
|
function deleteDraft() {
|
||||||
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
|
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
|
||||||
|
|
||||||
delete draftData[draftKey];
|
delete draftData[draftKey.value];
|
||||||
|
|
||||||
miLocalStorage.setItem('drafts', JSON.stringify(draftData));
|
miLocalStorage.setItem('drafts', JSON.stringify(draftData));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function post(ev?: MouseEvent) {
|
async function post(ev?: MouseEvent) {
|
||||||
if (useCw && (cw == null || cw.trim() === '')) {
|
if (useCw.value && (cw.value == null || cw.value.trim() === '')) {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: i18n.ts.cwNotationRequired,
|
text: i18n.ts.cwNotationRequired,
|
||||||
@ -700,13 +700,13 @@ async function post(ev?: MouseEvent) {
|
|||||||
if (props.mock) return;
|
if (props.mock) return;
|
||||||
|
|
||||||
const annoying =
|
const annoying =
|
||||||
text.includes('$[x2') ||
|
text.value.includes('$[x2') ||
|
||||||
text.includes('$[x3') ||
|
text.value.includes('$[x3') ||
|
||||||
text.includes('$[x4') ||
|
text.value.includes('$[x4') ||
|
||||||
text.includes('$[scale') ||
|
text.value.includes('$[scale') ||
|
||||||
text.includes('$[position');
|
text.value.includes('$[position');
|
||||||
|
|
||||||
if (annoying && visibility === 'public') {
|
if (annoying && visibility.value === 'public') {
|
||||||
const { canceled, result } = await os.actions({
|
const { canceled, result } = await os.actions({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
text: i18n.ts.thisPostMayBeAnnoying,
|
text: i18n.ts.thisPostMayBeAnnoying,
|
||||||
@ -726,26 +726,26 @@ async function post(ev?: MouseEvent) {
|
|||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
if (result === 'cancel') return;
|
if (result === 'cancel') return;
|
||||||
if (result === 'home') {
|
if (result === 'home') {
|
||||||
visibility = 'home';
|
visibility.value = 'home';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let postData = {
|
let postData = {
|
||||||
text: text === '' ? null : text,
|
text: text.value === '' ? null : text.value,
|
||||||
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 ? props.renote.id : quoteId ? quoteId : undefined,
|
renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
|
||||||
channelId: props.channel ? props.channel.id : undefined,
|
channelId: props.channel ? props.channel.id : undefined,
|
||||||
poll: poll,
|
poll: poll.value,
|
||||||
cw: useCw ? cw ?? '' : null,
|
cw: useCw.value ? cw.value ?? '' : null,
|
||||||
localOnly: localOnly,
|
localOnly: localOnly.value,
|
||||||
visibility: visibility,
|
visibility: visibility.value,
|
||||||
visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
|
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
|
||||||
reactionAcceptance,
|
reactionAcceptance: reactionAcceptance.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (withHashtags && hashtags && hashtags.trim() !== '') {
|
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
|
||||||
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
|
const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
|
||||||
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,15 +762,15 @@ async function post(ev?: MouseEvent) {
|
|||||||
|
|
||||||
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('notes/create', postData, token).then(() => {
|
os.api('notes/create', postData, token).then(() => {
|
||||||
if (props.freezeAfterPosted) {
|
if (props.freezeAfterPosted) {
|
||||||
posted = true;
|
posted.value = true;
|
||||||
} else {
|
} else {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
@ -782,8 +782,8 @@ async function post(ev?: MouseEvent) {
|
|||||||
const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
|
const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
|
||||||
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
|
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
|
||||||
}
|
}
|
||||||
posting = false;
|
posting.value = false;
|
||||||
postAccount = null;
|
postAccount.value = null;
|
||||||
|
|
||||||
incNotesCount();
|
incNotesCount();
|
||||||
if (notesCount === 1) {
|
if (notesCount === 1) {
|
||||||
@ -828,7 +828,7 @@ async function post(ev?: MouseEvent) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).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,
|
||||||
@ -842,7 +842,7 @@ function cancel() {
|
|||||||
|
|
||||||
function insertMention() {
|
function insertMention() {
|
||||||
os.selectUser().then(user => {
|
os.selectUser().then(user => {
|
||||||
insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' ');
|
insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' ');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -852,7 +852,7 @@ async function insertEmoji(ev: MouseEvent) {
|
|||||||
emojiPicker.show(
|
emojiPicker.show(
|
||||||
ev.currentTarget ?? ev.target,
|
ev.currentTarget ?? ev.target,
|
||||||
emoji => {
|
emoji => {
|
||||||
insertTextAtCursor(textareaEl, emoji);
|
insertTextAtCursor(textareaEl.value, emoji);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
textAreaReadOnly.value = false;
|
textAreaReadOnly.value = false;
|
||||||
@ -866,17 +866,17 @@ function showActions(ev) {
|
|||||||
text: action.title,
|
text: action.title,
|
||||||
action: () => {
|
action: () => {
|
||||||
action.handler({
|
action.handler({
|
||||||
text: text,
|
text: text.value,
|
||||||
cw: cw,
|
cw: cw.value,
|
||||||
}, (key, value) => {
|
}, (key, value) => {
|
||||||
if (key === 'text') { text = value; }
|
if (key === 'text') { text.value = value; }
|
||||||
if (key === 'cw') { useCw = value !== null; cw = value; }
|
if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})), ev.currentTarget ?? ev.target);
|
})), ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
let postAccount = $ref<Misskey.entities.UserDetailed | null>(null);
|
const postAccount = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
|
|
||||||
function openAccountMenu(ev: MouseEvent) {
|
function openAccountMenu(ev: MouseEvent) {
|
||||||
if (props.mock) return;
|
if (props.mock) return;
|
||||||
@ -884,12 +884,12 @@ 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, ev);
|
}, ev);
|
||||||
@ -905,23 +905,23 @@ 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);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 書きかけの投稿を復元
|
// 書きかけの投稿を復元
|
||||||
if (!props.instant && !props.mention && !props.specified && !props.mock) {
|
if (!props.instant && !props.mention && !props.specified && !props.mock) {
|
||||||
const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey];
|
const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[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(draftFile => draftFile);
|
files.value = (draft.data.files || []).filter(draftFile => draftFile);
|
||||||
if (draft.data.poll) {
|
if (draft.data.poll) {
|
||||||
poll = draft.data.poll;
|
poll.value = draft.data.poll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -929,21 +929,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());
|
||||||
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkPostForm from '@/components/MkPostForm.vue';
|
import MkPostForm from '@/components/MkPostForm.vue';
|
||||||
@ -36,11 +36,11 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
|
const form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
function onPosted() {
|
function onPosted() {
|
||||||
modal.close({
|
modal.value.close({
|
||||||
useSendAnimation: true,
|
useSendAnimation: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, watch } from 'vue';
|
import { onMounted, onUnmounted, watch, ref, shallowRef } from 'vue';
|
||||||
import { deviceKind } from '@/scripts/device-kind.js';
|
import { deviceKind } from '@/scripts/device-kind.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { getScrollContainer } from '@/scripts/scroll.js';
|
import { getScrollContainer } from '@/scripts/scroll.js';
|
||||||
@ -35,15 +35,15 @@ const RELEASE_TRANSITION_DURATION = 200;
|
|||||||
const PULL_BRAKE_BASE = 1.5;
|
const PULL_BRAKE_BASE = 1.5;
|
||||||
const PULL_BRAKE_FACTOR = 170;
|
const PULL_BRAKE_FACTOR = 170;
|
||||||
|
|
||||||
let isPullStart = $ref(false);
|
const isPullStart = ref(false);
|
||||||
let isPullEnd = $ref(false);
|
const isPullEnd = ref(false);
|
||||||
let isRefreshing = $ref(false);
|
const isRefreshing = ref(false);
|
||||||
let pullDistance = $ref(0);
|
const pullDistance = ref(0);
|
||||||
|
|
||||||
let supportPointerDesktop = false;
|
let supportPointerDesktop = false;
|
||||||
let startScreenY: number | null = null;
|
let startScreenY: number | null = null;
|
||||||
|
|
||||||
const rootEl = $shallowRef<HTMLDivElement>();
|
const rootEl = shallowRef<HTMLDivElement>();
|
||||||
let scrollEl: HTMLElement | null = null;
|
let scrollEl: HTMLElement | null = null;
|
||||||
|
|
||||||
let disabled = false;
|
let disabled = false;
|
||||||
@ -66,17 +66,17 @@ function getScreenY(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function moveStart(event) {
|
function moveStart(event) {
|
||||||
if (!isPullStart && !isRefreshing && !disabled) {
|
if (!isPullStart.value && !isRefreshing.value && !disabled) {
|
||||||
isPullStart = true;
|
isPullStart.value = true;
|
||||||
startScreenY = getScreenY(event);
|
startScreenY = getScreenY(event);
|
||||||
pullDistance = 0;
|
pullDistance.value = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveBySystem(to: number): Promise<void> {
|
function moveBySystem(to: number): Promise<void> {
|
||||||
return new Promise(r => {
|
return new Promise(r => {
|
||||||
const startHeight = pullDistance;
|
const startHeight = pullDistance.value;
|
||||||
const overHeight = pullDistance - to;
|
const overHeight = pullDistance.value - to;
|
||||||
if (overHeight < 1) {
|
if (overHeight < 1) {
|
||||||
r();
|
r();
|
||||||
return;
|
return;
|
||||||
@ -85,36 +85,36 @@ function moveBySystem(to: number): Promise<void> {
|
|||||||
let intervalId = setInterval(() => {
|
let intervalId = setInterval(() => {
|
||||||
const time = Date.now() - startTime;
|
const time = Date.now() - startTime;
|
||||||
if (time > RELEASE_TRANSITION_DURATION) {
|
if (time > RELEASE_TRANSITION_DURATION) {
|
||||||
pullDistance = to;
|
pullDistance.value = to;
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
r();
|
r();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time;
|
const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time;
|
||||||
if (pullDistance < nextHeight) return;
|
if (pullDistance.value < nextHeight) return;
|
||||||
pullDistance = nextHeight;
|
pullDistance.value = nextHeight;
|
||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fixOverContent() {
|
async function fixOverContent() {
|
||||||
if (pullDistance > FIRE_THRESHOLD) {
|
if (pullDistance.value > FIRE_THRESHOLD) {
|
||||||
await moveBySystem(FIRE_THRESHOLD);
|
await moveBySystem(FIRE_THRESHOLD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeContent() {
|
async function closeContent() {
|
||||||
if (pullDistance > 0) {
|
if (pullDistance.value > 0) {
|
||||||
await moveBySystem(0);
|
await moveBySystem(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveEnd() {
|
function moveEnd() {
|
||||||
if (isPullStart && !isRefreshing) {
|
if (isPullStart.value && !isRefreshing.value) {
|
||||||
startScreenY = null;
|
startScreenY = null;
|
||||||
if (isPullEnd) {
|
if (isPullEnd.value) {
|
||||||
isPullEnd = false;
|
isPullEnd.value = false;
|
||||||
isRefreshing = true;
|
isRefreshing.value = true;
|
||||||
fixOverContent().then(() => {
|
fixOverContent().then(() => {
|
||||||
emit('refresh');
|
emit('refresh');
|
||||||
props.refresher().then(() => {
|
props.refresher().then(() => {
|
||||||
@ -122,17 +122,17 @@ function moveEnd() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
closeContent().then(() => isPullStart = false);
|
closeContent().then(() => isPullStart.value = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function moving(event: TouchEvent | PointerEvent) {
|
function moving(event: TouchEvent | PointerEvent) {
|
||||||
if (!isPullStart || isRefreshing || disabled) return;
|
if (!isPullStart.value || isRefreshing.value || disabled) return;
|
||||||
|
|
||||||
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance)) {
|
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) {
|
||||||
pullDistance = 0;
|
pullDistance.value = 0;
|
||||||
isPullEnd = false;
|
isPullEnd.value = false;
|
||||||
moveEnd();
|
moveEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -143,13 +143,13 @@ function moving(event: TouchEvent | PointerEvent) {
|
|||||||
const moveScreenY = getScreenY(event);
|
const moveScreenY = getScreenY(event);
|
||||||
|
|
||||||
const moveHeight = moveScreenY - startScreenY!;
|
const moveHeight = moveScreenY - startScreenY!;
|
||||||
pullDistance = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
|
pullDistance.value = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
|
||||||
|
|
||||||
if (pullDistance > 0) {
|
if (pullDistance.value > 0) {
|
||||||
if (event.cancelable) event.preventDefault();
|
if (event.cancelable) event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
isPullEnd = pullDistance >= FIRE_THRESHOLD;
|
isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,8 +159,8 @@ function moving(event: TouchEvent | PointerEvent) {
|
|||||||
*/
|
*/
|
||||||
function refreshFinished() {
|
function refreshFinished() {
|
||||||
closeContent().then(() => {
|
closeContent().then(() => {
|
||||||
isPullStart = false;
|
isPullStart.value = false;
|
||||||
isRefreshing = false;
|
isRefreshing.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,26 +182,26 @@ function onScrollContainerScroll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerEventListenersForReadyToPull() {
|
function registerEventListenersForReadyToPull() {
|
||||||
if (rootEl == null) return;
|
if (rootEl.value == null) return;
|
||||||
rootEl.addEventListener('touchstart', moveStart, { passive: true });
|
rootEl.value.addEventListener('touchstart', moveStart, { passive: true });
|
||||||
rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない
|
rootEl.value.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない
|
||||||
}
|
}
|
||||||
|
|
||||||
function unregisterEventListenersForReadyToPull() {
|
function unregisterEventListenersForReadyToPull() {
|
||||||
if (rootEl == null) return;
|
if (rootEl.value == null) return;
|
||||||
rootEl.removeEventListener('touchstart', moveStart);
|
rootEl.value.removeEventListener('touchstart', moveStart);
|
||||||
rootEl.removeEventListener('touchmove', moving);
|
rootEl.value.removeEventListener('touchmove', moving);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (rootEl == null) return;
|
if (rootEl.value == null) return;
|
||||||
|
|
||||||
scrollEl = getScrollContainer(rootEl);
|
scrollEl = getScrollContainer(rootEl.value);
|
||||||
if (scrollEl == null) return;
|
if (scrollEl == null) return;
|
||||||
|
|
||||||
scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true });
|
scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true });
|
||||||
|
|
||||||
rootEl.addEventListener('touchend', moveEnd, { passive: true });
|
rootEl.value.addEventListener('touchend', moveEnd, { passive: true });
|
||||||
|
|
||||||
registerEventListenersForReadyToPull();
|
registerEventListenersForReadyToPull();
|
||||||
});
|
});
|
||||||
|
@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import { $i, getAccounts } from '@/account.js';
|
import { $i, getAccounts } from '@/account.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
@ -62,26 +63,26 @@ defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
// ServiceWorker registration
|
// ServiceWorker registration
|
||||||
let registration = $ref<ServiceWorkerRegistration | undefined>();
|
const registration = ref<ServiceWorkerRegistration | undefined>();
|
||||||
// If this browser supports push notification
|
// If this browser supports push notification
|
||||||
let supported = $ref(false);
|
const 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);
|
const pushSubscription = ref<PushSubscription | null>(null);
|
||||||
let pushRegistrationInServer = $ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
|
const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
|
||||||
|
|
||||||
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(registration.pushManager.subscribe({
|
return promiseDialog(registration.value.pushManager.subscribe({
|
||||||
userVisibleOnly: true,
|
userVisibleOnly: true,
|
||||||
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
|
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
|
||||||
})
|
})
|
||||||
.then(async subscription => {
|
.then(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')),
|
||||||
@ -102,12 +103,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', {
|
||||||
@ -115,11 +116,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,20 +151,20 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,6 +172,6 @@ if (navigator.serviceWorker == null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
pushRegistrationInServer: $$(pushRegistrationInServer),
|
pushRegistrationInServer: pushRegistrationInServer,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: any;
|
modelValue: any;
|
||||||
@ -36,7 +36,7 @@ const emit = defineEmits<{
|
|||||||
(ev: 'update:modelValue', value: any): void;
|
(ev: 'update:modelValue', value: any): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let checked = $computed(() => props.modelValue === props.value);
|
const checked = computed(() => props.modelValue === props.value);
|
||||||
|
|
||||||
function toggle(): void {
|
function toggle(): void {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
|
|
||||||
@ -27,13 +27,13 @@ const emit = defineEmits<{
|
|||||||
(ev: 'end'): void;
|
(ev: 'end'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let up = $ref(false);
|
const up = ref(false);
|
||||||
const zIndex = os.claimZIndex('middle');
|
const zIndex = os.claimZIndex('middle');
|
||||||
const angle = (90 - (Math.random() * 180)) + 'deg';
|
const angle = (90 - (Math.random() * 180)) + 'deg';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
up = true;
|
up.value = true;
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { inject, watch } from 'vue';
|
import { inject, watch, ref } from 'vue';
|
||||||
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
|
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
@ -38,31 +38,31 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const initialReactions = new Set(Object.keys(props.note.reactions));
|
const initialReactions = new Set(Object.keys(props.note.reactions));
|
||||||
|
|
||||||
let reactions = $ref<[string, number][]>([]);
|
const reactions = ref<[string, number][]>([]);
|
||||||
let hasMoreReactions = $ref(false);
|
const hasMoreReactions = ref(false);
|
||||||
|
|
||||||
if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) {
|
if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) {
|
||||||
reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction];
|
reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction];
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMockToggleReaction(emoji: string, count: number) {
|
function onMockToggleReaction(emoji: string, count: number) {
|
||||||
if (!mock) return;
|
if (!mock) return;
|
||||||
|
|
||||||
const i = reactions.findIndex((item) => item[0] === emoji);
|
const i = reactions.value.findIndex((item) => item[0] === emoji);
|
||||||
if (i < 0) return;
|
if (i < 0) return;
|
||||||
|
|
||||||
emit('mockUpdateMyReaction', emoji, (count - reactions[i][1]));
|
emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
|
watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
|
||||||
let newReactions: [string, number][] = [];
|
let newReactions: [string, number][] = [];
|
||||||
hasMoreReactions = Object.keys(newSource).length > maxNumber;
|
hasMoreReactions.value = Object.keys(newSource).length > maxNumber;
|
||||||
|
|
||||||
for (let i = 0; i < reactions.length; i++) {
|
for (let i = 0; i < reactions.value.length; i++) {
|
||||||
const reaction = reactions[i][0];
|
const reaction = reactions.value[i][0];
|
||||||
if (reaction in newSource && newSource[reaction] !== 0) {
|
if (reaction in newSource && newSource[reaction] !== 0) {
|
||||||
reactions[i][1] = newSource[reaction];
|
reactions.value[i][1] = newSource[reaction];
|
||||||
newReactions.push(reactions[i]);
|
newReactions.push(reactions.value[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
|
|||||||
newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
|
newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
reactions = newReactions;
|
reactions.value = newReactions;
|
||||||
}, { immediate: true, deep: true });
|
}, { immediate: true, deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, nextTick } from 'vue';
|
import { onMounted, nextTick, shallowRef, ref } from 'vue';
|
||||||
import { Chart } from 'chart.js';
|
import { Chart } from 'chart.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -23,11 +23,11 @@ import { initChart } from '@/scripts/init-chart.js';
|
|||||||
|
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
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);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||||
position: 'middle',
|
position: 'middle',
|
||||||
@ -38,8 +38,8 @@ async function renderChart() {
|
|||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const wide = rootEl.offsetWidth > 600;
|
const wide = rootEl.value.offsetWidth > 600;
|
||||||
const narrow = rootEl.offsetWidth < 400;
|
const narrow = rootEl.value.offsetWidth < 400;
|
||||||
|
|
||||||
const maxDays = wide ? 10 : narrow ? 5 : 7;
|
const maxDays = wide ? 10 : narrow ? 5 : 7;
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ async function renderChart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ async function renderChart() {
|
|||||||
|
|
||||||
const marginEachCell = 12;
|
const marginEachCell = 12;
|
||||||
|
|
||||||
chartInstance = new Chart(chartEl, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
type: 'matrix',
|
type: 'matrix',
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
|
@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent, ref } from 'vue';
|
||||||
import { toUnicode } from 'punycode/';
|
import { toUnicode } from 'punycode/';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
|
import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
|
||||||
@ -62,17 +62,17 @@ import * as os from '@/os.js';
|
|||||||
import { login } from '@/account.js';
|
import { login } from '@/account.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
let signing = $ref(false);
|
const signing = ref(false);
|
||||||
let user = $ref<Misskey.entities.UserDetailed | null>(null);
|
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
let username = $ref('');
|
const username = ref('');
|
||||||
let password = $ref('');
|
const password = ref('');
|
||||||
let token = $ref('');
|
const token = ref('');
|
||||||
let host = $ref(toUnicode(configHost));
|
const host = ref(toUnicode(configHost));
|
||||||
let totpLogin = $ref(false);
|
const totpLogin = ref(false);
|
||||||
let queryingKey = $ref(false);
|
const queryingKey = ref(false);
|
||||||
let credentialRequest = $ref<CredentialRequestOptions | null>(null);
|
const credentialRequest = ref<CredentialRequestOptions | null>(null);
|
||||||
let hCaptchaResponse = $ref(null);
|
const hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
const reCaptchaResponse = ref(null);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'login', v: any): void;
|
(ev: 'login', v: any): void;
|
||||||
@ -98,11 +98,11 @@ const props = defineProps({
|
|||||||
|
|
||||||
function onUsernameChange(): void {
|
function onUsernameChange(): void {
|
||||||
os.api('users/show', {
|
os.api('users/show', {
|
||||||
username: username,
|
username: username.value,
|
||||||
}).then(userResponse => {
|
}).then(userResponse => {
|
||||||
user = userResponse;
|
user.value = userResponse;
|
||||||
}, () => {
|
}, () => {
|
||||||
user = null;
|
user.value = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,21 +113,21 @@ function onLogin(res: any): Promise<void> | void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function queryKey(): Promise<void> {
|
async function queryKey(): Promise<void> {
|
||||||
queryingKey = true;
|
queryingKey.value = true;
|
||||||
await webAuthnRequest(credentialRequest)
|
await webAuthnRequest(credentialRequest.value)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
queryingKey = false;
|
queryingKey.value = false;
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}).then(credential => {
|
}).then(credential => {
|
||||||
credentialRequest = null;
|
credentialRequest.value = null;
|
||||||
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,
|
||||||
credential: credential.toJSON(),
|
credential: credential.toJSON(),
|
||||||
'hcaptcha-response': hCaptchaResponse,
|
'hcaptcha-response': hCaptchaResponse.value,
|
||||||
'g-recaptcha-response': reCaptchaResponse,
|
'g-recaptcha-response': reCaptchaResponse.value,
|
||||||
});
|
});
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
emit('login', res);
|
emit('login', res);
|
||||||
@ -138,39 +138,39 @@ async function queryKey(): Promise<void> {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
text: i18n.ts.signinFailed,
|
text: i18n.ts.signinFailed,
|
||||||
});
|
});
|
||||||
signing = false;
|
signing.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit(): void {
|
function onSubmit(): void {
|
||||||
signing = true;
|
signing.value = true;
|
||||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
||||||
if (webAuthnSupported() && user.securityKeys) {
|
if (webAuthnSupported() && 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;
|
||||||
credentialRequest = parseRequestOptionsFromJSON({
|
credentialRequest.value = parseRequestOptionsFromJSON({
|
||||||
publicKey: res,
|
publicKey: res,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => queryKey())
|
.then(() => 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?.twoFactorEnabled ? token : undefined,
|
token: user.value?.twoFactorEnabled ? token.value : undefined,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
emit('login', res);
|
emit('login', res);
|
||||||
onLogin(res);
|
onLogin(res);
|
||||||
@ -218,8 +218,8 @@ function loginFailed(err: any): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totpLogin = false;
|
totpLogin.value = false;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPassword(): void {
|
function resetPassword(): void {
|
||||||
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef } from 'vue';
|
||||||
import MkSignin from '@/components/MkSignin.vue';
|
import MkSignin from '@/components/MkSignin.vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
@ -39,15 +39,15 @@ const emit = defineEmits<{
|
|||||||
(ev: 'cancelled'): void;
|
(ev: 'cancelled'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit('cancelled');
|
emit('cancelled');
|
||||||
if (dialog) dialog.close();
|
if (dialog.value) dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLogin(res) {
|
function onLogin(res) {
|
||||||
emit('done', res);
|
emit('done', res);
|
||||||
if (dialog) dialog.close();
|
if (dialog.value) dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { toUnicode } from 'punycode/';
|
import { toUnicode } from 'punycode/';
|
||||||
import MkButton from './MkButton.vue';
|
import MkButton from './MkButton.vue';
|
||||||
import MkInput from './MkInput.vue';
|
import MkInput from './MkInput.vue';
|
||||||
@ -101,34 +101,34 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const host = toUnicode(config.host);
|
const host = toUnicode(config.host);
|
||||||
|
|
||||||
let hcaptcha = $ref<Captcha | undefined>();
|
const hcaptcha = ref<Captcha | undefined>();
|
||||||
let recaptcha = $ref<Captcha | undefined>();
|
const recaptcha = ref<Captcha | undefined>();
|
||||||
let turnstile = $ref<Captcha | undefined>();
|
const turnstile = ref<Captcha | undefined>();
|
||||||
|
|
||||||
let username: string = $ref('');
|
const username = ref<string>('');
|
||||||
let password: string = $ref('');
|
const password = ref<string>('');
|
||||||
let retypedPassword: string = $ref('');
|
const retypedPassword = ref<string>('');
|
||||||
let invitationCode: string = $ref('');
|
const invitationCode = ref<string>('');
|
||||||
let email = $ref('');
|
const email = ref('');
|
||||||
let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null);
|
const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
|
||||||
let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null);
|
const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
|
||||||
let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
|
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
|
||||||
let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
|
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
|
||||||
let submitting: boolean = $ref(false);
|
const submitting = ref<boolean>(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
const hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
const reCaptchaResponse = ref(null);
|
||||||
let turnstileResponse = $ref(null);
|
const turnstileResponse = ref(null);
|
||||||
let usernameAbortController: null | AbortController = $ref(null);
|
const usernameAbortController = ref<null | AbortController>(null);
|
||||||
let emailAbortController: null | AbortController = $ref(null);
|
const emailAbortController = ref<null | AbortController>(null);
|
||||||
|
|
||||||
const shouldDisableSubmitting = $computed((): boolean => {
|
const shouldDisableSubmitting = computed((): boolean => {
|
||||||
return submitting ||
|
return submitting.value ||
|
||||||
instance.enableHcaptcha && !hCaptchaResponse ||
|
instance.enableHcaptcha && !hCaptchaResponse.value ||
|
||||||
instance.enableRecaptcha && !reCaptchaResponse ||
|
instance.enableRecaptcha && !reCaptchaResponse.value ||
|
||||||
instance.enableTurnstile && !turnstileResponse ||
|
instance.enableTurnstile && !turnstileResponse.value ||
|
||||||
instance.emailRequiredForSignup && emailState !== 'ok' ||
|
instance.emailRequiredForSignup && emailState.value !== 'ok' ||
|
||||||
usernameState !== 'ok' ||
|
usernameState.value !== 'ok' ||
|
||||||
passwordRetypeState !== 'match';
|
passwordRetypeState.value !== 'match';
|
||||||
});
|
});
|
||||||
|
|
||||||
function getPasswordStrength(source: string): number {
|
function getPasswordStrength(source: string): number {
|
||||||
@ -156,57 +156,57 @@ function getPasswordStrength(source: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onChangeUsername(): void {
|
function onChangeUsername(): void {
|
||||||
if (username === '') {
|
if (username.value === '') {
|
||||||
usernameState = null;
|
usernameState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const err =
|
const err =
|
||||||
!username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
|
!username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
|
||||||
username.length < 1 ? 'min-range' :
|
username.value.length < 1 ? 'min-range' :
|
||||||
username.length > 20 ? 'max-range' :
|
username.value.length > 20 ? 'max-range' :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
usernameState = err;
|
usernameState.value = err;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usernameAbortController != null) {
|
if (usernameAbortController.value != null) {
|
||||||
usernameAbortController.abort();
|
usernameAbortController.value.abort();
|
||||||
}
|
}
|
||||||
usernameState = 'wait';
|
usernameState.value = 'wait';
|
||||||
usernameAbortController = new AbortController();
|
usernameAbortController.value = new AbortController();
|
||||||
|
|
||||||
os.api('username/available', {
|
os.api('username/available', {
|
||||||
username,
|
username: username.value,
|
||||||
}, undefined, usernameAbortController.signal).then(result => {
|
}, undefined, usernameAbortController.value.signal).then(result => {
|
||||||
usernameState = result.available ? 'ok' : 'unavailable';
|
usernameState.value = result.available ? 'ok' : 'unavailable';
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (err.name !== 'AbortError') {
|
if (err.name !== 'AbortError') {
|
||||||
usernameState = 'error';
|
usernameState.value = 'error';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeEmail(): void {
|
function onChangeEmail(): void {
|
||||||
if (email === '') {
|
if (email.value === '') {
|
||||||
emailState = null;
|
emailState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emailAbortController != null) {
|
if (emailAbortController.value != null) {
|
||||||
emailAbortController.abort();
|
emailAbortController.value.abort();
|
||||||
}
|
}
|
||||||
emailState = 'wait';
|
emailState.value = 'wait';
|
||||||
emailAbortController = new AbortController();
|
emailAbortController.value = new AbortController();
|
||||||
|
|
||||||
os.api('email-address/available', {
|
os.api('email-address/available', {
|
||||||
emailAddress: email,
|
emailAddress: email.value,
|
||||||
}, undefined, emailAbortController.signal).then(result => {
|
}, undefined, emailAbortController.value.signal).then(result => {
|
||||||
emailState = result.available ? 'ok' :
|
emailState.value = result.available ? 'ok' :
|
||||||
result.reason === 'used' ? 'unavailable:used' :
|
result.reason === 'used' ? 'unavailable:used' :
|
||||||
result.reason === 'format' ? 'unavailable:format' :
|
result.reason === 'format' ? 'unavailable:format' :
|
||||||
result.reason === 'disposable' ? 'unavailable:disposable' :
|
result.reason === 'disposable' ? 'unavailable:disposable' :
|
||||||
@ -215,55 +215,55 @@ function onChangeEmail(): void {
|
|||||||
'unavailable';
|
'unavailable';
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (err.name !== 'AbortError') {
|
if (err.name !== 'AbortError') {
|
||||||
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 = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
|
passwordStrength.value = 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';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmit(): Promise<void> {
|
async function onSubmit(): Promise<void> {
|
||||||
if (submitting) return;
|
if (submitting.value) return;
|
||||||
submitting = true;
|
submitting.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await os.api('signup', {
|
await 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,
|
||||||
'turnstile-response': turnstileResponse,
|
'turnstile-response': turnstileResponse.value,
|
||||||
});
|
});
|
||||||
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 {
|
||||||
const res = await os.api('signin', {
|
const res = await os.api('signin', {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
});
|
});
|
||||||
emit('signup', res);
|
emit('signup', res);
|
||||||
|
|
||||||
@ -272,10 +272,10 @@ async function onSubmit(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
submitting = false;
|
submitting.value = false;
|
||||||
hcaptcha?.reset?.();
|
hcaptcha.value?.reset?.();
|
||||||
recaptcha?.reset?.();
|
recaptcha.value?.reset?.();
|
||||||
turnstile?.reset?.();
|
turnstile.value?.reset?.();
|
||||||
|
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
@ -33,8 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef, ref } from 'vue';
|
||||||
import { $ref } from 'vue/macros';
|
|
||||||
import XSignup from '@/components/MkSignupDialog.form.vue';
|
import XSignup from '@/components/MkSignupDialog.form.vue';
|
||||||
import XServerRules from '@/components/MkSignupDialog.rules.vue';
|
import XServerRules from '@/components/MkSignupDialog.rules.vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
@ -52,17 +52,17 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
|
|
||||||
const isAcceptedServerRule = $ref(false);
|
const isAcceptedServerRule = ref(false);
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkMediaList from '@/components/MkMediaList.vue';
|
import MkMediaList from '@/components/MkMediaList.vue';
|
||||||
import MkPoll from '@/components/MkPoll.vue';
|
import MkPoll from '@/components/MkPoll.vue';
|
||||||
@ -44,7 +44,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const isLong = shouldCollapsed(props.note, []);
|
const isLong = shouldCollapsed(props.note, []);
|
||||||
|
|
||||||
const collapsed = $ref(isLong);
|
const collapsed = ref(isLong);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, watch, onBeforeUnmount } from 'vue';
|
import { onMounted, watch, onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
const loaded = !!window.TagCanvas;
|
const loaded = !!window.TagCanvas;
|
||||||
@ -23,13 +23,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
|
|||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||||
const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||||
let available = $ref(false);
|
const available = ref(false);
|
||||||
let rootEl = $shallowRef<HTMLElement | null>(null);
|
const rootEl = shallowRef<HTMLElement | null>(null);
|
||||||
let canvasEl = $shallowRef<HTMLCanvasElement | null>(null);
|
const canvasEl = shallowRef<HTMLCanvasElement | null>(null);
|
||||||
let tagsEl = $shallowRef<HTMLElement | null>(null);
|
const tagsEl = shallowRef<HTMLElement | null>(null);
|
||||||
let width = $ref(300);
|
const width = ref(300);
|
||||||
|
|
||||||
watch($$(available), () => {
|
watch(available, () => {
|
||||||
try {
|
try {
|
||||||
window.TagCanvas.Start(idForCanvas, idForTags, {
|
window.TagCanvas.Start(idForCanvas, idForTags, {
|
||||||
textColour: '#ffffff',
|
textColour: '#ffffff',
|
||||||
@ -52,15 +52,15 @@ 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.appendChild(Object.assign(document.createElement('script'), {
|
document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||||
async: true,
|
async: true,
|
||||||
src: '/client-assets/tagcanvas.min.js',
|
src: '/client-assets/tagcanvas.min.js',
|
||||||
})).addEventListener('load', () => available = true);
|
})).addEventListener('load', () => available.value = true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, onUnmounted, provide } from 'vue';
|
import { computed, watch, onUnmounted, provide, ref } from 'vue';
|
||||||
import { Connection } from 'misskey-js/built/streaming.js';
|
import { Connection } from 'misskey-js/built/streaming.js';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||||
@ -62,8 +62,8 @@ type TimelineQueryType = {
|
|||||||
roleId?: string
|
roleId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const prComponent: InstanceType<typeof MkPullToRefresh> = $ref();
|
const prComponent = ref<InstanceType<typeof MkPullToRefresh>>();
|
||||||
const tlComponent: InstanceType<typeof MkNotes> = $ref();
|
const tlComponent = ref<InstanceType<typeof MkNotes>>();
|
||||||
|
|
||||||
let tlNotesCount = 0;
|
let tlNotesCount = 0;
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ const prepend = note => {
|
|||||||
note._shouldInsertAd_ = true;
|
note._shouldInsertAd_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tlComponent.pagingComponent?.prepend(note);
|
tlComponent.value.pagingComponent?.prepend(note);
|
||||||
|
|
||||||
emit('note');
|
emit('note');
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ function reloadTimeline() {
|
|||||||
return new Promise<void>((res) => {
|
return new Promise<void>((res) => {
|
||||||
tlNotesCount = 0;
|
tlNotesCount = 0;
|
||||||
|
|
||||||
tlComponent.pagingComponent?.reload().then(() => {
|
tlComponent.value.pagingComponent?.reload().then(() => {
|
||||||
res();
|
res();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
@ -35,11 +35,11 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex('high');
|
const zIndex = os.claimZIndex('high');
|
||||||
let showing = $ref(true);
|
const showing = ref(true);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}, 4000);
|
}, 4000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { shallowRef, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkInput from './MkInput.vue';
|
import MkInput from './MkInput.vue';
|
||||||
import MkSwitch from './MkSwitch.vue';
|
import MkSwitch from './MkSwitch.vue';
|
||||||
@ -67,37 +67,37 @@ const emit = defineEmits<{
|
|||||||
(ev: 'done', result: { name: string | null, permissions: string[] }): void;
|
(ev: 'done', result: { name: string | null, permissions: string[] }): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
let name = $ref(props.initialName);
|
const name = ref(props.initialName);
|
||||||
let permissions = $ref({});
|
const 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 Misskey.permissions) {
|
for (const kind of Misskey.permissions) {
|
||||||
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>
|
||||||
|
@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, onUnmounted } from 'vue';
|
import { defineAsyncComponent, onUnmounted, ref } from 'vue';
|
||||||
import type { summaly } from 'summaly';
|
import type { summaly } from 'summaly';
|
||||||
import { url as local } from '@/config.js';
|
import { url as local } from '@/config.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
@ -107,36 +107,36 @@ const props = withDefaults(defineProps<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
const MOBILE_THRESHOLD = 500;
|
const MOBILE_THRESHOLD = 500;
|
||||||
const isMobile = $ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
||||||
|
|
||||||
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);
|
const fetching = ref(true);
|
||||||
let title = $ref<string | null>(null);
|
const title = ref<string | null>(null);
|
||||||
let description = $ref<string | null>(null);
|
const description = ref<string | null>(null);
|
||||||
let thumbnail = $ref<string | null>(null);
|
const thumbnail = ref<string | null>(null);
|
||||||
let icon = $ref<string | null>(null);
|
const icon = ref<string | null>(null);
|
||||||
let sitename = $ref<string | null>(null);
|
const sitename = ref<string | null>(null);
|
||||||
let sensitive = $ref<boolean>(false);
|
const sensitive = ref<boolean>(false);
|
||||||
let player = $ref({
|
const player = ref({
|
||||||
url: null,
|
url: null,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
} as SummalyResult['player']);
|
} as SummalyResult['player']);
|
||||||
let playerEnabled = $ref(false);
|
const playerEnabled = ref(false);
|
||||||
let tweetId = $ref<string | null>(null);
|
const tweetId = ref<string | null>(null);
|
||||||
let tweetExpanded = $ref(props.detail);
|
const tweetExpanded = ref(props.detail);
|
||||||
const embedId = `embed${Math.random().toString().replace(/\D/, '')}`;
|
const embedId = `embed${Math.random().toString().replace(/\D/, '')}`;
|
||||||
let tweetHeight = $ref(150);
|
const tweetHeight = ref(150);
|
||||||
let unknownUrl = $ref(false);
|
const unknownUrl = ref(false);
|
||||||
|
|
||||||
const requestUrl = new URL(props.url);
|
const requestUrl = new URL(props.url);
|
||||||
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
|
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
|
||||||
|
|
||||||
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com' || requestUrl.hostname === 'x.com' || requestUrl.hostname === 'mobile.x.com') {
|
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com' || requestUrl.hostname === 'x.com' || requestUrl.hostname === 'mobile.x.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 (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) {
|
if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) {
|
||||||
@ -148,8 +148,8 @@ requestUrl.hash = '';
|
|||||||
window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`)
|
window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
unknownUrl = true;
|
unknownUrl.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,21 +157,21 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa
|
|||||||
})
|
})
|
||||||
.then((info: SummalyResult) => {
|
.then((info: SummalyResult) => {
|
||||||
if (info.url == null) {
|
if (info.url == null) {
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
unknownUrl = true;
|
unknownUrl.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
unknownUrl = false;
|
unknownUrl.value = false;
|
||||||
|
|
||||||
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;
|
||||||
player = info.player;
|
player.value = info.player;
|
||||||
sensitive = info.sensitive ?? false;
|
sensitive.value = info.sensitive ?? false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function adjustTweetHeight(message: any) {
|
function adjustTweetHeight(message: any) {
|
||||||
@ -180,7 +180,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
const openPlayer = (): void => {
|
const openPlayer = (): void => {
|
||||||
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -28,16 +28,16 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex('middle');
|
const zIndex = os.claimZIndex('middle');
|
||||||
let top = $ref(0);
|
const top = ref(0);
|
||||||
let left = $ref(0);
|
const left = ref(0);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const rect = props.source.getBoundingClientRect();
|
const rect = props.source.getBoundingClientRect();
|
||||||
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
|
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + 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>
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
@ -66,12 +66,12 @@ const props = defineProps<{
|
|||||||
announcement?: any,
|
announcement?: any,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog = $ref(null);
|
const dialog = ref(null);
|
||||||
let title: string = $ref(props.announcement ? props.announcement.title : '');
|
const title = ref<string>(props.announcement ? props.announcement.title : '');
|
||||||
let text: string = $ref(props.announcement ? props.announcement.text : '');
|
const text = ref<string>(props.announcement ? props.announcement.text : '');
|
||||||
let icon: string = $ref(props.announcement ? props.announcement.icon : 'info');
|
const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
|
||||||
let display: string = $ref(props.announcement ? props.announcement.display : 'dialog');
|
const display = ref<string>(props.announcement ? props.announcement.display : 'dialog');
|
||||||
let needConfirmationToRead = $ref(props.announcement ? props.announcement.needConfirmationToRead : false);
|
const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
|
(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
|
||||||
@ -80,12 +80,12 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
async function done() {
|
async function done() {
|
||||||
const params = {
|
const params = {
|
||||||
title: title,
|
title: title.value,
|
||||||
text: text,
|
text: text.value,
|
||||||
icon: icon,
|
icon: icon.value,
|
||||||
imageUrl: null,
|
imageUrl: null,
|
||||||
display: display,
|
display: display.value,
|
||||||
needConfirmationToRead: needConfirmationToRead,
|
needConfirmationToRead: needConfirmationToRead.value,
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ async function done() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
} else {
|
} else {
|
||||||
const created = await os.apiWithDialog('admin/announcements/create', params);
|
const created = await os.apiWithDialog('admin/announcements/create', params);
|
||||||
|
|
||||||
@ -110,14 +110,14 @@ async function done() {
|
|||||||
created: created,
|
created: created,
|
||||||
});
|
});
|
||||||
|
|
||||||
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: title }),
|
text: i18n.t('removeAreYouSure', { x: title.value }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ async function del() {
|
|||||||
emit('done', {
|
emit('done', {
|
||||||
deleted: true,
|
deleted: true,
|
||||||
});
|
});
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { acct } from '@/filters/user.js';
|
import { acct } from '@/filters/user.js';
|
||||||
@ -28,14 +28,14 @@ const props = withDefaults(defineProps<{
|
|||||||
withChart: true,
|
withChart: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let chartValues = $ref<number[] | null>(null);
|
const chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.withChart) {
|
if (props.withChart) {
|
||||||
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
|
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
|
||||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||||
res.inc.splice(0, 1);
|
res.inc.splice(0, 1);
|
||||||
chartValues = res.inc;
|
chartValues.value = res.inc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
@ -24,7 +24,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': return i18n.ts.online;
|
case 'online': return i18n.ts.online;
|
||||||
case 'active': return i18n.ts.active;
|
case 'active': return i18n.ts.active;
|
||||||
|
@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
@ -80,18 +80,18 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex('middle');
|
const zIndex = os.claimZIndex('middle');
|
||||||
let user = $ref<Misskey.entities.UserDetailed | null>(null);
|
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
let top = $ref(0);
|
const top = ref(0);
|
||||||
let left = $ref(0);
|
const left = ref(0);
|
||||||
|
|
||||||
function showMenu(ev: MouseEvent) {
|
function showMenu(ev: MouseEvent) {
|
||||||
const { menu, cleanup } = getUserMenu(user);
|
const { menu, cleanup } = getUserMenu(user.value);
|
||||||
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
|
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
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('@') ?
|
||||||
Misskey.acct.parse(props.q.substring(1)) :
|
Misskey.acct.parse(props.q.substring(1)) :
|
||||||
@ -99,7 +99,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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +107,8 @@ onMounted(() => {
|
|||||||
const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
|
const x = ((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>
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
@ -78,43 +78,43 @@ const props = defineProps<{
|
|||||||
includeSelf?: boolean;
|
includeSelf?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let username = $ref('');
|
const username = ref('');
|
||||||
let host = $ref('');
|
const host = ref('');
|
||||||
let users: Misskey.entities.UserDetailed[] = $ref([]);
|
const users = ref<Misskey.entities.UserDetailed[]>([]);
|
||||||
let recentUsers: Misskey.entities.UserDetailed[] = $ref([]);
|
const recentUsers = ref<Misskey.entities.UserDetailed[]>([]);
|
||||||
let selected: Misskey.entities.UserDetailed | null = $ref(null);
|
const selected = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
let dialogEl = $ref();
|
const 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(() => {
|
||||||
@ -122,9 +122,9 @@ onMounted(() => {
|
|||||||
userIds: defaultStore.state.recentlyUsedUsers,
|
userIds: defaultStore.state.recentlyUsedUsers,
|
||||||
}).then(users => {
|
}).then(users => {
|
||||||
if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
|
if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
|
||||||
recentUsers = [$i, ...users];
|
recentUsers.value = [$i, ...users];
|
||||||
} else {
|
} else {
|
||||||
recentUsers = users;
|
recentUsers.value = users;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -53,10 +53,10 @@ import MkFolder from '@/components/MkFolder.vue';
|
|||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
|
|
||||||
let isLocked = ref(false);
|
const isLocked = ref(false);
|
||||||
let hideOnlineStatus = ref(false);
|
const hideOnlineStatus = ref(false);
|
||||||
let noCrawle = ref(false);
|
const noCrawle = ref(false);
|
||||||
let preventAiLearning = ref(true);
|
const preventAiLearning = ref(true);
|
||||||
|
|
||||||
watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => {
|
watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => {
|
||||||
os.api('i/update', {
|
os.api('i/update', {
|
||||||
|
@ -42,12 +42,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick } from 'vue';
|
import { nextTick, shallowRef, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
currentVisibility: typeof Misskey.noteVisibilities[number];
|
currentVisibility: typeof Misskey.noteVisibilities[number];
|
||||||
@ -62,13 +62,13 @@ const emit = defineEmits<{
|
|||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let v = $ref(props.currentVisibility);
|
const v = ref(props.currentVisibility);
|
||||||
|
|
||||||
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(() => {
|
||||||
if (modal) modal.close();
|
if (modal.value) modal.value.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, shallowRef, ref } 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';
|
||||||
@ -25,11 +25,11 @@ import { initChart } from '@/scripts/init-chart.js';
|
|||||||
|
|
||||||
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 = 30;
|
const chartLimit = 30;
|
||||||
let fetching = $ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
@ -65,7 +65,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: [{
|
||||||
@ -147,7 +147,7 @@ async function renderChart() {
|
|||||||
plugins: [chartVLine(vLineColor)],
|
plugins: [chartVLine(vLineColor)],
|
||||||
});
|
});
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import XTimeline from './welcome.timeline.vue';
|
import XTimeline from './welcome.timeline.vue';
|
||||||
import XSigninDialog from '@/components/MkSigninDialog.vue';
|
import XSigninDialog from '@/components/MkSigninDialog.vue';
|
||||||
@ -67,15 +67,15 @@ import number from '@/filters/number.js';
|
|||||||
import MkNumber from '@/components/MkNumber.vue';
|
import MkNumber from '@/components/MkNumber.vue';
|
||||||
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
|
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
|
||||||
|
|
||||||
let meta = $ref<Misskey.entities.MetaResponse | null>(null);
|
const meta = ref<Misskey.entities.MetaResponse | null>(null);
|
||||||
let stats = $ref<Misskey.entities.StatsResponse | null>(null);
|
const stats = ref<Misskey.entities.StatsResponse | null>(null);
|
||||||
|
|
||||||
os.api('meta', { detail: true }).then(_meta => {
|
os.api('meta', { detail: true }).then(_meta => {
|
||||||
meta = _meta;
|
meta.value = _meta;
|
||||||
});
|
});
|
||||||
|
|
||||||
os.api('stats', {}).then((res) => {
|
os.api('stats', {}).then((res) => {
|
||||||
stats = res;
|
stats.value = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
function signin() {
|
function signin() {
|
||||||
|
@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted, provide } from 'vue';
|
import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
|
||||||
import contains from '@/scripts/contains.js';
|
import contains from '@/scripts/contains.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { MenuItem } from '@/types/menu';
|
import { MenuItem } from '@/types/menu';
|
||||||
@ -107,18 +107,18 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
provide('inWindow', true);
|
provide('inWindow', true);
|
||||||
|
|
||||||
let rootEl = $shallowRef<HTMLElement | null>();
|
const rootEl = shallowRef<HTMLElement | null>();
|
||||||
let showing = $ref(true);
|
const showing = ref(true);
|
||||||
let beforeClickedAt = 0;
|
let beforeClickedAt = 0;
|
||||||
let maximized = $ref(false);
|
const maximized = ref(false);
|
||||||
let minimized = $ref(false);
|
const minimized = ref(false);
|
||||||
let unResizedTop = '';
|
let unResizedTop = '';
|
||||||
let unResizedLeft = '';
|
let unResizedLeft = '';
|
||||||
let unResizedWidth = '';
|
let unResizedWidth = '';
|
||||||
let unResizedHeight = '';
|
let unResizedHeight = '';
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(evt) {
|
function onKeydown(evt) {
|
||||||
@ -137,46 +137,46 @@ 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;
|
||||||
unResizedTop = rootEl.style.top;
|
unResizedTop = rootEl.value.style.top;
|
||||||
unResizedLeft = rootEl.style.left;
|
unResizedLeft = rootEl.value.style.left;
|
||||||
unResizedWidth = rootEl.style.width;
|
unResizedWidth = rootEl.value.style.width;
|
||||||
unResizedHeight = rootEl.style.height;
|
unResizedHeight = 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 = unResizedTop;
|
rootEl.value.style.top = unResizedTop;
|
||||||
rootEl.style.left = unResizedLeft;
|
rootEl.value.style.left = unResizedLeft;
|
||||||
rootEl.style.width = unResizedWidth;
|
rootEl.value.style.width = unResizedWidth;
|
||||||
rootEl.style.height = unResizedHeight;
|
rootEl.value.style.height = unResizedHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function minimize() {
|
function minimize() {
|
||||||
minimized = true;
|
minimized.value = true;
|
||||||
unResizedWidth = rootEl.style.width;
|
unResizedWidth = rootEl.value.style.width;
|
||||||
unResizedHeight = rootEl.style.height;
|
unResizedHeight = rootEl.value.style.height;
|
||||||
rootEl.style.width = minWidth + 'px';
|
rootEl.value.style.width = minWidth + 'px';
|
||||||
rootEl.style.height = props.mini ? '32px' : '39px';
|
rootEl.value.style.height = props.mini ? '32px' : '39px';
|
||||||
}
|
}
|
||||||
|
|
||||||
function unMinimize() {
|
function unMinimize() {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
minimized = false;
|
minimized.value = false;
|
||||||
rootEl.style.width = unResizedWidth;
|
rootEl.value.style.width = unResizedWidth;
|
||||||
rootEl.style.height = unResizedHeight;
|
rootEl.value.style.height = unResizedHeight;
|
||||||
const browserWidth = window.innerWidth;
|
const browserWidth = window.innerWidth;
|
||||||
const browserHeight = window.innerHeight;
|
const browserHeight = window.innerHeight;
|
||||||
const windowWidth = main.offsetWidth;
|
const windowWidth = main.offsetWidth;
|
||||||
@ -192,7 +192,7 @@ function onBodyMousedown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onDblClick() {
|
function onDblClick() {
|
||||||
if (minimized) {
|
if (minimized.value) {
|
||||||
unMinimize();
|
unMinimize();
|
||||||
} else {
|
} else {
|
||||||
maximize();
|
maximize();
|
||||||
@ -205,7 +205,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
|||||||
|
|
||||||
let beforeMaximized = false;
|
let beforeMaximized = false;
|
||||||
|
|
||||||
if (maximized) {
|
if (maximized.value) {
|
||||||
beforeMaximized = true;
|
beforeMaximized = true;
|
||||||
unMaximize();
|
unMaximize();
|
||||||
}
|
}
|
||||||
@ -219,7 +219,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();
|
||||||
@ -251,8 +251,8 @@ function onHeaderMousedown(evt: MouseEvent) {
|
|||||||
// 右はみ出し
|
// 右はみ出し
|
||||||
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
|
if (moveLeft + windowWidth > browserWidth) 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) {
|
||||||
@ -270,7 +270,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
|||||||
|
|
||||||
// 上ハンドル掴み時
|
// 上ハンドル掴み時
|
||||||
function onTopHandleMousedown(evt) {
|
function onTopHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
// どういうわけかnullになることがある
|
// どういうわけかnullになることがある
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ function onTopHandleMousedown(evt) {
|
|||||||
|
|
||||||
// 右ハンドル掴み時
|
// 右ハンドル掴み時
|
||||||
function onRightHandleMousedown(evt) {
|
function onRightHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
const base = evt.clientX;
|
const base = evt.clientX;
|
||||||
@ -323,7 +323,7 @@ function onRightHandleMousedown(evt) {
|
|||||||
|
|
||||||
// 下ハンドル掴み時
|
// 下ハンドル掴み時
|
||||||
function onBottomHandleMousedown(evt) {
|
function onBottomHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
const base = evt.clientY;
|
const base = evt.clientY;
|
||||||
@ -348,7 +348,7 @@ function onBottomHandleMousedown(evt) {
|
|||||||
|
|
||||||
// 左ハンドル掴み時
|
// 左ハンドル掴み時
|
||||||
function onLeftHandleMousedown(evt) {
|
function onLeftHandleMousedown(evt) {
|
||||||
const main = rootEl;
|
const main = rootEl.value;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
const base = evt.clientX;
|
const base = evt.clientX;
|
||||||
@ -400,27 +400,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;
|
||||||
if (main == null) return;
|
if (main == null) return;
|
||||||
|
|
||||||
const position = main.getBoundingClientRect();
|
const position = main.getBoundingClientRect();
|
||||||
@ -438,8 +438,8 @@ onMounted(() => {
|
|||||||
applyTransformWidth(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,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import MkWindow from '@/components/MkWindow.vue';
|
import MkWindow from '@/components/MkWindow.vue';
|
||||||
import { versatileLang } from '@/scripts/intl-const.js';
|
import { versatileLang } from '@/scripts/intl-const.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@ -35,22 +36,22 @@ const props = defineProps<{
|
|||||||
const requestUrl = new URL(props.url);
|
const requestUrl = new URL(props.url);
|
||||||
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
|
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
|
||||||
|
|
||||||
let fetching = $ref(true);
|
const fetching = ref(true);
|
||||||
let title = $ref<string | null>(null);
|
const title = ref<string | null>(null);
|
||||||
let player = $ref({
|
const player = ref({
|
||||||
url: null,
|
url: null,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ytFetch = (): void => {
|
const ytFetch = (): void => {
|
||||||
fetching = true;
|
fetching.value = true;
|
||||||
window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => {
|
window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).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;
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
player = info.player;
|
player.value = info.player;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { url } from '@/config.js';
|
import { url } from '@/config.js';
|
||||||
@ -28,7 +29,7 @@ const props = withDefaults(defineProps<{
|
|||||||
|
|
||||||
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;
|
||||||
|
@ -96,7 +96,7 @@ const choseAd = (): Ad | null => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const chosen = ref(choseAd());
|
const chosen = ref(choseAd());
|
||||||
const shouldHide = $ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null));
|
const shouldHide = ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null));
|
||||||
|
|
||||||
function reduceFrequency(): void {
|
function reduceFrequency(): void {
|
||||||
if (chosen.value == null) return;
|
if (chosen.value == null) return;
|
||||||
|
@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
|
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
|
||||||
import MkA from './MkA.vue';
|
import MkA from './MkA.vue';
|
||||||
@ -47,9 +47,9 @@ import { acct, userPage } from '@/filters/user.js';
|
|||||||
import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
|
import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const animation = $ref(defaultStore.state.animation);
|
const animation = ref(defaultStore.state.animation);
|
||||||
const squareAvatars = $ref(defaultStore.state.squareAvatars);
|
const squareAvatars = ref(defaultStore.state.squareAvatars);
|
||||||
const useBlurEffect = $ref(defaultStore.state.useBlurEffect);
|
const useBlurEffect = ref(defaultStore.state.useBlurEffect);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
user: Misskey.entities.User;
|
user: Misskey.entities.User;
|
||||||
@ -79,11 +79,11 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations;
|
const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations;
|
||||||
|
|
||||||
const bound = $computed(() => props.link
|
const bound = computed(() => props.link
|
||||||
? { to: userPage(props.user), target: props.target }
|
? { to: userPage(props.user), target: props.target }
|
||||||
: {});
|
: {});
|
||||||
|
|
||||||
const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
|
const url = computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
|
||||||
? getStaticImageUrl(props.user.avatarUrl)
|
? getStaticImageUrl(props.user.avatarUrl)
|
||||||
: props.user.avatarUrl);
|
: props.user.avatarUrl);
|
||||||
|
|
||||||
@ -116,10 +116,10 @@ function getDecorationScale() {
|
|||||||
return scaleX === 1 ? undefined : `${scaleX} 1`;
|
return scaleX === 1 ? undefined : `${scaleX} 1`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = $ref<string | undefined>();
|
const color = ref<string | undefined>();
|
||||||
|
|
||||||
watch(() => props.user.avatarBlurhash, () => {
|
watch(() => props.user.avatarBlurhash, () => {
|
||||||
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject } from 'vue';
|
import { computed, inject, ref } from 'vue';
|
||||||
import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js';
|
import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { customEmojisMap } from '@/custom-emojis.js';
|
import { customEmojisMap } from '@/custom-emojis.js';
|
||||||
@ -71,7 +71,7 @@ const url = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const alt = computed(() => `:${customEmojiName.value}:`);
|
const alt = computed(() => `:${customEmojiName.value}:`);
|
||||||
let errored = $ref(url.value == null);
|
const errored = ref(url.value == null);
|
||||||
|
|
||||||
function onClick(ev: MouseEvent) {
|
function onClick(ev: MouseEvent) {
|
||||||
if (props.menu) {
|
if (props.menu) {
|
||||||
|
@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref, inject } from 'vue';
|
import { onMounted, onUnmounted, ref, inject, shallowRef, computed } from 'vue';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
|
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
|
||||||
import { scrollToTop } from '@/scripts/scroll.js';
|
import { scrollToTop } from '@/scripts/scroll.js';
|
||||||
@ -69,13 +69,13 @@ 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);
|
||||||
|
|
||||||
let el = $shallowRef<HTMLElement | undefined>(undefined);
|
const el = shallowRef<HTMLElement | undefined>(undefined);
|
||||||
const bg = ref<string | undefined>(undefined);
|
const bg = ref<string | undefined>(undefined);
|
||||||
let narrow = $ref(false);
|
const narrow = ref(false);
|
||||||
const hasTabs = $computed(() => props.tabs.length > 0);
|
const hasTabs = computed(() => 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 preventDrag = (ev: TouchEvent) => {
|
const preventDrag = (ev: TouchEvent) => {
|
||||||
@ -83,8 +83,8 @@ const preventDrag = (ev: TouchEvent) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const top = () => {
|
const top = () => {
|
||||||
if (el) {
|
if (el.value) {
|
||||||
scrollToTop(el as HTMLElement, { behavior: 'smooth' });
|
scrollToTop(el.value as HTMLElement, { behavior: 'smooth' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,14 +111,14 @@ onMounted(() => {
|
|||||||
calcBg();
|
calcBg();
|
||||||
globalEvents.on('themeChanged', calcBg);
|
globalEvents.on('themeChanged', calcBg);
|
||||||
|
|
||||||
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 && el.parentElement && document.body.contains(el as HTMLElement)) {
|
if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) {
|
||||||
narrow = el.parentElement.offsetWidth < 500;
|
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ro.observe(el.parentElement as HTMLElement);
|
ro.observe(el.value.parentElement as HTMLElement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,36 +18,36 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue';
|
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
|
||||||
import { $$ } from 'vue/macros';
|
|
||||||
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
|
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
|
||||||
|
|
||||||
const rootEl = $shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
const headerEl = $shallowRef<HTMLElement>();
|
const headerEl = shallowRef<HTMLElement>();
|
||||||
const footerEl = $shallowRef<HTMLElement>();
|
const footerEl = shallowRef<HTMLElement>();
|
||||||
const bodyEl = $shallowRef<HTMLElement>();
|
const bodyEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
let headerHeight = $ref<string | undefined>();
|
const headerHeight = ref<string | undefined>();
|
||||||
let childStickyTop = $ref(0);
|
const 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);
|
||||||
|
|
||||||
let footerHeight = $ref<string | undefined>();
|
const footerHeight = ref<string | undefined>();
|
||||||
let childStickyBottom = $ref(0);
|
const childStickyBottom = ref(0);
|
||||||
const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
|
const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
|
||||||
provide(CURRENT_STICKY_BOTTOM, $$(childStickyBottom));
|
provide(CURRENT_STICKY_BOTTOM, childStickyBottom);
|
||||||
|
|
||||||
const calc = () => {
|
const calc = () => {
|
||||||
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
||||||
if (headerEl != null) {
|
if (headerEl.value != null) {
|
||||||
childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
|
childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
|
||||||
headerHeight = headerEl.offsetHeight.toString();
|
headerHeight.value = headerEl.value.offsetHeight.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
||||||
if (footerEl != null) {
|
if (footerEl.value != null) {
|
||||||
childStickyBottom = parentStickyBottom.value + footerEl.offsetHeight;
|
childStickyBottom.value = parentStickyBottom.value + footerEl.value.offsetHeight;
|
||||||
footerHeight = footerEl.offsetHeight.toString();
|
footerHeight.value = footerEl.value.offsetHeight.toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,28 +62,28 @@ onMounted(() => {
|
|||||||
|
|
||||||
watch([parentStickyTop, parentStickyBottom], calc);
|
watch([parentStickyTop, parentStickyBottom], calc);
|
||||||
|
|
||||||
watch($$(childStickyTop), () => {
|
watch(childStickyTop, () => {
|
||||||
bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`);
|
bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
watch($$(childStickyBottom), () => {
|
watch(childStickyBottom, () => {
|
||||||
bodyEl.style.setProperty('--stickyBottom', `${childStickyBottom}px`);
|
bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`);
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
headerEl.style.position = 'sticky';
|
headerEl.value.style.position = 'sticky';
|
||||||
headerEl.style.top = 'var(--stickyTop, 0)';
|
headerEl.value.style.top = 'var(--stickyTop, 0)';
|
||||||
headerEl.style.zIndex = '1000';
|
headerEl.value.style.zIndex = '1000';
|
||||||
|
|
||||||
footerEl.style.position = 'sticky';
|
footerEl.value.style.position = 'sticky';
|
||||||
footerEl.style.bottom = 'var(--stickyBottom, 0)';
|
footerEl.value.style.bottom = 'var(--stickyBottom, 0)';
|
||||||
footerEl.style.zIndex = '1000';
|
footerEl.value.style.zIndex = '1000';
|
||||||
|
|
||||||
observer.observe(headerEl);
|
observer.observe(headerEl.value);
|
||||||
observer.observe(footerEl);
|
observer.observe(footerEl.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -91,6 +91,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
rootEl: $$(rootEl),
|
rootEl: rootEl,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import isChromatic from 'chromatic/isChromatic';
|
import isChromatic from 'chromatic/isChromatic';
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, ref, computed } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { dateTimeFormat } from '@/scripts/intl-const.js';
|
import { dateTimeFormat } from '@/scripts/intl-const.js';
|
||||||
|
|
||||||
@ -47,29 +47,29 @@ 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;
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||||
let now = $ref((props.origin ?? new Date()).getTime());
|
const now = ref((props.origin ?? new Date()).getTime());
|
||||||
const ago = $computed(() => (now - _time) / 1000/*ms*/);
|
const ago = computed(() => (now.value - _time) / 1000/*ms*/);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
|
ago.value >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago.value / 31536000).toString() }) :
|
||||||
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
|
ago.value >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago.value / 2592000).toString() }) :
|
||||||
ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) :
|
ago.value >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago.value / 604800).toString() }) :
|
||||||
ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) :
|
ago.value >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago.value / 86400).toString() }) :
|
||||||
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
|
ago.value >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago.value / 3600).toString() }) :
|
||||||
ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
|
ago.value >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago.value / 60)).toString() }) :
|
||||||
ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
|
ago.value >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago.value % 60)).toString() }) :
|
||||||
ago >= -3 ? i18n.ts._ago.justNow :
|
ago.value >= -3 ? i18n.ts._ago.justNow :
|
||||||
ago < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago / 31536000).toString() }) :
|
ago.value < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago.value / 31536000).toString() }) :
|
||||||
ago < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago / 2592000).toString() }) :
|
ago.value < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago.value / 2592000).toString() }) :
|
||||||
ago < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago / 604800).toString() }) :
|
ago.value < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago.value / 604800).toString() }) :
|
||||||
ago < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago / 86400).toString() }) :
|
ago.value < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago.value / 86400).toString() }) :
|
||||||
ago < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago / 3600).toString() }) :
|
ago.value < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago.value / 3600).toString() }) :
|
||||||
ago < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago / 60)).toString() }) :
|
ago.value < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago.value / 60)).toString() }) :
|
||||||
i18n.t('_timeIn.seconds', { n: (~~(-ago % 60)).toString() })
|
i18n.t('_timeIn.seconds', { n: (~~(-ago.value % 60)).toString() })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ let tickId: number;
|
|||||||
let currentInterval: number;
|
let currentInterval: number;
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
now = (new Date()).getTime();
|
now.value = (new Date()).getTime();
|
||||||
const nextInterval = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
|
const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000;
|
||||||
|
|
||||||
if (currentInterval !== nextInterval) {
|
if (currentInterval !== nextInterval) {
|
||||||
if (tickId) window.clearInterval(tickId);
|
if (tickId) window.clearInterval(tickId);
|
||||||
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</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 { Resolved, Router } from '@/nirax';
|
import { Resolved, Router } from '@/nirax';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
@ -46,16 +46,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const current = resolveNested(router.current)!;
|
const current = resolveNested(router.current)!;
|
||||||
let currentPageComponent = $shallowRef(current.route.component);
|
const currentPageComponent = shallowRef(current.route.component);
|
||||||
let currentPageProps = $ref(current.props);
|
const currentPageProps = ref(current.props);
|
||||||
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
const key = ref(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 = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
||||||
}
|
}
|
||||||
|
|
||||||
router.addListener('change', onChange);
|
router.addListener('change', onChange);
|
||||||
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { version } from '@/config.js';
|
import { version } from '@/config.js';
|
||||||
@ -42,29 +42,29 @@ const props = withDefaults(defineProps<{
|
|||||||
}>(), {
|
}>(), {
|
||||||
});
|
});
|
||||||
|
|
||||||
let loaded = $ref(false);
|
const loaded = ref(false);
|
||||||
let serverIsDead = $ref(false);
|
const serverIsDead = ref(false);
|
||||||
let meta = $ref<Misskey.entities.MetaResponse | null>(null);
|
const meta = ref<Misskey.entities.MetaResponse | null>(null);
|
||||||
|
|
||||||
os.api('meta', {
|
os.api('meta', {
|
||||||
detail: false,
|
detail: false,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
serverIsDead = false;
|
serverIsDead.value = false;
|
||||||
meta = res;
|
meta.value = res;
|
||||||
miLocalStorage.setItem('v', res.version);
|
miLocalStorage.setItem('v', res.version);
|
||||||
}, () => {
|
}, () => {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
serverIsDead = true;
|
serverIsDead.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
function reload() {
|
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,
|
||||||
|
@ -123,7 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onBeforeUnmount } from 'vue';
|
import { nextTick, onBeforeUnmount, ref, shallowRef, computed } from 'vue';
|
||||||
import { version } from '@/config.js';
|
import { version } from '@/config.js';
|
||||||
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';
|
||||||
@ -310,18 +310,18 @@ const patrons = [
|
|||||||
'SHO SEKIGUCHI',
|
'SHO SEKIGUCHI',
|
||||||
];
|
];
|
||||||
|
|
||||||
let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure'));
|
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
|
||||||
|
|
||||||
let easterEggReady = false;
|
let easterEggReady = false;
|
||||||
let easterEggEmojis = $ref([]);
|
const easterEggEmojis = ref([]);
|
||||||
let easterEggEngine = $ref(null);
|
const easterEggEngine = ref(null);
|
||||||
const containerEl = $shallowRef<HTMLElement>();
|
const containerEl = shallowRef<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),
|
||||||
@ -337,7 +337,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() {
|
||||||
@ -348,19 +348,19 @@ function iLoveMisskey() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTreasure() {
|
function getTreasure() {
|
||||||
thereIsTreasure = false;
|
thereIsTreasure.value = false;
|
||||||
claimAchievement('foundTreasure');
|
claimAchievement('foundTreasure');
|
||||||
}
|
}
|
||||||
|
|
||||||
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.aboutMisskey,
|
title: i18n.ts.aboutMisskey,
|
||||||
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from 'vue';
|
import { watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import XEmoji from './emojis.emoji.vue';
|
import XEmoji from './emojis.emoji.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
@ -47,44 +47,44 @@ import { i18n } from '@/i18n.js';
|
|||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
|
|
||||||
const customEmojiTags = getCustomEmojiTags();
|
const customEmojiTags = getCustomEmojiTags();
|
||||||
let q = $ref('');
|
const q = ref('');
|
||||||
let searchEmojis = $ref<Misskey.entities.EmojiSimple[]>(null);
|
const searchEmojis = ref<Misskey.entities.EmojiSimple[]>(null);
|
||||||
let selectedTags = $ref(new Set());
|
const selectedTags = ref(new Set());
|
||||||
|
|
||||||
function search() {
|
function search() {
|
||||||
if ((q === '' || q == null) && selectedTags.size === 0) {
|
if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) {
|
||||||
searchEmojis = null;
|
searchEmojis.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedTags.size === 0) {
|
if (selectedTags.value.size === 0) {
|
||||||
const queryarry = q.match(/\:([a-z0-9_]*)\:/g);
|
const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
|
||||||
|
|
||||||
if (queryarry) {
|
if (queryarry) {
|
||||||
searchEmojis = customEmojis.value.filter(emoji =>
|
searchEmojis.value = customEmojis.value.filter(emoji =>
|
||||||
queryarry.includes(`:${emoji.name}:`),
|
queryarry.includes(`:${emoji.name}:`),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
|
searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t)));
|
searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q.value)) && [...selectedTags.value].every(t => emoji.aliases.includes(t)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTag(tag) {
|
function toggleTag(tag) {
|
||||||
if (selectedTags.has(tag)) {
|
if (selectedTags.value.has(tag)) {
|
||||||
selectedTags.delete(tag);
|
selectedTags.value.delete(tag);
|
||||||
} else {
|
} else {
|
||||||
selectedTags.add(tag);
|
selectedTags.value.add(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch($$(q), () => {
|
watch(q, () => {
|
||||||
search();
|
search();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch($$(selectedTags), () => {
|
watch(selectedTags, () => {
|
||||||
search();
|
search();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
@ -59,25 +59,25 @@ import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
|||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
let host = $ref('');
|
const host = ref('');
|
||||||
let state = $ref('federating');
|
const state = ref('federating');
|
||||||
let sort = $ref('+pubSub');
|
const sort = ref('+pubSub');
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: 'federation/instances' as const,
|
endpoint: 'federation/instances' as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
displayLimit: 50,
|
displayLimit: 50,
|
||||||
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' ? { federating: true } :
|
state.value === 'federating' ? { federating: true } :
|
||||||
state === 'subscribing' ? { subscribing: true } :
|
state.value === 'subscribing' ? { subscribing: true } :
|
||||||
state === 'publishing' ? { publishing: true } :
|
state.value === 'publishing' ? { publishing: true } :
|
||||||
state === 'suspended' ? { suspended: true } :
|
state.value === 'suspended' ? { suspended: true } :
|
||||||
state === 'blocked' ? { blocked: true } :
|
state.value === 'blocked' ? { blocked: true } :
|
||||||
state === 'silenced' ? { silenced: true } :
|
state.value === 'silenced' ? { silenced: true } :
|
||||||
state === 'notResponding' ? { notResponding: true } :
|
state.value === 'notResponding' ? { notResponding: true } :
|
||||||
{}),
|
{}),
|
||||||
})),
|
})),
|
||||||
} as Paging;
|
} as Paging;
|
||||||
|
@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from 'vue';
|
import { computed, watch, ref } from 'vue';
|
||||||
import XEmojis from './about.emojis.vue';
|
import XEmojis from './about.emojis.vue';
|
||||||
import XFederation from './about.federation.vue';
|
import XFederation from './about.federation.vue';
|
||||||
import { version, host } from '@/config.js';
|
import { version, host } from '@/config.js';
|
||||||
@ -126,23 +126,23 @@ const props = withDefaults(defineProps<{
|
|||||||
initialTab: 'overview',
|
initialTab: 'overview',
|
||||||
});
|
});
|
||||||
|
|
||||||
let stats = $ref(null);
|
const stats = ref(null);
|
||||||
let tab = $ref(props.initialTab);
|
const tab = ref(props.initialTab);
|
||||||
|
|
||||||
watch($$(tab), () => {
|
watch(tab, () => {
|
||||||
if (tab === 'charts') {
|
if (tab.value === 'charts') {
|
||||||
claimAchievement('viewInstanceChart');
|
claimAchievement('viewInstanceChart');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const initStats = () => os.api('stats', {
|
const initStats = () => os.api('stats', {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
stats = res;
|
stats.value = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [{
|
const headerTabs = computed(() => [{
|
||||||
key: 'overview',
|
key: 'overview',
|
||||||
title: i18n.ts.overview,
|
title: i18n.ts.overview,
|
||||||
}, {
|
}, {
|
||||||
|
@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkObjectView from '@/components/MkObjectView.vue';
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
@ -82,19 +82,19 @@ import { i18n } from '@/i18n.js';
|
|||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { iAmAdmin, iAmModerator } from '@/account.js';
|
import { iAmAdmin, iAmModerator } from '@/account.js';
|
||||||
|
|
||||||
let tab = $ref('overview');
|
const tab = ref('overview');
|
||||||
let file: any = $ref(null);
|
const file = ref<any>(null);
|
||||||
let info: any = $ref(null);
|
const info = ref<any>(null);
|
||||||
let isSensitive: boolean = $ref(false);
|
const isSensitive = ref<boolean>(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', { fileId: props.fileId });
|
||||||
isSensitive = file.isSensitive;
|
isSensitive.value = file.value.isSensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch();
|
fetch();
|
||||||
@ -102,29 +102,29 @@ 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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleIsSensitive(v) {
|
async function toggleIsSensitive(v) {
|
||||||
await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
|
await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
|
||||||
isSensitive = v;
|
isSensitive.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = computed(() => [{
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
icon: 'ti ti-external-link',
|
icon: 'ti ti-external-link',
|
||||||
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,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
@ -139,7 +139,7 @@ const headerTabs = $computed(() => [{
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: file ? i18n.ts.file + ': ' + file.name : i18n.ts.file,
|
title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
|
||||||
icon: 'ti ti-file',
|
icon: 'ti ti-file',
|
||||||
})));
|
})));
|
||||||
</script>
|
</script>
|
||||||
|
@ -203,7 +203,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, watch } from 'vue';
|
import { computed, defineAsyncComponent, watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkChart from '@/components/MkChart.vue';
|
import MkChart from '@/components/MkChart.vue';
|
||||||
import MkObjectView from '@/components/MkObjectView.vue';
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
@ -234,17 +234,17 @@ const props = withDefaults(defineProps<{
|
|||||||
initialTab: 'overview',
|
initialTab: 'overview',
|
||||||
});
|
});
|
||||||
|
|
||||||
let tab = $ref(props.initialTab);
|
const tab = ref(props.initialTab);
|
||||||
let chartSrc = $ref('per-user-notes');
|
const chartSrc = ref('per-user-notes');
|
||||||
let user = $ref<null | Misskey.entities.UserDetailed>();
|
const user = ref<null | Misskey.entities.UserDetailed>();
|
||||||
let init = $ref<ReturnType<typeof createFetcher>>();
|
const init = ref<ReturnType<typeof createFetcher>>();
|
||||||
let info = $ref();
|
const info = ref();
|
||||||
let ips = $ref(null);
|
const ips = ref(null);
|
||||||
let ap = $ref(null);
|
const ap = ref(null);
|
||||||
let moderator = $ref(false);
|
const moderator = ref(false);
|
||||||
let silenced = $ref(false);
|
const silenced = ref(false);
|
||||||
let suspended = $ref(false);
|
const suspended = ref(false);
|
||||||
let moderationNote = $ref('');
|
const moderationNote = ref('');
|
||||||
const filesPagination = {
|
const filesPagination = {
|
||||||
endpoint: 'admin/drive/files' as const,
|
endpoint: 'admin/drive/files' as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -259,7 +259,7 @@ const announcementsPagination = {
|
|||||||
userId: props.userId,
|
userId: props.userId,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
let expandedRoles = $ref([]);
|
const expandedRoles = ref([]);
|
||||||
|
|
||||||
function createFetcher() {
|
function createFetcher() {
|
||||||
return () => Promise.all([os.api('users/show', {
|
return () => Promise.all([os.api('users/show', {
|
||||||
@ -269,27 +269,27 @@ function createFetcher() {
|
|||||||
}), iAmAdmin ? os.api('admin/get-user-ips', {
|
}), iAmAdmin ? os.api('admin/get-user-ips', {
|
||||||
userId: props.userId,
|
userId: props.userId,
|
||||||
}) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
|
}) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
|
||||||
user = _user;
|
user.value = _user;
|
||||||
info = _info;
|
info.value = _info;
|
||||||
ips = _ips;
|
ips.value = _ips;
|
||||||
moderator = info.isModerator;
|
moderator.value = info.value.isModerator;
|
||||||
silenced = info.isSilenced;
|
silenced.value = info.value.isSilenced;
|
||||||
suspended = info.isSuspended;
|
suspended.value = info.value.isSuspended;
|
||||||
moderationNote = info.moderationNote;
|
moderationNote.value = info.value.moderationNote;
|
||||||
|
|
||||||
watch($$(moderationNote), async () => {
|
watch(moderationNote, async () => {
|
||||||
await os.api('admin/update-user-note', { userId: user.id, text: moderationNote });
|
await os.api('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
|
||||||
await refreshUser();
|
await refreshUser();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshUser() {
|
function refreshUser() {
|
||||||
init = createFetcher();
|
init.value = createFetcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateRemoteUser() {
|
async function updateRemoteUser() {
|
||||||
await os.apiWithDialog('federation/update-remote-user', { userId: user.id });
|
await os.apiWithDialog('federation/update-remote-user', { userId: user.value.id });
|
||||||
refreshUser();
|
refreshUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ async function resetPassword() {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const { password } = await os.api('admin/reset-password', {
|
const { password } = await os.api('admin/reset-password', {
|
||||||
userId: user.id,
|
userId: user.value.id,
|
||||||
});
|
});
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
@ -317,9 +317,9 @@ async function toggleSuspend(v) {
|
|||||||
text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm,
|
text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm,
|
||||||
});
|
});
|
||||||
if (confirm.canceled) {
|
if (confirm.canceled) {
|
||||||
suspended = !v;
|
suspended.value = !v;
|
||||||
} else {
|
} else {
|
||||||
await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.id });
|
await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id });
|
||||||
await refreshUser();
|
await refreshUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -331,7 +331,7 @@ async function unsetUserAvatar() {
|
|||||||
});
|
});
|
||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
const process = async () => {
|
const process = async () => {
|
||||||
await os.api('admin/unset-user-avatar', { userId: user.id });
|
await os.api('admin/unset-user-avatar', { userId: user.value.id });
|
||||||
os.success();
|
os.success();
|
||||||
};
|
};
|
||||||
await process().catch(err => {
|
await process().catch(err => {
|
||||||
@ -350,7 +350,7 @@ async function unsetUserBanner() {
|
|||||||
});
|
});
|
||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
const process = async () => {
|
const process = async () => {
|
||||||
await os.api('admin/unset-user-banner', { userId: user.id });
|
await os.api('admin/unset-user-banner', { userId: user.value.id });
|
||||||
os.success();
|
os.success();
|
||||||
};
|
};
|
||||||
await process().catch(err => {
|
await process().catch(err => {
|
||||||
@ -369,7 +369,7 @@ async function deleteAllFiles() {
|
|||||||
});
|
});
|
||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
const process = async () => {
|
const process = async () => {
|
||||||
await os.api('admin/delete-all-files-of-a-user', { userId: user.id });
|
await os.api('admin/delete-all-files-of-a-user', { userId: user.value.id });
|
||||||
os.success();
|
os.success();
|
||||||
};
|
};
|
||||||
await process().catch(err => {
|
await process().catch(err => {
|
||||||
@ -389,13 +389,13 @@ async function deleteAccount() {
|
|||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
|
|
||||||
const typed = await os.inputText({
|
const typed = await os.inputText({
|
||||||
text: i18n.t('typeToConfirm', { x: user?.username }),
|
text: i18n.t('typeToConfirm', { x: user.value?.username }),
|
||||||
});
|
});
|
||||||
if (typed.canceled) return;
|
if (typed.canceled) return;
|
||||||
|
|
||||||
if (typed.result === user?.username) {
|
if (typed.result === user.value?.username) {
|
||||||
await os.apiWithDialog('admin/delete-account', {
|
await os.apiWithDialog('admin/delete-account', {
|
||||||
userId: user.id,
|
userId: user.value.id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
os.alert({
|
os.alert({
|
||||||
@ -438,7 +438,7 @@ async function assignRole() {
|
|||||||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.id, expiresAt });
|
await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.value.id, expiresAt });
|
||||||
refreshUser();
|
refreshUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,50 +448,50 @@ async function unassignRole(role, ev) {
|
|||||||
icon: 'ti ti-x',
|
icon: 'ti ti-x',
|
||||||
danger: true,
|
danger: true,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id });
|
await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.value.id });
|
||||||
refreshUser();
|
refreshUser();
|
||||||
},
|
},
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleRoleItem(role) {
|
function toggleRoleItem(role) {
|
||||||
if (expandedRoles.includes(role.id)) {
|
if (expandedRoles.value.includes(role.id)) {
|
||||||
expandedRoles = expandedRoles.filter(x => x !== role.id);
|
expandedRoles.value = expandedRoles.value.filter(x => x !== role.id);
|
||||||
} else {
|
} else {
|
||||||
expandedRoles.push(role.id);
|
expandedRoles.value.push(role.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAnnouncement() {
|
function createAnnouncement() {
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
|
||||||
user,
|
user: user.value,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
function editAnnouncement(announcement) {
|
function editAnnouncement(announcement) {
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
|
||||||
user,
|
user: user.value,
|
||||||
announcement,
|
announcement,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.userId, () => {
|
watch(() => props.userId, () => {
|
||||||
init = createFetcher();
|
init.value = createFetcher();
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
watch($$(user), () => {
|
watch(user, () => {
|
||||||
os.api('ap/get', {
|
os.api('ap/get', {
|
||||||
uri: user.uri ?? `${url}/users/${user.id}`,
|
uri: user.value.uri ?? `${url}/users/${user.value.id}`,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
ap = res;
|
ap.value = res;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [{
|
const headerTabs = computed(() => [{
|
||||||
key: 'overview',
|
key: 'overview',
|
||||||
title: i18n.ts.overview,
|
title: i18n.ts.overview,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
@ -518,7 +518,7 @@ const headerTabs = $computed(() => [{
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: user ? acct(user) : i18n.ts.userInfo,
|
title: user.value ? acct(user.value) : i18n.ts.userInfo,
|
||||||
icon: 'ti ti-user-exclamation',
|
icon: 'ti ti-user-exclamation',
|
||||||
})));
|
})));
|
||||||
</script>
|
</script>
|
||||||
|
@ -69,7 +69,7 @@ const metadata = injectPageMetadata();
|
|||||||
|
|
||||||
const el = shallowRef<HTMLElement>(null);
|
const el = shallowRef<HTMLElement>(null);
|
||||||
const tabRefs = {};
|
const tabRefs = {};
|
||||||
const tabHighlightEl = $shallowRef<HTMLElement | null>(null);
|
const tabHighlightEl = shallowRef<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(() => {
|
||||||
@ -131,13 +131,13 @@ onMounted(() => {
|
|||||||
watch(() => [props.tab, props.tabs], () => {
|
watch(() => [props.tab, props.tabs], () => {
|
||||||
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 = tabEl.parentElement.getBoundingClientRect();
|
const parentRect = 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 = (rect.left - parentRect.left) + 'px';
|
tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
|
@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, shallowRef, ref } from 'vue';
|
||||||
|
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
@ -61,31 +61,31 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
let reports = $shallowRef<InstanceType<typeof MkPagination>>();
|
const reports = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
let state = $ref('unresolved');
|
const state = ref('unresolved');
|
||||||
let reporterOrigin = $ref('combined');
|
const reporterOrigin = ref('combined');
|
||||||
let targetUserOrigin = $ref('combined');
|
const targetUserOrigin = ref('combined');
|
||||||
let searchUsername = $ref('');
|
const searchUsername = ref('');
|
||||||
let searchHost = $ref('');
|
const 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(reportId);
|
reports.value.removeItem(reportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.abuseReports,
|
title: i18n.ts.abuseReports,
|
||||||
|
@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -98,7 +98,7 @@ import * as os from '@/os.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
let ads: any[] = $ref([]);
|
const ads = ref<any[]>([]);
|
||||||
|
|
||||||
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
|
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
|
||||||
const localTime = new Date();
|
const localTime = new Date();
|
||||||
@ -109,7 +109,7 @@ let publishing: boolean | null = null;
|
|||||||
|
|
||||||
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
||||||
if (adsResponse != null) {
|
if (adsResponse != null) {
|
||||||
ads = adsResponse.map(r => {
|
ads.value = adsResponse.map(r => {
|
||||||
const exdate = new Date(r.expiresAt);
|
const exdate = new Date(r.expiresAt);
|
||||||
const stdate = new Date(r.startsAt);
|
const stdate = new Date(r.startsAt);
|
||||||
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
||||||
@ -141,7 +141,7 @@ function toggleDayOfWeek(ad, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function add() {
|
function add() {
|
||||||
ads.unshift({
|
ads.value.unshift({
|
||||||
id: null,
|
id: null,
|
||||||
memo: '',
|
memo: '',
|
||||||
place: 'square',
|
place: 'square',
|
||||||
@ -161,7 +161,7 @@ function remove(ad) {
|
|||||||
text: i18n.t('removeAreYouSure', { x: ad.url }),
|
text: i18n.t('removeAreYouSure', { x: ad.url }),
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
ads = ads.filter(x => x !== ad);
|
ads.value = ads.value.filter(x => x !== ad);
|
||||||
if (ad.id == null) return;
|
if (ad.id == null) return;
|
||||||
os.apiWithDialog('admin/ad/delete', {
|
os.apiWithDialog('admin/ad/delete', {
|
||||||
id: ad.id,
|
id: ad.id,
|
||||||
@ -209,9 +209,9 @@ function save(ad) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function more() {
|
function more() {
|
||||||
os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
|
os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
|
||||||
if (adsResponse == null) return;
|
if (adsResponse == null) return;
|
||||||
ads = ads.concat(adsResponse.map(r => {
|
ads.value = ads.value.concat(adsResponse.map(r => {
|
||||||
const exdate = new Date(r.expiresAt);
|
const exdate = new Date(r.expiresAt);
|
||||||
const stdate = new Date(r.startsAt);
|
const stdate = new Date(r.startsAt);
|
||||||
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
||||||
@ -228,7 +228,7 @@ function more() {
|
|||||||
function refresh() {
|
function refresh() {
|
||||||
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
||||||
if (adsResponse == null) return;
|
if (adsResponse == null) return;
|
||||||
ads = adsResponse.map(r => {
|
ads.value = adsResponse.map(r => {
|
||||||
const exdate = new Date(r.expiresAt);
|
const exdate = new Date(r.expiresAt);
|
||||||
const stdate = new Date(r.startsAt);
|
const stdate = new Date(r.startsAt);
|
||||||
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
||||||
@ -244,14 +244,14 @@ function refresh() {
|
|||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = computed(() => [{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
text: i18n.ts.add,
|
text: i18n.ts.add,
|
||||||
handler: add,
|
handler: add,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.ads,
|
title: i18n.ts.ads,
|
||||||
|
@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -84,14 +84,14 @@ import { i18n } from '@/i18n.js';
|
|||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
|
||||||
let announcements: any[] = $ref([]);
|
const announcements = ref<any[]>([]);
|
||||||
|
|
||||||
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: Math.random().toString(36),
|
_id: Math.random().toString(36),
|
||||||
id: null,
|
id: null,
|
||||||
title: 'New announcement',
|
title: 'New announcement',
|
||||||
@ -111,7 +111,7 @@ function del(announcement) {
|
|||||||
text: i18n.t('deleteAreYouSure', { x: announcement.title }),
|
text: i18n.t('deleteAreYouSure', { 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,27 +134,27 @@ async function save(announcement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function more() {
|
function more() {
|
||||||
os.api('admin/announcements/list', { untilId: announcements.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
|
os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
|
||||||
announcements = announcements.concat(announcementResponse);
|
announcements.value = announcements.value.concat(announcementResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
os.api('admin/announcements/list').then(announcementResponse => {
|
os.api('admin/announcements/list').then(announcementResponse => {
|
||||||
announcements = announcementResponse;
|
announcements.value = announcementResponse;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = computed(() => [{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
text: i18n.ts.add,
|
text: i18n.ts.add,
|
||||||
handler: add,
|
handler: add,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.announcements,
|
title: i18n.ts.announcements,
|
||||||
|
@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent, ref } from 'vue';
|
||||||
import MkRadios from '@/components/MkRadios.vue';
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
@ -76,37 +76,37 @@ import { i18n } from '@/i18n.js';
|
|||||||
|
|
||||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||||
|
|
||||||
let provider = $ref(null);
|
const provider = ref(null);
|
||||||
let hcaptchaSiteKey: string | null = $ref(null);
|
const hcaptchaSiteKey = ref<string | null>(null);
|
||||||
let hcaptchaSecretKey: string | null = $ref(null);
|
const hcaptchaSecretKey = ref<string | null>(null);
|
||||||
let recaptchaSiteKey: string | null = $ref(null);
|
const recaptchaSiteKey = ref<string | null>(null);
|
||||||
let recaptchaSecretKey: string | null = $ref(null);
|
const recaptchaSecretKey = ref<string | null>(null);
|
||||||
let turnstileSiteKey: string | null = $ref(null);
|
const turnstileSiteKey = ref<string | null>(null);
|
||||||
let turnstileSecretKey: string | null = $ref(null);
|
const turnstileSecretKey = ref<string | null>(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;
|
||||||
turnstileSiteKey = meta.turnstileSiteKey;
|
turnstileSiteKey.value = meta.turnstileSiteKey;
|
||||||
turnstileSecretKey = meta.turnstileSecretKey;
|
turnstileSecretKey.value = meta.turnstileSecretKey;
|
||||||
|
|
||||||
provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
|
provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
enableTurnstile: provider === 'turnstile',
|
enableTurnstile: provider.value === 'turnstile',
|
||||||
turnstileSiteKey,
|
turnstileSiteKey: turnstileSiteKey.value,
|
||||||
turnstileSecretKey,
|
turnstileSecretKey: turnstileSecretKey.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
@ -111,55 +111,55 @@ import MkButton from '@/components/MkButton.vue';
|
|||||||
import MkColorInput from '@/components/MkColorInput.vue';
|
import MkColorInput from '@/components/MkColorInput.vue';
|
||||||
import { host } from '@/config.js';
|
import { host } from '@/config.js';
|
||||||
|
|
||||||
let iconUrl: string | null = $ref(null);
|
const iconUrl = ref<string | null>(null);
|
||||||
let app192IconUrl: string | null = $ref(null);
|
const app192IconUrl = ref<string | null>(null);
|
||||||
let app512IconUrl: string | null = $ref(null);
|
const app512IconUrl = ref<string | null>(null);
|
||||||
let bannerUrl: string | null = $ref(null);
|
const bannerUrl = ref<string | null>(null);
|
||||||
let backgroundImageUrl: string | null = $ref(null);
|
const backgroundImageUrl = ref<string | null>(null);
|
||||||
let themeColor: any = $ref(null);
|
const themeColor = ref<any>(null);
|
||||||
let defaultLightTheme: any = $ref(null);
|
const defaultLightTheme = ref<any>(null);
|
||||||
let defaultDarkTheme: any = $ref(null);
|
const defaultDarkTheme = ref<any>(null);
|
||||||
let serverErrorImageUrl: string | null = $ref(null);
|
const serverErrorImageUrl = ref<string | null>(null);
|
||||||
let infoImageUrl: string | null = $ref(null);
|
const infoImageUrl = ref<string | null>(null);
|
||||||
let notFoundImageUrl: string | null = $ref(null);
|
const notFoundImageUrl = ref<string | null>(null);
|
||||||
let manifestJsonOverride: string = $ref('{}');
|
const manifestJsonOverride = ref<string>('{}');
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
iconUrl = meta.iconUrl;
|
iconUrl.value = meta.iconUrl;
|
||||||
app192IconUrl = meta.app192IconUrl;
|
app192IconUrl.value = meta.app192IconUrl;
|
||||||
app512IconUrl = meta.app512IconUrl;
|
app512IconUrl.value = meta.app512IconUrl;
|
||||||
bannerUrl = meta.bannerUrl;
|
bannerUrl.value = meta.bannerUrl;
|
||||||
backgroundImageUrl = meta.backgroundImageUrl;
|
backgroundImageUrl.value = meta.backgroundImageUrl;
|
||||||
themeColor = meta.themeColor;
|
themeColor.value = meta.themeColor;
|
||||||
defaultLightTheme = meta.defaultLightTheme;
|
defaultLightTheme.value = meta.defaultLightTheme;
|
||||||
defaultDarkTheme = meta.defaultDarkTheme;
|
defaultDarkTheme.value = meta.defaultDarkTheme;
|
||||||
serverErrorImageUrl = meta.serverErrorImageUrl;
|
serverErrorImageUrl.value = meta.serverErrorImageUrl;
|
||||||
infoImageUrl = meta.infoImageUrl;
|
infoImageUrl.value = meta.infoImageUrl;
|
||||||
notFoundImageUrl = meta.notFoundImageUrl;
|
notFoundImageUrl.value = meta.notFoundImageUrl;
|
||||||
manifestJsonOverride = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
|
manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog('admin/update-meta', {
|
os.apiWithDialog('admin/update-meta', {
|
||||||
iconUrl,
|
iconUrl: iconUrl.value,
|
||||||
app192IconUrl,
|
app192IconUrl: app192IconUrl.value,
|
||||||
app512IconUrl,
|
app512IconUrl: app512IconUrl.value,
|
||||||
bannerUrl,
|
bannerUrl: bannerUrl.value,
|
||||||
backgroundImageUrl,
|
backgroundImageUrl: backgroundImageUrl.value,
|
||||||
themeColor: themeColor === '' ? null : themeColor,
|
themeColor: themeColor.value === '' ? null : themeColor.value,
|
||||||
defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
|
defaultLightTheme: defaultLightTheme.value === '' ? null : defaultLightTheme.value,
|
||||||
defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
|
defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value,
|
||||||
infoImageUrl,
|
infoImageUrl: infoImageUrl.value,
|
||||||
notFoundImageUrl,
|
notFoundImageUrl: notFoundImageUrl.value,
|
||||||
serverErrorImageUrl,
|
serverErrorImageUrl: serverErrorImageUrl.value,
|
||||||
manifestJsonOverride: manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride)),
|
manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.branding,
|
title: i18n.ts.branding,
|
||||||
|
@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { computed } from 'vue';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
@ -29,9 +29,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|||||||
|
|
||||||
const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
|
const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => 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,
|
||||||
|
@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
@ -78,23 +78,23 @@ import { i18n } from '@/i18n.js';
|
|||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
let enableEmail: boolean = $ref(false);
|
const enableEmail = ref<boolean>(false);
|
||||||
let email: any = $ref(null);
|
const email = ref<any>(null);
|
||||||
let smtpSecure: boolean = $ref(false);
|
const smtpSecure = ref<boolean>(false);
|
||||||
let smtpHost: string = $ref('');
|
const smtpHost = ref<string>('');
|
||||||
let smtpPort: number = $ref(0);
|
const smtpPort = ref<number>(0);
|
||||||
let smtpUser: string = $ref('');
|
const smtpUser = ref<string>('');
|
||||||
let smtpPass: string = $ref('');
|
const smtpPass = ref<string>('');
|
||||||
|
|
||||||
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() {
|
||||||
@ -115,19 +115,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 headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.emailServer,
|
title: i18n.ts.emailServer,
|
||||||
|
@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
@ -46,27 +46,27 @@ import { fetchInstance } from '@/instance.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
let deeplAuthKey: string = $ref('');
|
const deeplAuthKey = ref<string>('');
|
||||||
let deeplIsPro: boolean = $ref(false);
|
const deeplIsPro = ref<boolean>(false);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
deeplAuthKey = meta.deeplAuthKey;
|
deeplAuthKey.value = meta.deeplAuthKey;
|
||||||
deeplIsPro = meta.deeplIsPro;
|
deeplIsPro.value = meta.deeplIsPro;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog('admin/update-meta', {
|
os.apiWithDialog('admin/update-meta', {
|
||||||
deeplAuthKey,
|
deeplAuthKey: deeplAuthKey.value,
|
||||||
deeplIsPro,
|
deeplIsPro: deeplIsPro.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.externalServices,
|
title: i18n.ts.externalServices,
|
||||||
|
@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
@ -68,24 +68,24 @@ import FormSplit from '@/components/form/split.vue';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
let host = $ref('');
|
const host = ref('');
|
||||||
let state = $ref('federating');
|
const state = ref('federating');
|
||||||
let sort = $ref('+pubSub');
|
const 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' ? { federating: true } :
|
state.value === 'federating' ? { federating: true } :
|
||||||
state === 'subscribing' ? { subscribing: true } :
|
state.value === 'subscribing' ? { subscribing: true } :
|
||||||
state === 'publishing' ? { publishing: true } :
|
state.value === 'publishing' ? { publishing: true } :
|
||||||
state === 'suspended' ? { suspended: true } :
|
state.value === 'suspended' ? { suspended: true } :
|
||||||
state === 'blocked' ? { blocked: true } :
|
state.value === 'blocked' ? { blocked: true } :
|
||||||
state === 'silenced' ? { silenced: true } :
|
state.value === 'silenced' ? { silenced: true } :
|
||||||
state === 'notResponding' ? { notResponding: true } :
|
state.value === 'notResponding' ? { notResponding: true } :
|
||||||
{}),
|
{}),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@ -98,9 +98,9 @@ function getStatus(instance) {
|
|||||||
return 'Alive';
|
return 'Alive';
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: i18n.ts.federation,
|
title: i18n.ts.federation,
|
||||||
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
@ -45,19 +45,19 @@ import * as os from '@/os.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
let origin = $ref('local');
|
const origin = ref('local');
|
||||||
let type = $ref(null);
|
const type = ref(null);
|
||||||
let searchHost = $ref('');
|
const searchHost = ref('');
|
||||||
let userId = $ref('');
|
const userId = ref('');
|
||||||
let viewMode = $ref('grid');
|
const 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,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ async function find() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = computed(() => [{
|
||||||
text: i18n.ts.lookup,
|
text: i18n.ts.lookup,
|
||||||
icon: 'ti ti-search',
|
icon: 'ti ti-search',
|
||||||
handler: find,
|
handler: find,
|
||||||
@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
|
|||||||
handler: clear,
|
handler: clear,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: i18n.ts.files,
|
title: i18n.ts.files,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user