Merge branch 'master' into ncat2
This commit is contained in:
@ -1,68 +1,61 @@
|
||||
<template>
|
||||
<MkLoading v-if="!loaded" />
|
||||
<MkLoading v-if="!loaded"/>
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
|
||||
<div v-show="loaded" class="mjndxjch">
|
||||
<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
|
||||
<p><b><i class="fas fa-exclamation-triangle"></i> {{ $ts.pageLoadError }}</b></p>
|
||||
<p v-if="version === meta.version">{{ $ts.pageLoadErrorDescription }}</p>
|
||||
<p v-else-if="serverIsDead">{{ $ts.serverIsDead }}</p>
|
||||
<p><b><i class="fas fa-exclamation-triangle"></i> {{ i18n.locale.pageLoadError }}</b></p>
|
||||
<p v-if="meta && (version === meta.version)">{{ i18n.locale.pageLoadErrorDescription }}</p>
|
||||
<p v-else-if="serverIsDead">{{ i18n.locale.serverIsDead }}</p>
|
||||
<template v-else>
|
||||
<p>{{ $ts.newVersionOfClientAvailable }}</p>
|
||||
<p>{{ $ts.youShouldUpgradeClient }}</p>
|
||||
<MkButton class="button primary" @click="reload">{{ $ts.reload }}</MkButton>
|
||||
<p>{{ i18n.locale.newVersionOfClientAvailable }}</p>
|
||||
<p>{{ i18n.locale.youShouldUpgradeClient }}</p>
|
||||
<MkButton class="button primary" @click="reload">{{ i18n.locale.reload }}</MkButton>
|
||||
</template>
|
||||
<p><MkA to="/docs/general/troubleshooting" class="_link">{{ $ts.troubleshooting }}</MkA></p>
|
||||
<p><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.locale.troubleshooting }}</MkA></p>
|
||||
<p v-if="error" class="error">ERROR: {{ error }}</p>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { version } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
},
|
||||
props: {
|
||||
error: {
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.error,
|
||||
icon: 'fas fa-exclamation-triangle'
|
||||
},
|
||||
loaded: false,
|
||||
serverIsDead: false,
|
||||
meta: {} as any,
|
||||
version,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
os.api('meta', {
|
||||
detail: false
|
||||
}).then(meta => {
|
||||
this.loaded = true;
|
||||
this.serverIsDead = false;
|
||||
this.meta = meta;
|
||||
localStorage.setItem('v', meta.version);
|
||||
}, () => {
|
||||
this.loaded = true;
|
||||
this.serverIsDead = true;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
unisonReload();
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
error?: Error;
|
||||
}>(), {
|
||||
});
|
||||
|
||||
let loaded = $ref(false);
|
||||
let serverIsDead = $ref(false);
|
||||
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
||||
|
||||
os.api('meta', {
|
||||
detail: false,
|
||||
}).then(res => {
|
||||
loaded = true;
|
||||
serverIsDead = false;
|
||||
meta = res;
|
||||
localStorage.setItem('v', res.version);
|
||||
}, () => {
|
||||
loaded = true;
|
||||
serverIsDead = true;
|
||||
});
|
||||
|
||||
function reload() {
|
||||
unisonReload();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.error,
|
||||
icon: 'fas fa-exclamation-triangle',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -2,9 +2,5 @@
|
||||
<MkLoading/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({});
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
@ -3,36 +3,39 @@
|
||||
<MkSpacer :content-max="600" :margin-min="20">
|
||||
<div class="_formRoot znqjceqz">
|
||||
<div id="debug"></div>
|
||||
<div ref="about" v-panel class="_formBlock about" :class="{ playing: easterEggEngine != null }">
|
||||
<div ref="containerEl" v-panel class="_formBlock about" :class="{ playing: easterEggEngine != null }">
|
||||
<img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/>
|
||||
<div class="misskey">Misskey</div>
|
||||
<div class="version">v{{ version }}</div>
|
||||
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span>
|
||||
</div>
|
||||
<div class="_formBlock" style="text-align: center;">
|
||||
{{ $ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ $ts.learnMore }}</a>
|
||||
{{ i18n.locale._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.locale.learnMore }}</a>
|
||||
</div>
|
||||
<div class="_formBlock" style="text-align: center;">
|
||||
<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton>
|
||||
</div>
|
||||
<FormSection>
|
||||
<div class="_formLinks">
|
||||
<FormLink to="https://github.com/misskey-dev/misskey" external>
|
||||
<template #icon><i class="fas fa-code"></i></template>
|
||||
{{ $ts._aboutMisskey.source }}
|
||||
{{ i18n.locale._aboutMisskey.source }}
|
||||
<template #suffix>GitHub</template>
|
||||
</FormLink>
|
||||
<FormLink to="https://crowdin.com/project/misskey" external>
|
||||
<template #icon><i class="fas fa-language"></i></template>
|
||||
{{ $ts._aboutMisskey.translation }}
|
||||
{{ i18n.locale._aboutMisskey.translation }}
|
||||
<template #suffix>Crowdin</template>
|
||||
</FormLink>
|
||||
<FormLink to="https://www.patreon.com/syuilo" external>
|
||||
<template #icon><i class="fas fa-hand-holding-medical"></i></template>
|
||||
{{ $ts._aboutMisskey.donate }}
|
||||
{{ i18n.locale._aboutMisskey.donate }}
|
||||
<template #suffix>Patreon</template>
|
||||
</FormLink>
|
||||
</div>
|
||||
</FormSection>
|
||||
<FormSection>
|
||||
<template #label>{{ $ts._aboutMisskey.contributors }}</template>
|
||||
<template #label>{{ i18n.locale._aboutMisskey.contributors }}</template>
|
||||
<div class="_formLinks">
|
||||
<FormLink to="https://github.com/syuilo" external>@syuilo</FormLink>
|
||||
<FormLink to="https://github.com/AyaMorisawa" external>@AyaMorisawa</FormLink>
|
||||
@ -44,27 +47,30 @@
|
||||
<FormLink to="https://github.com/u1-liquid" external>@u1-liquid</FormLink>
|
||||
<FormLink to="https://github.com/marihachi" external>@marihachi</FormLink>
|
||||
</div>
|
||||
<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ $ts._aboutMisskey.allContributors }}</MkLink></template>
|
||||
<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.locale._aboutMisskey.allContributors }}</MkLink></template>
|
||||
</FormSection>
|
||||
<FormSection>
|
||||
<template #label><Mfm text="$[jelly ❤]"/> {{ $ts._aboutMisskey.patrons }}</template>
|
||||
<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.locale._aboutMisskey.patrons }}</template>
|
||||
<div v-for="patron in patrons" :key="patron">{{ patron }}</div>
|
||||
<template #caption>{{ $ts._aboutMisskey.morePatrons }}</template>
|
||||
<template #caption>{{ i18n.locale._aboutMisskey.morePatrons }}</template>
|
||||
</FormSection>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount } from 'vue';
|
||||
import { version } from '@/config';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkLink from '@/components/link.vue';
|
||||
import { physics } from '@/scripts/physics';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
import { defaultStore } from '@/store';
|
||||
import * as os from '@/os';
|
||||
|
||||
const patrons = [
|
||||
'まっちゃとーにゅ',
|
||||
@ -145,58 +151,52 @@ const patrons = [
|
||||
'蝉暮せせせ',
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSection,
|
||||
FormLink,
|
||||
MkKeyValue,
|
||||
MkLink,
|
||||
},
|
||||
let easterEggReady = false;
|
||||
let easterEggEmojis = $ref([]);
|
||||
let easterEggEngine = $ref(null);
|
||||
const containerEl = $ref<HTMLElement>();
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.aboutMisskey,
|
||||
icon: null
|
||||
},
|
||||
version,
|
||||
patrons,
|
||||
easterEggReady: false,
|
||||
easterEggEmojis: [],
|
||||
easterEggEngine: null,
|
||||
}
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.easterEggEngine) {
|
||||
this.easterEggEngine.stop();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
iconLoaded() {
|
||||
const emojis = this.$store.state.reactions;
|
||||
const containerWidth = this.$refs.about.offsetWidth;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
this.easterEggEmojis.push({
|
||||
id: i.toString(),
|
||||
top: -(128 + (Math.random() * 256)),
|
||||
left: (Math.random() * containerWidth),
|
||||
emoji: emojis[Math.floor(Math.random() * emojis.length)],
|
||||
});
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.easterEggReady = true;
|
||||
});
|
||||
},
|
||||
|
||||
gravity() {
|
||||
if (!this.easterEggReady) return;
|
||||
this.easterEggReady = false;
|
||||
this.easterEggEngine = physics(this.$refs.about);
|
||||
}
|
||||
function iconLoaded() {
|
||||
const emojis = defaultStore.state.reactions;
|
||||
const containerWidth = containerEl.offsetWidth;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
easterEggEmojis.push({
|
||||
id: i.toString(),
|
||||
top: -(128 + (Math.random() * 256)),
|
||||
left: (Math.random() * containerWidth),
|
||||
emoji: emojis[Math.floor(Math.random() * emojis.length)],
|
||||
});
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
easterEggReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
function gravity() {
|
||||
if (!easterEggReady) return;
|
||||
easterEggReady = false;
|
||||
easterEggEngine = physics(containerEl);
|
||||
}
|
||||
|
||||
function iLoveMisskey() {
|
||||
os.post({
|
||||
initialText: 'I $[jelly ❤] #Misskey',
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (easterEggEngine) {
|
||||
easterEggEngine.stop();
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.aboutMisskey,
|
||||
icon: null,
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -67,8 +67,8 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { version, instanceName } from '@/config';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
@ -79,36 +79,21 @@ import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
import * as symbols from '@/symbols';
|
||||
import { host } from '@/config';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkKeyValue,
|
||||
FormSection,
|
||||
FormLink,
|
||||
FormSuspense,
|
||||
FormSplit,
|
||||
const stats = ref(null);
|
||||
|
||||
const initStats = () => os.api('stats', {
|
||||
}).then((res) => {
|
||||
stats.value = res;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.instanceInfo,
|
||||
icon: 'fas fa-info-circle',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.instanceInfo,
|
||||
icon: 'fas fa-info-circle'
|
||||
},
|
||||
host,
|
||||
version,
|
||||
instanceName,
|
||||
stats: null,
|
||||
initStats: () => os.api('stats', {
|
||||
}).then((stats) => {
|
||||
this.stats = stats;
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -34,27 +34,7 @@
|
||||
-->
|
||||
|
||||
<MkPagination v-slot="{items}" ref="reports" :pagination="pagination" style="margin-top: var(--margin);">
|
||||
<div v-for="report in items" :key="report.id" class="bcekxzvu _card _gap">
|
||||
<div class="_content target">
|
||||
<MkAvatar class="avatar" :user="report.targetUser" :show-indicator="true"/>
|
||||
<div class="info">
|
||||
<MkUserName class="name" :user="report.targetUser"/>
|
||||
<div class="acct">@{{ acct(report.targetUser) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<div>
|
||||
<Mfm :text="report.comment"/>
|
||||
</div>
|
||||
<hr>
|
||||
<div>Reporter: <MkAcct :user="report.reporter"/></div>
|
||||
<div><MkTime :time="report.createdAt"/></div>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<div v-if="report.assignee">Assignee: <MkAcct :user="report.assignee"/></div>
|
||||
<MkButton v-if="!report.resolved" primary @click="resolve(report)">{{ $ts.abuseMarkAsResolved }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,22 +42,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import { acct } from '@/filters/user';
|
||||
import XAbuseReport from '@/components/abuse-report.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
MkInput,
|
||||
MkSelect,
|
||||
MkPagination,
|
||||
XAbuseReport,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -95,44 +74,20 @@ export default defineComponent({
|
||||
reporterOrigin: 'combined',
|
||||
targetUserOrigin: 'combined',
|
||||
pagination: {
|
||||
endpoint: 'admin/abuse-user-reports',
|
||||
endpoint: 'admin/abuse-user-reports' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
state: this.state,
|
||||
reporterOrigin: this.reporterOrigin,
|
||||
targetUserOrigin: this.targetUserOrigin,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
state() {
|
||||
this.$refs.reports.reload();
|
||||
},
|
||||
|
||||
reporterOrigin() {
|
||||
this.$refs.reports.reload();
|
||||
},
|
||||
|
||||
targetUserOrigin() {
|
||||
this.$refs.reports.reload();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
acct,
|
||||
|
||||
resolve(report) {
|
||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||
reportId: report.id,
|
||||
}).then(() => {
|
||||
this.$refs.reports.removeItem(item => item.id === report.id);
|
||||
});
|
||||
resolved(reportId) {
|
||||
this.$refs.reports.removeItem(item => item.id === reportId);
|
||||
},
|
||||
}
|
||||
});
|
||||
@ -142,29 +97,4 @@ export default defineComponent({
|
||||
.lcixvhis {
|
||||
margin: var(--margin);
|
||||
}
|
||||
|
||||
.bcekxzvu {
|
||||
> .target {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
align-items: center;
|
||||
|
||||
> .avatar {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
> .info {
|
||||
margin-left: 0.3em;
|
||||
padding: 0 8px;
|
||||
flex: 1;
|
||||
|
||||
> .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -87,10 +87,6 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.ads.unshift({
|
||||
|
@ -61,10 +61,6 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.announcements.unshift({
|
||||
|
@ -1,70 +1,55 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div>
|
||||
<FormSuspense :p="init">
|
||||
<FormRadios v-model="provider">
|
||||
<template #desc><i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}</template>
|
||||
<option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
</FormRadios>
|
||||
<div class="_formRoot">
|
||||
<FormRadios v-model="provider" class="_formBlock">
|
||||
<option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
</FormRadios>
|
||||
|
||||
<template v-if="provider === 'hcaptcha'">
|
||||
<div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
|
||||
<div class="_debobigegoLabel">hCaptcha</div>
|
||||
<div class="main">
|
||||
<FormInput v-model="hcaptchaSiteKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<span>{{ $ts.hcaptchaSiteKey }}</span>
|
||||
</FormInput>
|
||||
<FormInput v-model="hcaptchaSecretKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<span>{{ $ts.hcaptchaSecretKey }}</span>
|
||||
</FormInput>
|
||||
</div>
|
||||
</div>
|
||||
<div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
|
||||
<div class="_debobigegoLabel">{{ $ts.preview }}</div>
|
||||
<div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
|
||||
<template v-if="provider === 'hcaptcha'">
|
||||
<FormInput v-model="hcaptchaSiteKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>{{ $ts.hcaptchaSiteKey }}</template>
|
||||
</FormInput>
|
||||
<FormInput v-model="hcaptchaSecretKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>{{ $ts.hcaptchaSecretKey }}</template>
|
||||
</FormInput>
|
||||
<FormSlot class="_formBlock">
|
||||
<template #label>{{ $ts.preview }}</template>
|
||||
<MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="provider === 'recaptcha'">
|
||||
<div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
|
||||
<div class="_debobigegoLabel">reCAPTCHA</div>
|
||||
<div class="main">
|
||||
<FormInput v-model="recaptchaSiteKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<span>{{ $ts.recaptchaSiteKey }}</span>
|
||||
</FormInput>
|
||||
<FormInput v-model="recaptchaSecretKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<span>{{ $ts.recaptchaSecretKey }}</span>
|
||||
</FormInput>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="recaptchaSiteKey" v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
|
||||
<div class="_debobigegoLabel">{{ $ts.preview }}</div>
|
||||
<div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
|
||||
</FormSlot>
|
||||
</template>
|
||||
<template v-else-if="provider === 'recaptcha'">
|
||||
<FormInput v-model="recaptchaSiteKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>{{ $ts.recaptchaSiteKey }}</template>
|
||||
</FormInput>
|
||||
<FormInput v-model="recaptchaSecretKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>{{ $ts.recaptchaSecretKey }}</template>
|
||||
</FormInput>
|
||||
<FormSlot v-if="recaptchaSiteKey" class="_formBlock">
|
||||
<template #label>{{ $ts.preview }}</template>
|
||||
<MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FormSlot>
|
||||
</template>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormRadios from '@/components/debobigego/radios.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
@ -73,11 +58,9 @@ export default defineComponent({
|
||||
components: {
|
||||
FormRadios,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
FormInfo,
|
||||
FormSuspense,
|
||||
FormSlot,
|
||||
MkCaptcha: defineAsyncComponent(() => import('@/components/captcha.vue')),
|
||||
},
|
||||
|
||||
@ -99,10 +82,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -1,28 +1,18 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
|
||||
<FormGroup v-for="table in database" :key="table[0]">
|
||||
<template #label>{{ table[0] }}</template>
|
||||
<FormKeyValueView>
|
||||
<template #key>Size</template>
|
||||
<template #value>{{ bytes(table[1].size) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>Records</template>
|
||||
<template #value>{{ number(table[1].count) }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
|
||||
<template #key>{{ table[0] }}</template>
|
||||
<template #value>{{ bytes(table[1].size) }} ({{ number(table[1].count) }} recs)</template>
|
||||
</MkKeyValue>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import bytes from '@/filters/bytes';
|
||||
@ -31,10 +21,7 @@ import number from '@/filters/number';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSuspense,
|
||||
FormKeyValueView,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
MkKeyValue,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -50,10 +37,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
bytes, number,
|
||||
}
|
||||
|
@ -93,10 +93,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -95,7 +95,7 @@ export default defineComponent({
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
os.api('admin/emoji/remove', {
|
||||
os.api('admin/emoji/delete', {
|
||||
id: this.emoji.id
|
||||
}).then(() => {
|
||||
this.$emit('done', {
|
||||
|
@ -6,11 +6,22 @@
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.search }}</template>
|
||||
</MkInput>
|
||||
<MkPagination ref="emojis" :pagination="pagination">
|
||||
<MkSwitch v-model="selectMode" style="margin: 8px 0;">
|
||||
<template #label>Select mode</template>
|
||||
</MkSwitch>
|
||||
<div v-if="selectMode" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
|
||||
<MkButton inline @click="selectAll">Select all</MkButton>
|
||||
<MkButton inline @click="setCategoryBulk">Set category</MkButton>
|
||||
<MkButton inline @click="addTagBulk">Add tag</MkButton>
|
||||
<MkButton inline @click="removeTagBulk">Remove tag</MkButton>
|
||||
<MkButton inline @click="setTagBulk">Set tag</MkButton>
|
||||
<MkButton inline danger @click="delBulk">Delete</MkButton>
|
||||
</div>
|
||||
<MkPagination ref="emojisPaginationComponent" :pagination="pagination">
|
||||
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
|
||||
<template v-slot="{items}">
|
||||
<div class="ldhfsamy">
|
||||
<button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="edit(emoji)">
|
||||
<button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||
<div class="body">
|
||||
<div class="name _monospace">{{ emoji.name }}</div>
|
||||
@ -32,7 +43,7 @@
|
||||
<template #label>{{ $ts.host }}</template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
<MkPagination ref="remoteEmojis" :pagination="remotePagination">
|
||||
<MkPagination :pagination="remotePagination">
|
||||
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
|
||||
<template v-slot="{items}">
|
||||
<div class="ldhfsamy">
|
||||
@ -51,148 +62,233 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineComponent, ref, toRef } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import MkTab from '@/components/tab.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import { selectFiles } from '@/scripts/select-file';
|
||||
import { selectFile, selectFiles } from '@/scripts/select-file';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkTab,
|
||||
MkButton,
|
||||
MkInput,
|
||||
MkPagination,
|
||||
FormSplit,
|
||||
},
|
||||
const emojisPaginationComponent = ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
emits: ['info'],
|
||||
const tab = ref('local');
|
||||
const query = ref(null);
|
||||
const queryRemote = ref(null);
|
||||
const host = ref(null);
|
||||
const selectMode = ref(false);
|
||||
const selectedEmojis = ref<string[]>([]);
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: this.$ts.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.addEmoji,
|
||||
handler: this.add,
|
||||
}, {
|
||||
icon: 'fas fa-ellipsis-h',
|
||||
handler: this.menu,
|
||||
}],
|
||||
tabs: [{
|
||||
active: this.tab === 'local',
|
||||
title: this.$ts.local,
|
||||
onClick: () => { this.tab = 'local'; },
|
||||
}, {
|
||||
active: this.tab === 'remote',
|
||||
title: this.$ts.remote,
|
||||
onClick: () => { this.tab = 'remote'; },
|
||||
},]
|
||||
})),
|
||||
tab: 'local',
|
||||
query: null,
|
||||
queryRemote: null,
|
||||
host: '',
|
||||
pagination: {
|
||||
endpoint: 'admin/emoji/list',
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (this.query && this.query !== '') ? this.query : null
|
||||
}))
|
||||
},
|
||||
remotePagination: {
|
||||
endpoint: 'admin/emoji/list-remote',
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (this.queryRemote && this.queryRemote !== '') ? this.queryRemote : null,
|
||||
host: (this.host && this.host !== '') ? this.host : null
|
||||
}))
|
||||
},
|
||||
}
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'admin/emoji/list' as const,
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (query.value && query.value !== '') ? query.value : null,
|
||||
})),
|
||||
};
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', toRef(this, symbols.PAGE_INFO));
|
||||
},
|
||||
const remotePagination = {
|
||||
endpoint: 'admin/emoji/list-remote' as const,
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
|
||||
host: (host.value && host.value !== '') ? host.value : null,
|
||||
})),
|
||||
};
|
||||
|
||||
methods: {
|
||||
async add(e) {
|
||||
const files = await selectFiles(e.currentTarget || e.target, null);
|
||||
|
||||
const promise = Promise.all(files.map(file => os.api('admin/emoji/add', {
|
||||
fileId: file.id,
|
||||
})));
|
||||
promise.then(() => {
|
||||
this.$refs.emojis.reload();
|
||||
});
|
||||
os.promiseDialog(promise);
|
||||
},
|
||||
|
||||
edit(emoji) {
|
||||
os.popup(import('./emoji-edit-dialog.vue'), {
|
||||
emoji: emoji
|
||||
}, {
|
||||
done: result => {
|
||||
if (result.updated) {
|
||||
this.$refs.emojis.replaceItem(item => item.id === emoji.id, {
|
||||
...emoji,
|
||||
...result.updated
|
||||
});
|
||||
} else if (result.deleted) {
|
||||
this.$refs.emojis.removeItem(item => item.id === emoji.id);
|
||||
}
|
||||
},
|
||||
}, 'closed');
|
||||
},
|
||||
|
||||
im(emoji) {
|
||||
os.apiWithDialog('admin/emoji/copy', {
|
||||
emojiId: emoji.id,
|
||||
});
|
||||
},
|
||||
|
||||
remoteMenu(emoji, ev) {
|
||||
os.popupMenu([{
|
||||
type: 'label',
|
||||
text: ':' + emoji.name + ':',
|
||||
}, {
|
||||
text: this.$ts.import,
|
||||
icon: 'fas fa-plus',
|
||||
action: () => { this.im(emoji) }
|
||||
}], ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
menu(ev) {
|
||||
os.popupMenu([{
|
||||
icon: 'fas fa-download',
|
||||
text: this.$ts.export,
|
||||
action: async () => {
|
||||
os.api('export-custom-emojis', {
|
||||
})
|
||||
.then(() => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: this.$ts.exportRequested,
|
||||
});
|
||||
}).catch((e) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
}
|
||||
const selectAll = () => {
|
||||
if (selectedEmojis.value.length > 0) {
|
||||
selectedEmojis.value = [];
|
||||
} else {
|
||||
selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSelect = (emoji) => {
|
||||
if (selectedEmojis.value.includes(emoji.id)) {
|
||||
selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id);
|
||||
} else {
|
||||
selectedEmojis.value.push(emoji.id);
|
||||
}
|
||||
};
|
||||
|
||||
const add = async (ev: MouseEvent) => {
|
||||
const files = await selectFiles(ev.currentTarget || ev.target, null);
|
||||
|
||||
const promise = Promise.all(files.map(file => os.api('admin/emoji/add', {
|
||||
fileId: file.id,
|
||||
})));
|
||||
promise.then(() => {
|
||||
emojisPaginationComponent.value.reload();
|
||||
});
|
||||
os.promiseDialog(promise);
|
||||
};
|
||||
|
||||
const edit = (emoji) => {
|
||||
os.popup(import('./emoji-edit-dialog.vue'), {
|
||||
emoji: emoji
|
||||
}, {
|
||||
done: result => {
|
||||
if (result.updated) {
|
||||
emojisPaginationComponent.value.replaceItem(item => item.id === emoji.id, {
|
||||
...emoji,
|
||||
...result.updated
|
||||
});
|
||||
} else if (result.deleted) {
|
||||
emojisPaginationComponent.value.removeItem(item => item.id === emoji.id);
|
||||
}
|
||||
},
|
||||
}, 'closed');
|
||||
};
|
||||
|
||||
const im = (emoji) => {
|
||||
os.apiWithDialog('admin/emoji/copy', {
|
||||
emojiId: emoji.id,
|
||||
});
|
||||
};
|
||||
|
||||
const remoteMenu = (emoji, ev: MouseEvent) => {
|
||||
os.popupMenu([{
|
||||
type: 'label',
|
||||
text: ':' + emoji.name + ':',
|
||||
}, {
|
||||
text: i18n.locale.import,
|
||||
icon: 'fas fa-plus',
|
||||
action: () => { im(emoji) }
|
||||
}], ev.currentTarget || ev.target);
|
||||
};
|
||||
|
||||
const menu = (ev: MouseEvent) => {
|
||||
os.popupMenu([{
|
||||
icon: 'fas fa-download',
|
||||
text: i18n.locale.export,
|
||||
action: async () => {
|
||||
os.api('export-custom-emojis', {
|
||||
})
|
||||
.then(() => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: i18n.locale.exportRequested,
|
||||
});
|
||||
}).catch((e) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, {
|
||||
icon: 'fas fa-upload',
|
||||
text: i18n.locale.import,
|
||||
action: async () => {
|
||||
const file = await selectFile(ev.currentTarget || ev.target);
|
||||
os.api('admin/emoji/import-zip', {
|
||||
fileId: file.id,
|
||||
})
|
||||
.then(() => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: i18n.locale.importRequested,
|
||||
});
|
||||
}).catch((e) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
};
|
||||
|
||||
const setCategoryBulk = async () => {
|
||||
const { canceled, result } = await os.inputText({
|
||||
title: 'Category',
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('admin/emoji/set-category-bulk', {
|
||||
ids: selectedEmojis.value,
|
||||
category: result,
|
||||
});
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
const addTagBulk = async () => {
|
||||
const { canceled, result } = await os.inputText({
|
||||
title: 'Tag',
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('admin/emoji/add-aliases-bulk', {
|
||||
ids: selectedEmojis.value,
|
||||
aliases: result.split(' '),
|
||||
});
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
const removeTagBulk = async () => {
|
||||
const { canceled, result } = await os.inputText({
|
||||
title: 'Tag',
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('admin/emoji/remove-aliases-bulk', {
|
||||
ids: selectedEmojis.value,
|
||||
aliases: result.split(' '),
|
||||
});
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
const setTagBulk = async () => {
|
||||
const { canceled, result } = await os.inputText({
|
||||
title: 'Tag',
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('admin/emoji/set-aliases-bulk', {
|
||||
ids: selectedEmojis.value,
|
||||
aliases: result.split(' '),
|
||||
});
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
const delBulk = async () => {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.locale.deleteConfirm,
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('admin/emoji/delete-bulk', {
|
||||
ids: selectedEmojis.value,
|
||||
});
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: i18n.locale.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: i18n.locale.addEmoji,
|
||||
handler: add,
|
||||
}, {
|
||||
icon: 'fas fa-ellipsis-h',
|
||||
handler: menu,
|
||||
}],
|
||||
tabs: [{
|
||||
active: tab.value === 'local',
|
||||
title: i18n.locale.local,
|
||||
onClick: () => { tab.value = 'local'; },
|
||||
}, {
|
||||
active: tab.value === 'remote',
|
||||
title: i18n.locale.remote,
|
||||
onClick: () => { tab.value = 'remote'; },
|
||||
},]
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -212,11 +308,16 @@ export default defineComponent({
|
||||
> .emoji {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
padding: 11px;
|
||||
text-align: left;
|
||||
border: solid 1px var(--panel);
|
||||
|
||||
&:hover {
|
||||
color: var(--accent);
|
||||
border-color: var(--inputBorderHover);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
> .img {
|
||||
|
@ -19,7 +19,7 @@
|
||||
<option value="local">{{ $ts.local }}</option>
|
||||
<option value="remote">{{ $ts.remote }}</option>
|
||||
</MkSelect>
|
||||
<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params().origin === 'local'">
|
||||
<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'">
|
||||
<template #label>{{ $ts.host }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
@ -55,7 +55,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
@ -95,33 +95,17 @@ export default defineComponent({
|
||||
type: null,
|
||||
searchHost: '',
|
||||
pagination: {
|
||||
endpoint: 'admin/drive/files',
|
||||
endpoint: 'admin/drive/files' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
type: (this.type && this.type !== '') ? this.type : null,
|
||||
origin: this.origin,
|
||||
hostname: (this.hostname && this.hostname !== '') ? this.hostname : null,
|
||||
}),
|
||||
hostname: (this.searchHost && this.searchHost !== '') ? this.searchHost : null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
type() {
|
||||
this.$refs.files.reload();
|
||||
},
|
||||
origin() {
|
||||
this.$refs.files.reload();
|
||||
},
|
||||
searchHost() {
|
||||
this.$refs.files.reload();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear() {
|
||||
os.confirm({
|
||||
|
@ -10,7 +10,7 @@
|
||||
</div>
|
||||
|
||||
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
|
||||
|
||||
<MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
|
||||
</div>
|
||||
@ -19,7 +19,7 @@
|
||||
<div class="main">
|
||||
<MkStickyContainer>
|
||||
<template #header><MkHeader v-if="childInfo && !childInfo.hideHeader" :info="childInfo"/></template>
|
||||
<component :is="component" :key="page" v-bind="pageProps" @info="onInfo"/>
|
||||
<component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
|
||||
</MkStickyContainer>
|
||||
</div>
|
||||
</div>
|
||||
@ -29,9 +29,6 @@
|
||||
import { computed, defineAsyncComponent, defineComponent, isRef, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import MkSuperMenu from '@/components/ui/super-menu.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import MkInfo from '@/components/ui/info.vue';
|
||||
import { scroll } from '@/scripts/scroll';
|
||||
import { instance } from '@/instance';
|
||||
@ -41,10 +38,7 @@ import { lookupUser } from '@/scripts/lookup-user';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
MkSuperMenu,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
MkInfo,
|
||||
},
|
||||
|
||||
@ -72,7 +66,9 @@ export default defineComponent({
|
||||
const narrow = ref(false);
|
||||
const view = ref(null);
|
||||
const el = ref(null);
|
||||
const onInfo = (viewInfo) => {
|
||||
const pageChanged = (page) => {
|
||||
if (page == null) return;
|
||||
const viewInfo = page[symbols.PAGE_INFO];
|
||||
if (isRef(viewInfo)) {
|
||||
watch(viewInfo, () => {
|
||||
childInfo.value = viewInfo.value;
|
||||
@ -177,11 +173,6 @@ export default defineComponent({
|
||||
text: i18n.locale.security,
|
||||
to: '/admin/security',
|
||||
active: page.value === 'security',
|
||||
}, {
|
||||
icon: 'fas fa-bolt',
|
||||
text: 'ServiceWorker',
|
||||
to: '/admin/service-worker',
|
||||
active: page.value === 'service-worker',
|
||||
}, {
|
||||
icon: 'fas fa-globe',
|
||||
text: i18n.locale.relays,
|
||||
@ -234,13 +225,8 @@ export default defineComponent({
|
||||
case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
|
||||
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
||||
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
||||
case 'bot-protection': return defineAsyncComponent(() => import('./bot-protection.vue'));
|
||||
case 'service-worker': return defineAsyncComponent(() => import('./service-worker.vue'));
|
||||
case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
|
||||
case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
|
||||
case 'integrations/twitter': return defineAsyncComponent(() => import('./integrations-twitter.vue'));
|
||||
case 'integrations/github': return defineAsyncComponent(() => import('./integrations-github.vue'));
|
||||
case 'integrations/discord': return defineAsyncComponent(() => import('./integrations-discord.vue'));
|
||||
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
|
||||
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
||||
case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
|
||||
@ -327,7 +313,7 @@ export default defineComponent({
|
||||
narrow,
|
||||
view,
|
||||
el,
|
||||
onInfo,
|
||||
pageChanged,
|
||||
childInfo,
|
||||
pageProps,
|
||||
component,
|
||||
|
@ -1,39 +1,29 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<FormTextarea v-model="blockedHosts">
|
||||
<FormTextarea v-model="blockedHosts" class="_formBlock">
|
||||
<span>{{ $ts.blockedInstances }}</span>
|
||||
<template #desc>{{ $ts.blockedInstancesDescription }}</template>
|
||||
<template #caption>{{ $ts.blockedInstancesDescription }}</template>
|
||||
</FormTextarea>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
FormTextarea,
|
||||
FormInfo,
|
||||
FormSuspense,
|
||||
},
|
||||
|
||||
@ -50,10 +40,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -1,291 +0,0 @@
|
||||
<template>
|
||||
<XModalWindow ref="dialog"
|
||||
:width="520"
|
||||
:height="500"
|
||||
@close="$refs.dialog.close()"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>{{ instance.host }}</template>
|
||||
<div class="mk-instance-info">
|
||||
<div class="_table section">
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.software }}</div>
|
||||
<div class="_data">{{ instance.softwareName || '?' }}</div>
|
||||
</div>
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.version }}</div>
|
||||
<div class="_data">{{ instance.softwareVersion || '?' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_table data section">
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.registeredAt }}</div>
|
||||
<div class="_data">{{ new Date(instance.caughtAt).toLocaleString() }} (<MkTime :time="instance.caughtAt"/>)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.following }}</div>
|
||||
<button class="_data _textButton" @click="showFollowing()">{{ number(instance.followingCount) }}</button>
|
||||
</div>
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.followers }}</div>
|
||||
<button class="_data _textButton" @click="showFollowers()">{{ number(instance.followersCount) }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.users }}</div>
|
||||
<button class="_data _textButton" @click="showUsers()">{{ number(instance.usersCount) }}</button>
|
||||
</div>
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.notes }}</div>
|
||||
<div class="_data">{{ number(instance.notesCount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.files }}</div>
|
||||
<div class="_data">{{ number(instance.driveFiles) }}</div>
|
||||
</div>
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.storageUsage }}</div>
|
||||
<div class="_data">{{ bytes(instance.driveUsage) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.latestRequestSentAt }}</div>
|
||||
<div class="_data"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.latestStatus }}</div>
|
||||
<div class="_data">{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_row">
|
||||
<div class="_cell">
|
||||
<div class="_label">{{ $ts.latestRequestReceivedAt }}</div>
|
||||
<div class="_data"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<div class="header">
|
||||
<span class="label">{{ $ts.charts }}</span>
|
||||
<div class="selects">
|
||||
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
|
||||
<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
|
||||
<option value="instance-users">{{ $ts._instanceCharts.users }}</option>
|
||||
<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
|
||||
<option value="instance-notes">{{ $ts._instanceCharts.notes }}</option>
|
||||
<option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
|
||||
<option value="instance-ff">{{ $ts._instanceCharts.ff }}</option>
|
||||
<option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
|
||||
<option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
|
||||
<option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
|
||||
<option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option>
|
||||
<option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="chartSpan" style="margin: 0;">
|
||||
<option value="hour">{{ $ts.perHour }}</option>
|
||||
<option value="day">{{ $ts.perDay }}</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operations section">
|
||||
<span class="label">{{ $ts.operations }}</span>
|
||||
<MkSwitch v-model="isSuspended" class="switch">{{ $ts.stopActivityDelivery }}</MkSwitch>
|
||||
<MkSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch>
|
||||
<details>
|
||||
<summary>{{ $ts.deleteAllFiles }}</summary>
|
||||
<MkButton style="margin: 0.5em 0 0.5em 0;" @click="deleteAllFiles()"><i class="fas fa-trash-alt"></i> {{ $ts.deleteAllFiles }}</MkButton>
|
||||
</details>
|
||||
<details>
|
||||
<summary>{{ $ts.removeAllFollowing }}</summary>
|
||||
<MkButton style="margin: 0.5em 0 0.5em 0;" @click="removeAllFollowing()"><i class="fas fa-minus-circle"></i> {{ $ts.removeAllFollowing }}</MkButton>
|
||||
<MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo>
|
||||
</details>
|
||||
</div>
|
||||
<details class="metadata section">
|
||||
<summary class="label">{{ $ts.metadata }}</summary>
|
||||
<pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre>
|
||||
</details>
|
||||
</div>
|
||||
</XModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import MkInfo from '@/components/ui/info.vue';
|
||||
import MkChart from '@/components/chart.vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
import number from '@/filters/number';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XModalWindow,
|
||||
MkSelect,
|
||||
MkButton,
|
||||
MkSwitch,
|
||||
MkInfo,
|
||||
MkChart,
|
||||
},
|
||||
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isSuspended: this.instance.isSuspended,
|
||||
chartSrc: 'requests',
|
||||
chartSpan: 'hour',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
meta() {
|
||||
return this.$instance;
|
||||
},
|
||||
|
||||
isBlocked() {
|
||||
return this.meta && this.meta.blockedHosts && this.meta.blockedHosts.includes(this.instance.host);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
isSuspended() {
|
||||
os.api('admin/federation/update-instance', {
|
||||
host: this.instance.host,
|
||||
isSuspended: this.isSuspended
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeBlock(e) {
|
||||
os.api('admin/update-meta', {
|
||||
blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
|
||||
});
|
||||
},
|
||||
|
||||
removeAllFollowing() {
|
||||
os.apiWithDialog('admin/federation/remove-all-following', {
|
||||
host: this.instance.host
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllFiles() {
|
||||
os.apiWithDialog('admin/federation/delete-all-files', {
|
||||
host: this.instance.host
|
||||
});
|
||||
},
|
||||
|
||||
showFollowing() {
|
||||
// TODO: ページ遷移
|
||||
},
|
||||
|
||||
showFollowers() {
|
||||
// TODO: ページ遷移
|
||||
},
|
||||
|
||||
showUsers() {
|
||||
// TODO: ページ遷移
|
||||
},
|
||||
|
||||
bytes,
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mk-instance-info {
|
||||
overflow: auto;
|
||||
|
||||
> .section {
|
||||
padding: 16px 32px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .chart {
|
||||
border-top: solid 0.5px var(--divider);
|
||||
padding: 16px 0 12px 0;
|
||||
|
||||
> .header {
|
||||
padding: 0 32px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
> .label {
|
||||
font-size: 80%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .selects {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
> .chart {
|
||||
padding: 0 16px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .operations {
|
||||
> .label {
|
||||
font-size: 80%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .switch {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .metadata {
|
||||
> .label {
|
||||
font-size: 80%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> pre > code {
|
||||
display: block;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,37 +1,36 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSuspense :p="init">
|
||||
<FormSwitch v-model="enableDiscordIntegration">
|
||||
{{ $ts.enable }}
|
||||
<FormSuspense :p="init">
|
||||
<div class="_formRoot">
|
||||
<FormSwitch v-model="enableDiscordIntegration" class="_formBlock">
|
||||
<template #label>{{ $ts.enable }}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<template v-if="enableDiscordIntegration">
|
||||
<FormInfo>Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo>
|
||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo>
|
||||
|
||||
<FormInput v-model="discordClientId">
|
||||
<FormInput v-model="discordClientId" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Client ID
|
||||
<template #label>Client ID</template>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="discordClientSecret">
|
||||
<FormInput v-model="discordClientSecret" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Client Secret
|
||||
<template #label>Client Secret</template>
|
||||
</FormInput>
|
||||
</template>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
@ -40,7 +39,6 @@ export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormInfo,
|
||||
FormButton,
|
||||
FormSuspense,
|
||||
@ -60,10 +58,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
@ -1,37 +1,36 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSuspense :p="init">
|
||||
<FormSwitch v-model="enableGithubIntegration">
|
||||
{{ $ts.enable }}
|
||||
<FormSuspense :p="init">
|
||||
<div class="_formRoot">
|
||||
<FormSwitch v-model="enableGithubIntegration" class="_formBlock">
|
||||
<template #label>{{ $ts.enable }}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<template v-if="enableGithubIntegration">
|
||||
<FormInfo>Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo>
|
||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo>
|
||||
|
||||
<FormInput v-model="githubClientId">
|
||||
<FormInput v-model="githubClientId" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Client ID
|
||||
<template #label>Client ID</template>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="githubClientSecret">
|
||||
<FormInput v-model="githubClientSecret" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Client Secret
|
||||
<template #label>Client Secret</template>
|
||||
</FormInput>
|
||||
</template>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
@ -40,7 +39,6 @@ export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormInfo,
|
||||
FormButton,
|
||||
FormSuspense,
|
||||
@ -60,10 +58,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
@ -1,37 +1,36 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSuspense :p="init">
|
||||
<FormSwitch v-model="enableTwitterIntegration">
|
||||
{{ $ts.enable }}
|
||||
<FormSuspense :p="init">
|
||||
<div class="_formRoot">
|
||||
<FormSwitch v-model="enableTwitterIntegration" class="_formBlock">
|
||||
<template #label>{{ $ts.enable }}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<template v-if="enableTwitterIntegration">
|
||||
<FormInfo>Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
|
||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
|
||||
|
||||
<FormInput v-model="twitterConsumerKey">
|
||||
<FormInput v-model="twitterConsumerKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Consumer Key
|
||||
<template #label>Consumer Key</template>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="twitterConsumerSecret">
|
||||
<FormInput v-model="twitterConsumerSecret" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Consumer Secret
|
||||
<template #label>Consumer Secret</template>
|
||||
</FormInput>
|
||||
</template>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
@ -40,7 +39,6 @@ export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormInfo,
|
||||
FormButton,
|
||||
FormSuspense,
|
||||
@ -60,10 +58,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
@ -1,46 +1,48 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<FormLink to="/admin/integrations/twitter">
|
||||
<i class="fab fa-twitter"></i> Twitter
|
||||
<FormFolder class="_formBlock">
|
||||
<template #icon><i class="fab fa-twitter"></i></template>
|
||||
<template #label>Twitter</template>
|
||||
<template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template>
|
||||
</FormLink>
|
||||
<FormLink to="/admin/integrations/github">
|
||||
<i class="fab fa-github"></i> GitHub
|
||||
<XTwitter/>
|
||||
</FormFolder>
|
||||
<FormFolder to="/admin/integrations/github" class="_formBlock">
|
||||
<template #icon><i class="fab fa-github"></i></template>
|
||||
<template #label>GitHub</template>
|
||||
<template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template>
|
||||
</FormLink>
|
||||
<FormLink to="/admin/integrations/discord">
|
||||
<i class="fab fa-discord"></i> Discord
|
||||
<XGithub/>
|
||||
</FormFolder>
|
||||
<FormFolder to="/admin/integrations/discord" class="_formBlock">
|
||||
<template #icon><i class="fab fa-discord"></i></template>
|
||||
<template #label>Discord</template>
|
||||
<template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template>
|
||||
</FormLink>
|
||||
<XDiscord/>
|
||||
</FormFolder>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormFolder from '@/components/form/folder.vue';
|
||||
import FormSecion from '@/components/form/section.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import XTwitter from './integrations.twitter.vue';
|
||||
import XGithub from './integrations.github.vue';
|
||||
import XDiscord from './integrations.discord.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormLink,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
FormTextarea,
|
||||
FormInfo,
|
||||
FormFolder,
|
||||
FormSecion,
|
||||
FormSuspense,
|
||||
XTwitter,
|
||||
XGithub,
|
||||
XDiscord,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -58,10 +60,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -76,7 +76,6 @@ import MkwFederation from '../../widgets/federation.vue';
|
||||
import { version, url } from '@/config';
|
||||
import bytes from '@/filters/bytes';
|
||||
import number from '@/filters/number';
|
||||
import MkInstanceInfo from './instance.vue';
|
||||
|
||||
Chart.register(
|
||||
ArcElement,
|
||||
|
@ -118,10 +118,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -1,34 +1,17 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<FormGroup>
|
||||
<FormInput v-model="summalyProxy">
|
||||
<template #prefix><i class="fas fa-link"></i></template>
|
||||
Summaly Proxy URL
|
||||
</FormInput>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormInput v-model="deeplAuthKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
DeepL Auth Key
|
||||
</FormInput>
|
||||
<FormSwitch v-model="deeplIsPro">
|
||||
Pro account
|
||||
</FormSwitch>
|
||||
</FormGroup>
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
none
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
@ -37,9 +20,7 @@ export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
FormSection,
|
||||
FormSuspense,
|
||||
},
|
||||
|
||||
@ -51,29 +32,22 @@ export default defineComponent({
|
||||
title: this.$ts.other,
|
||||
icon: 'fas fa-cogs',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-check',
|
||||
text: this.$ts.save,
|
||||
handler: this.save,
|
||||
}],
|
||||
},
|
||||
summalyProxy: '',
|
||||
deeplAuthKey: '',
|
||||
deeplIsPro: false,
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
this.summalyProxy = meta.summalyProxy;
|
||||
this.deeplAuthKey = meta.deeplAuthKey;
|
||||
this.deeplIsPro = meta.deeplIsPro;
|
||||
},
|
||||
save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
summalyProxy: this.summalyProxy,
|
||||
deeplAuthKey: this.deeplAuthKey,
|
||||
deeplIsPro: this.deeplIsPro,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
<MkContainer :foldable="true" class="charts">
|
||||
<template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template>
|
||||
<div style="padding-top: 12px;">
|
||||
<div style="padding: 12px;">
|
||||
<MkInstanceStats :chart-limit="500" :detailed="true"/>
|
||||
</div>
|
||||
</MkContainer>
|
||||
@ -67,7 +67,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, markRaw, version as vueVersion } from 'vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import MkInstanceStats from '@/components/instance-stats.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
@ -78,7 +77,6 @@ import MkQueueChart from '@/components/queue-chart.vue';
|
||||
import { version, url } from '@/config';
|
||||
import bytes from '@/filters/bytes';
|
||||
import number from '@/filters/number';
|
||||
import MkInstanceInfo from './instance.vue';
|
||||
import XMetrics from './metrics.vue';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
@ -87,7 +85,6 @@ import * as symbols from '@/symbols';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkNumberDiff,
|
||||
FormKeyValueView,
|
||||
MkInstanceStats,
|
||||
MkContainer,
|
||||
MkFolder,
|
||||
@ -119,8 +116,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
@ -161,9 +156,7 @@ export default defineComponent({
|
||||
host: q
|
||||
});
|
||||
}
|
||||
os.popup(MkInstanceInfo, {
|
||||
instance: instance
|
||||
}, {}, 'closed');
|
||||
// TODO
|
||||
},
|
||||
|
||||
bytes,
|
||||
|
@ -1,42 +1,32 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.proxyAccount }}</template>
|
||||
<template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
|
||||
</FormKeyValueView>
|
||||
<template #caption>{{ $ts.proxyAccountDescription }}</template>
|
||||
</FormGroup>
|
||||
<MkInfo class="_formBlock">{{ $ts.proxyAccountDescription }}</MkInfo>
|
||||
<MkKeyValue class="_formBlock">
|
||||
<template #key>{{ $ts.proxyAccount }}</template>
|
||||
<template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
|
||||
</MkKeyValue>
|
||||
|
||||
<FormButton primary @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
|
||||
<FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import MkInfo from '@/components/ui/info.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormKeyValueView,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
MkKeyValue,
|
||||
FormButton,
|
||||
FormTextarea,
|
||||
FormInfo,
|
||||
MkInfo,
|
||||
FormSuspense,
|
||||
},
|
||||
|
||||
@ -54,10 +44,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
|
@ -1,29 +1,25 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<MkSpacer :content-max="800">
|
||||
<XQueue :connection="connection" domain="inbox">
|
||||
<template #title>In</template>
|
||||
</XQueue>
|
||||
<XQueue :connection="connection" domain="deliver">
|
||||
<template #title>Out</template>
|
||||
</XQueue>
|
||||
<FormButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</FormButton>
|
||||
</FormBase>
|
||||
<MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import XQueue from './queue.chart.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormButton,
|
||||
MkButton,
|
||||
XQueue,
|
||||
},
|
||||
@ -42,8 +38,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.connection.send('requestLog', {
|
||||
id: Math.random().toString().substr(2, 8),
|
||||
|
@ -1,32 +1,27 @@
|
||||
<template>
|
||||
<FormBase class="relaycxt">
|
||||
<FormButton primary @click="addRelay"><i class="fas fa-plus"></i> {{ $ts.addRelay }}</FormButton>
|
||||
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="_debobigegoItem">
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div>{{ $t(`_relayStatus.${relay.status}`) }}</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
<MkSpacer :content-max="800">
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel _block" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div class="status">
|
||||
<i v-if="relay.status === 'accepted'" class="fas fa-check icon accepted"></i>
|
||||
<i v-else-if="relay.status === 'rejected'" class="fas fa-ban icon rejected"></i>
|
||||
<i v-else class="fas fa-clock icon requesting"></i>
|
||||
<span>{{ $t(`_relayStatus.${relay.status}`) }}</span>
|
||||
</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
</div>
|
||||
</FormBase>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormButton,
|
||||
MkButton,
|
||||
MkInput,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -37,6 +32,12 @@ export default defineComponent({
|
||||
title: this.$ts.relays,
|
||||
icon: 'fas fa-globe',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.addRelay,
|
||||
handler: this.addRelay,
|
||||
}],
|
||||
},
|
||||
relays: [],
|
||||
inbox: '',
|
||||
@ -47,10 +48,6 @@ export default defineComponent({
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async addRelay() {
|
||||
const { canceled, result: inbox } = await os.inputText({
|
||||
@ -94,5 +91,22 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.relaycxt {
|
||||
> .status {
|
||||
margin: 8px 0;
|
||||
|
||||
> .icon {
|
||||
width: 1em;
|
||||
margin-right: 0.75em;
|
||||
|
||||
&.accepted {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
&.rejected {
|
||||
color: var(--error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -2,12 +2,28 @@
|
||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<div class="_formRoot">
|
||||
<FormLink to="/admin/bot-protection" class="_formBlock">
|
||||
<i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
|
||||
<FormFolder class="_formBlock">
|
||||
<template #icon><i class="fas fa-shield-alt"></i></template>
|
||||
<template #label>{{ $ts.botProtection }}</template>
|
||||
<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
|
||||
<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
|
||||
<template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
|
||||
</FormLink>
|
||||
|
||||
<XBotProtection/>
|
||||
</FormFolder>
|
||||
|
||||
<FormFolder class="_formBlock">
|
||||
<template #label>Summaly Proxy</template>
|
||||
|
||||
<div class="_formRoot">
|
||||
<FormInput v-model="summalyProxy" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-link"></i></template>
|
||||
<template #label>Summaly Proxy URL</template>
|
||||
</FormInput>
|
||||
|
||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</div>
|
||||
</FormFolder>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</MkSpacer>
|
||||
@ -15,22 +31,28 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormFolder from '@/components/form/folder.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import XBotProtection from './bot-protection.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormLink,
|
||||
FormFolder,
|
||||
FormSwitch,
|
||||
FormInfo,
|
||||
FormSection,
|
||||
FormSuspense,
|
||||
FormButton,
|
||||
FormInput,
|
||||
XBotProtection,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -42,21 +64,27 @@ export default defineComponent({
|
||||
icon: 'fas fa-lock',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
summalyProxy: '',
|
||||
enableHcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
this.summalyProxy = meta.summalyProxy;
|
||||
this.enableHcaptcha = meta.enableHcaptcha;
|
||||
this.enableRecaptcha = meta.enableRecaptcha;
|
||||
},
|
||||
|
||||
save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
summalyProxy: this.summalyProxy,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSuspense :p="init">
|
||||
<FormSwitch v-model="enableServiceWorker">
|
||||
{{ $ts.enableServiceworker }}
|
||||
<template #desc>{{ $ts.serviceworkerInfo }}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<template v-if="enableServiceWorker">
|
||||
<FormInput v-model="swPublicKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Public key
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="swPrivateKey">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
Private key
|
||||
</FormInput>
|
||||
</template>
|
||||
|
||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { fetchInstance } from '@/instance';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
FormSuspense,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: 'ServiceWorker',
|
||||
icon: 'fas fa-bolt',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
enableServiceWorker: false,
|
||||
swPublicKey: null,
|
||||
swPrivateKey: null,
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
this.enableServiceWorker = meta.enableServiceWorker;
|
||||
this.swPublicKey = meta.swPublickey;
|
||||
this.swPrivateKey = meta.swPrivateKey;
|
||||
},
|
||||
save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableServiceWorker: this.enableServiceWorker,
|
||||
swPublicKey: this.swPublicKey,
|
||||
swPrivateKey: this.swPrivateKey,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -94,6 +94,39 @@
|
||||
</FormInput>
|
||||
</FormSplit>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<template #label>ServiceWorker</template>
|
||||
|
||||
<FormSwitch v-model="enableServiceWorker" class="_formBlock">
|
||||
<template #label>{{ $ts.enableServiceworker }}</template>
|
||||
<template #caption>{{ $ts.serviceworkerInfo }}</template>
|
||||
</FormSwitch>
|
||||
|
||||
<template v-if="enableServiceWorker">
|
||||
<FormInput v-model="swPublicKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>Public key</template>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="swPrivateKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>Private key</template>
|
||||
</FormInput>
|
||||
</template>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<template #label>DeepL Translation</template>
|
||||
|
||||
<FormInput v-model="deeplAuthKey" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-key"></i></template>
|
||||
<template #label>DeepL Auth Key</template>
|
||||
</FormInput>
|
||||
<FormSwitch v-model="deeplIsPro" class="_formBlock">
|
||||
<template #label>Pro account</template>
|
||||
</FormSwitch>
|
||||
</FormSection>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</MkSpacer>
|
||||
@ -156,13 +189,14 @@ export default defineComponent({
|
||||
remoteDriveCapacityMb: 0,
|
||||
enableRegistration: false,
|
||||
emailRequiredForSignup: false,
|
||||
enableServiceWorker: false,
|
||||
swPublicKey: null,
|
||||
swPrivateKey: null,
|
||||
deeplAuthKey: '',
|
||||
deeplIsPro: false,
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
const meta = await os.api('meta', { detail: true });
|
||||
@ -184,6 +218,11 @@ export default defineComponent({
|
||||
this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
|
||||
this.enableRegistration = !meta.disableRegistration;
|
||||
this.emailRequiredForSignup = meta.emailRequiredForSignup;
|
||||
this.enableServiceWorker = meta.enableServiceWorker;
|
||||
this.swPublicKey = meta.swPublickey;
|
||||
this.swPrivateKey = meta.swPrivateKey;
|
||||
this.deeplAuthKey = meta.deeplAuthKey;
|
||||
this.deeplIsPro = meta.deeplIsPro;
|
||||
},
|
||||
|
||||
save() {
|
||||
@ -206,6 +245,11 @@ export default defineComponent({
|
||||
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
|
||||
disableRegistration: !this.enableRegistration,
|
||||
emailRequiredForSignup: this.emailRequiredForSignup,
|
||||
enableServiceWorker: this.enableServiceWorker,
|
||||
swPublicKey: this.swPublicKey,
|
||||
swPrivateKey: this.swPrivateKey,
|
||||
deeplAuthKey: this.deeplAuthKey,
|
||||
deeplIsPro: this.deeplIsPro,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -30,7 +30,7 @@
|
||||
<template #prefix>@</template>
|
||||
<template #label>{{ $ts.username }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" :disabled="pagination.params().origin === 'local'" @update:modelValue="$refs.users.reload()">
|
||||
<MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:modelValue="$refs.users.reload()">
|
||||
<template #prefix>@</template>
|
||||
<template #label>{{ $ts.host }}</template>
|
||||
</MkInput>
|
||||
@ -62,7 +62,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
@ -110,36 +110,20 @@ export default defineComponent({
|
||||
searchUsername: '',
|
||||
searchHost: '',
|
||||
pagination: {
|
||||
endpoint: 'admin/show-users',
|
||||
endpoint: 'admin/show-users' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
sort: this.sort,
|
||||
state: this.state,
|
||||
origin: this.origin,
|
||||
username: this.searchUsername,
|
||||
hostname: this.searchHost,
|
||||
}),
|
||||
})),
|
||||
offsetMode: true
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
sort() {
|
||||
this.$refs.users.reload();
|
||||
},
|
||||
state() {
|
||||
this.$refs.users.reload();
|
||||
},
|
||||
origin() {
|
||||
this.$refs.users.reload();
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
lookupUser,
|
||||
|
||||
|
@ -1,349 +0,0 @@
|
||||
<template>
|
||||
<div class="t9makv94">
|
||||
<section class="_section">
|
||||
<div class="_content">
|
||||
<details>
|
||||
<summary>{{ $ts.import }}</summary>
|
||||
<MkTextarea v-model="themeToImport">
|
||||
{{ $ts._theme.importInfo }}
|
||||
</MkTextarea>
|
||||
<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
<section class="_section">
|
||||
<div class="_content _card _gap">
|
||||
<div class="_content">
|
||||
<MkInput v-model="name" required><span>{{ $ts.name }}</span></MkInput>
|
||||
<MkInput v-model="author" required><span>{{ $ts.author }}</span></MkInput>
|
||||
<MkTextarea v-model="description"><span>{{ $ts.description }}</span></MkTextarea>
|
||||
<div class="_inputs">
|
||||
<div v-text="$ts._theme.base" />
|
||||
<MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio>
|
||||
<MkRadio v-model="baseTheme" value="dark">{{ $ts.dark }}</MkRadio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_content _card _gap">
|
||||
<div class="list-view _content">
|
||||
<div v-for="([ k, v ], i) in theme" :key="k" class="item">
|
||||
<div class="_inputs">
|
||||
<div>
|
||||
{{ k.startsWith('$') ? `${k} (${$ts._theme.constant})` : $t('_theme.keys.' + k) }}
|
||||
<button v-if="k.startsWith('$')" class="_button _link" @click="del(i)" v-text="$ts.delete" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="type" @click="chooseType($event, i)">
|
||||
{{ getTypeOf(v) }} <i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
<!-- default -->
|
||||
<div v-if="v === null" class="default-value" v-text="baseProps[k]" />
|
||||
<!-- color -->
|
||||
<div v-else-if="typeof v === 'string'" class="color">
|
||||
<input type="color" :value="v" @input="colorChanged($event.target.value, i)"/>
|
||||
<MkInput class="select" :value="v" @update:modelValue="colorChanged($event, i)"/>
|
||||
</div>
|
||||
<!-- ref const -->
|
||||
<MkInput v-else-if="v.type === 'refConst'" v-model="v.key">
|
||||
<template #prefix>$</template>
|
||||
<span>{{ $ts.name }}</span>
|
||||
</MkInput>
|
||||
<!-- ref props -->
|
||||
<MkSelect v-else-if="v.type === 'refProp'" v-model="v.key" class="select">
|
||||
<option v-for="key in themeProps" :key="key" :value="key">{{ $t('_theme.keys.' + key) }}</option>
|
||||
</MkSelect>
|
||||
<!-- func -->
|
||||
<template v-else-if="v.type === 'func'">
|
||||
<MkSelect v-model="v.name" class="select">
|
||||
<template #label>{{ $ts._theme.funcKind }}</template>
|
||||
<option v-for="n in ['alpha', 'darken', 'lighten']" :key="n" :value="n">{{ $t('_theme.' + n) }}</option>
|
||||
</MkSelect>
|
||||
<MkInput v-model="v.arg" type="number"><span>{{ $ts._theme.argument }}</span></MkInput>
|
||||
<MkSelect v-model="v.value" class="select">
|
||||
<template #label>{{ $ts._theme.basedProp }}</template>
|
||||
<option v-for="key in themeProps" :key="key" :value="key">{{ $t('_theme.keys.' + key) }}</option>
|
||||
</MkSelect>
|
||||
</template>
|
||||
<!-- CSS -->
|
||||
<MkInput v-else-if="v.type === 'css'" v-model="v.value">
|
||||
<span>CSS</span>
|
||||
</MkInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MkButton primary @click="addConst">{{ $ts._theme.addConstant }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="_section">
|
||||
<details class="_content">
|
||||
<summary>{{ $ts.sample }}</summary>
|
||||
<MkSample/>
|
||||
</details>
|
||||
</section>
|
||||
<section class="_section">
|
||||
<div class="_content">
|
||||
<MkButton inline @click="preview">{{ $ts.preview }}</MkButton>
|
||||
<MkButton inline primary :disabled="!name || !author" @click="save">{{ $ts.save }}</MkButton>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as JSON5 from 'json5';
|
||||
import { toUnicode } from 'punycode/';
|
||||
|
||||
import MkRadio from '@/components/form/radio.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkTextarea from '@/components/form/textarea.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import MkSample from '@/components/sample.vue';
|
||||
|
||||
import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@/scripts/theme-editor';
|
||||
import { Theme, applyTheme, lightTheme, darkTheme, themeProps, validateTheme } from '@/scripts/theme';
|
||||
import { host } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import { addTheme } from '@/theme-store';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkRadio,
|
||||
MkButton,
|
||||
MkInput,
|
||||
MkTextarea,
|
||||
MkSelect,
|
||||
MkSample,
|
||||
},
|
||||
|
||||
async beforeRouteLeave(to, from, next) {
|
||||
if (this.changed && !(await this.confirm())) {
|
||||
next(false);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.themeEditor,
|
||||
icon: 'fas fa-palette',
|
||||
},
|
||||
theme: [] as ThemeViewModel,
|
||||
name: '',
|
||||
description: '',
|
||||
baseTheme: 'light' as 'dark' | 'light',
|
||||
author: `@${this.$i.username}@${toUnicode(host)}`,
|
||||
themeToImport: '',
|
||||
changed: false,
|
||||
lightTheme, darkTheme, themeProps,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
baseProps() {
|
||||
return this.baseTheme === 'light' ? this.lightTheme.props : this.darkTheme.props;
|
||||
},
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('beforeunload', this.beforeunload);
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.init();
|
||||
window.addEventListener('beforeunload', this.beforeunload);
|
||||
const changed = () => this.changed = true;
|
||||
this.$watch('name', changed);
|
||||
this.$watch('description', changed);
|
||||
this.$watch('baseTheme', changed);
|
||||
this.$watch('author', changed);
|
||||
this.$watch('theme', changed);
|
||||
},
|
||||
|
||||
methods: {
|
||||
beforeunload(e: BeforeUnloadEvent) {
|
||||
if (this.changed) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
},
|
||||
|
||||
async confirm(): Promise<boolean> {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: this.$ts.leaveConfirm,
|
||||
});
|
||||
return !canceled;
|
||||
},
|
||||
|
||||
init() {
|
||||
const t: ThemeViewModel = [];
|
||||
for (const key of themeProps) {
|
||||
t.push([ key, null ]);
|
||||
}
|
||||
this.theme = t;
|
||||
},
|
||||
|
||||
async del(i: number) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: this.$t('_theme.deleteConstantConfirm', { const: this.theme[i][0] }),
|
||||
});
|
||||
if (canceled) return;
|
||||
Vue.delete(this.theme, i);
|
||||
},
|
||||
|
||||
async addConst() {
|
||||
const { canceled, result } = await os.inputText({
|
||||
title: this.$ts._theme.inputConstantName,
|
||||
});
|
||||
if (canceled) return;
|
||||
this.theme.push([ '$' + result, '#000000']);
|
||||
},
|
||||
|
||||
save() {
|
||||
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
|
||||
addTheme(theme);
|
||||
os.alert({
|
||||
type: 'success',
|
||||
text: this.$t('_theme.installed', { name: theme.name })
|
||||
});
|
||||
this.changed = false;
|
||||
},
|
||||
|
||||
preview() {
|
||||
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
|
||||
try {
|
||||
applyTheme(theme, false);
|
||||
} catch (e) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async importTheme() {
|
||||
if (this.changed && (!await this.confirm())) return;
|
||||
|
||||
try {
|
||||
const theme = JSON5.parse(this.themeToImport) as Theme;
|
||||
if (!validateTheme(theme)) throw new Error(this.$ts._theme.invalid);
|
||||
|
||||
this.name = theme.name;
|
||||
this.description = theme.desc || '';
|
||||
this.author = theme.author;
|
||||
this.baseTheme = theme.base || 'light';
|
||||
this.theme = convertToViewModel(theme);
|
||||
this.themeToImport = '';
|
||||
} catch (e) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
colorChanged(color: string, i: number) {
|
||||
this.theme[i] = [this.theme[i][0], color];
|
||||
},
|
||||
|
||||
getTypeOf(v: ThemeValue) {
|
||||
return v === null
|
||||
? this.$ts._theme.defaultValue
|
||||
: typeof v === 'string'
|
||||
? this.$ts._theme.color
|
||||
: this.$t('_theme.' + v.type);
|
||||
},
|
||||
|
||||
async chooseType(e: MouseEvent, i: number) {
|
||||
const newValue = await this.showTypeMenu(e);
|
||||
this.theme[i] = [ this.theme[i][0], newValue ];
|
||||
},
|
||||
|
||||
showTypeMenu(e: MouseEvent) {
|
||||
return new Promise<ThemeValue>((resolve) => {
|
||||
os.popupMenu([{
|
||||
text: this.$ts._theme.defaultValue,
|
||||
action: () => resolve(null),
|
||||
}, {
|
||||
text: this.$ts._theme.color,
|
||||
action: () => resolve('#000000'),
|
||||
}, {
|
||||
text: this.$ts._theme.func,
|
||||
action: () => resolve({
|
||||
type: 'func', name: 'alpha', arg: 1, value: 'accent'
|
||||
}),
|
||||
}, {
|
||||
text: this.$ts._theme.refProp,
|
||||
action: () => resolve({
|
||||
type: 'refProp', key: 'accent',
|
||||
}),
|
||||
}, {
|
||||
text: this.$ts._theme.refConst,
|
||||
action: () => resolve({
|
||||
type: 'refConst', key: '',
|
||||
}),
|
||||
}, {
|
||||
text: 'CSS',
|
||||
action: () => resolve({
|
||||
type: 'css', value: '',
|
||||
}),
|
||||
}], e.currentTarget || e.target);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.t9makv94 {
|
||||
> ._section {
|
||||
> ._content {
|
||||
> .list-view {
|
||||
> .item {
|
||||
min-height: 48px;
|
||||
word-break: break-all;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.select {
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.type {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.default-value {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.color {
|
||||
> input {
|
||||
display: inline-block;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin-left: 8px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -36,7 +36,7 @@ export default defineComponent({
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'announcements',
|
||||
endpoint: 'announcements' as const,
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
|
@ -67,11 +67,11 @@ export default defineComponent({
|
||||
channel: null,
|
||||
showBanner: true,
|
||||
pagination: {
|
||||
endpoint: 'channels/timeline',
|
||||
endpoint: 'channels/timeline' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
channelId: this.channelId,
|
||||
})
|
||||
}))
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -60,15 +60,15 @@ export default defineComponent({
|
||||
})),
|
||||
tab: 'featured',
|
||||
featuredPagination: {
|
||||
endpoint: 'channels/featured',
|
||||
endpoint: 'channels/featured' as const,
|
||||
noPaging: true,
|
||||
},
|
||||
followingPagination: {
|
||||
endpoint: 'channels/followed',
|
||||
endpoint: 'channels/followed' as const,
|
||||
limit: 5,
|
||||
},
|
||||
ownedPagination: {
|
||||
endpoint: 'channels/owned',
|
||||
endpoint: 'channels/owned' as const,
|
||||
limit: 5,
|
||||
},
|
||||
};
|
||||
|
@ -50,11 +50,11 @@ export default defineComponent({
|
||||
} : null),
|
||||
clip: null,
|
||||
pagination: {
|
||||
endpoint: 'clips/notes',
|
||||
endpoint: 'clips/notes' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
clipId: this.clipId,
|
||||
})
|
||||
}))
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -4,27 +4,21 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import XDrive from '@/components/drive.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XDrive
|
||||
},
|
||||
let folder = $ref(null);
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: computed(() => this.folder ? this.folder.name : this.$ts.drive),
|
||||
icon: 'fas fa-cloud',
|
||||
bg: 'var(--bg)',
|
||||
hideHeader: true,
|
||||
},
|
||||
folder: null,
|
||||
};
|
||||
},
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: folder ? folder.name : i18n.locale.drive,
|
||||
icon: 'fas fa-cloud',
|
||||
bg: 'var(--bg)',
|
||||
hideHeader: true,
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
@ -8,35 +8,29 @@
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
emoji: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
const props = defineProps<{
|
||||
emoji: Record<string, unknown>; // TODO
|
||||
}>();
|
||||
|
||||
methods: {
|
||||
menu(ev) {
|
||||
os.popupMenu([{
|
||||
type: 'label',
|
||||
text: ':' + this.emoji.name + ':',
|
||||
}, {
|
||||
text: this.$ts.copy,
|
||||
icon: 'fas fa-copy',
|
||||
action: () => {
|
||||
copyToClipboard(`:${this.emoji.name}:`);
|
||||
os.success();
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
function menu(ev) {
|
||||
os.popupMenu([{
|
||||
type: 'label',
|
||||
text: ':' + props.emoji.name + ':',
|
||||
}, {
|
||||
text: i18n.locale.copy,
|
||||
icon: 'fas fa-copy',
|
||||
action: () => {
|
||||
copyToClipboard(`:${props.emoji.name}:`);
|
||||
os.success();
|
||||
}
|
||||
}
|
||||
});
|
||||
}], ev.currentTarget || ev.target);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -4,55 +4,47 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import XCategory from './emojis.category.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XCategory,
|
||||
},
|
||||
const tab = ref('category');
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: this.$ts.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
icon: 'fas fa-ellipsis-h',
|
||||
handler: this.menu
|
||||
}],
|
||||
})),
|
||||
tab: 'category',
|
||||
function menu(ev) {
|
||||
os.popupMenu([{
|
||||
icon: 'fas fa-download',
|
||||
text: i18n.locale.export,
|
||||
action: async () => {
|
||||
os.api('export-custom-emojis', {
|
||||
})
|
||||
.then(() => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: i18n.locale.exportRequested,
|
||||
});
|
||||
}).catch((e) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
}], ev.currentTarget || ev.target);
|
||||
}
|
||||
|
||||
methods: {
|
||||
menu(ev) {
|
||||
os.popupMenu([{
|
||||
icon: 'fas fa-download',
|
||||
text: this.$ts.export,
|
||||
action: async () => {
|
||||
os.api('export-custom-emojis', {
|
||||
})
|
||||
.then(() => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: this.$ts.exportRequested,
|
||||
});
|
||||
}).catch((e) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
icon: 'fas fa-ellipsis-h',
|
||||
handler: menu,
|
||||
}],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -156,7 +156,7 @@ export default defineComponent({
|
||||
sort: '+createdAt',
|
||||
} },
|
||||
searchPagination: {
|
||||
endpoint: 'users/search',
|
||||
endpoint: 'users/search' as const,
|
||||
limit: 10,
|
||||
params: computed(() => (this.searchQuery && this.searchQuery !== '') ? {
|
||||
query: this.searchQuery,
|
||||
@ -178,7 +178,7 @@ export default defineComponent({
|
||||
},
|
||||
tagUsers(): any {
|
||||
return {
|
||||
endpoint: 'hashtags/users',
|
||||
endpoint: 'hashtags/users' as const,
|
||||
limit: 30,
|
||||
params: {
|
||||
tag: this.tag,
|
||||
|
@ -1,49 +1,49 @@
|
||||
<template>
|
||||
<div class="jmelgwjh">
|
||||
<div class="body">
|
||||
<XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'"/>
|
||||
</div>
|
||||
</div>
|
||||
<MkSpacer :content-max="800">
|
||||
<MkPagination ref="pagingComponent" :pagination="pagination">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $ts.noNotes }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #default="{ items }">
|
||||
<XList v-slot="{ item }" :items="items" :direction="'down'" :no-gap="false" :ad="false">
|
||||
<XNote :key="item.id" :note="item.note" :class="$style.note"/>
|
||||
</XList>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import * as os from '@/os';
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import XNote from '@/components/note.vue';
|
||||
import XList from '@/components/date-separated-list.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'i/favorites' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.favorites,
|
||||
icon: 'fas fa-star',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'i/favorites',
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
})
|
||||
},
|
||||
};
|
||||
const pagingComponent = ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.favorites,
|
||||
icon: 'fas fa-star',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.jmelgwjh {
|
||||
background: var(--bg);
|
||||
|
||||
> .body {
|
||||
box-sizing: border-box;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
}
|
||||
<style lang="scss" module>
|
||||
.note {
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
</style>
|
||||
|
@ -4,29 +4,22 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'notes/featured' as const,
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
};
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.featured,
|
||||
icon: 'fas fa-fire-alt',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/featured',
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
},
|
||||
};
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.featured,
|
||||
icon: 'fas fa-fire-alt',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -95,8 +95,8 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
@ -104,68 +104,41 @@ import MkPagination from '@/components/ui/pagination.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
MkInput,
|
||||
MkSelect,
|
||||
MkPagination,
|
||||
FormSplit,
|
||||
let host = $ref('');
|
||||
let state = $ref('federating');
|
||||
let sort = $ref('+pubSub');
|
||||
const pagination = {
|
||||
endpoint: 'federation/instances' as const,
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
params: computed(() => ({
|
||||
sort: sort,
|
||||
host: host != '' ? host : null,
|
||||
...(
|
||||
state === 'federating' ? { federating: true } :
|
||||
state === 'subscribing' ? { subscribing: true } :
|
||||
state === 'publishing' ? { publishing: true } :
|
||||
state === 'suspended' ? { suspended: true } :
|
||||
state === 'blocked' ? { blocked: true } :
|
||||
state === 'notResponding' ? { notResponding: true } :
|
||||
{})
|
||||
}))
|
||||
};
|
||||
|
||||
function getStatus(instance) {
|
||||
if (instance.isSuspended) return 'suspended';
|
||||
if (instance.isNotResponding) return 'error';
|
||||
return 'alive';
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.federation,
|
||||
icon: 'fas fa-globe',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.federation,
|
||||
icon: 'fas fa-globe',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
host: '',
|
||||
state: 'federating',
|
||||
sort: '+pubSub',
|
||||
pagination: {
|
||||
endpoint: 'federation/instances',
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
params: () => ({
|
||||
sort: this.sort,
|
||||
host: this.host != '' ? this.host : null,
|
||||
...(
|
||||
this.state === 'federating' ? { federating: true } :
|
||||
this.state === 'subscribing' ? { subscribing: true } :
|
||||
this.state === 'publishing' ? { publishing: true } :
|
||||
this.state === 'suspended' ? { suspended: true } :
|
||||
this.state === 'blocked' ? { blocked: true } :
|
||||
this.state === 'notResponding' ? { notResponding: true } :
|
||||
{})
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
host() {
|
||||
this.$refs.instances.reload();
|
||||
},
|
||||
state() {
|
||||
this.$refs.instances.reload();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
getStatus(instance) {
|
||||
if (instance.isSuspended) return 'suspended';
|
||||
if (instance.isNotResponding) return 'error';
|
||||
return 'alive';
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<MkPagination ref="list" :pagination="pagination" class="mk-follow-requests">
|
||||
<MkPagination ref="paginationComponent" :pagination="pagination">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
@ -8,19 +8,21 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot="{items}">
|
||||
<div v-for="req in items" :key="req.id" class="user _panel">
|
||||
<MkAvatar class="avatar" :user="req.follower" :show-indicator="true"/>
|
||||
<div class="body">
|
||||
<div class="name">
|
||||
<MkA v-user-preview="req.follower.id" class="name" :to="userPage(req.follower)"><MkUserName :user="req.follower"/></MkA>
|
||||
<p class="acct">@{{ acct(req.follower) }}</p>
|
||||
</div>
|
||||
<div v-if="req.follower.description" class="description" :title="req.follower.description">
|
||||
<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="_button" @click="accept(req.follower)"><i class="fas fa-check"></i></button>
|
||||
<button class="_button" @click="reject(req.follower)"><i class="fas fa-times"></i></button>
|
||||
<div class="mk-follow-requests">
|
||||
<div v-for="req in items" :key="req.id" class="user _panel">
|
||||
<MkAvatar class="avatar" :user="req.follower" :show-indicator="true"/>
|
||||
<div class="body">
|
||||
<div class="name">
|
||||
<MkA v-user-preview="req.follower.id" class="name" :to="userPage(req.follower)"><MkUserName :user="req.follower"/></MkA>
|
||||
<p class="acct">@{{ acct(req.follower) }}</p>
|
||||
</div>
|
||||
<div v-if="req.follower.description" class="description" :title="req.follower.description">
|
||||
<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="_button" @click="accept(req.follower)"><i class="fas fa-check"></i></button>
|
||||
<button class="_button" @click="reject(req.follower)"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -29,45 +31,39 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import { userPage, acct } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkPagination
|
||||
},
|
||||
const paginationComponent = ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.followRequests,
|
||||
icon: 'fas fa-user-clock',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'following/requests/list',
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'following/requests/list' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
methods: {
|
||||
accept(user) {
|
||||
os.api('following/requests/accept', { userId: user.id }).then(() => {
|
||||
this.$refs.list.reload();
|
||||
});
|
||||
},
|
||||
reject(user) {
|
||||
os.api('following/requests/reject', { userId: user.id }).then(() => {
|
||||
this.$refs.list.reload();
|
||||
});
|
||||
},
|
||||
userPage,
|
||||
acct
|
||||
}
|
||||
function accept(user) {
|
||||
os.api('following/requests/accept', { userId: user.id }).then(() => {
|
||||
paginationComponent.value.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function reject(user) {
|
||||
os.api('following/requests/reject', { userId: user.id }).then(() => {
|
||||
paginationComponent.value.reload();
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: i18n.locale.followRequests,
|
||||
icon: 'fas fa-user-clock',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div>
|
||||
<FormSuspense :p="init">
|
||||
<FormInput v-model="title">
|
||||
<span>{{ $ts.title }}</span>
|
||||
<template #label>{{ $ts.title }}</template>
|
||||
</FormInput>
|
||||
|
||||
<FormTextarea v-model="description" :max="500">
|
||||
<span>{{ $ts.description }}</span>
|
||||
<template #label>{{ $ts.description }}</template>
|
||||
</FormTextarea>
|
||||
|
||||
<FormGroup>
|
||||
<div v-for="file in files" :key="file.id" class="_debobigegoItem _debobigegoPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
|
||||
<div v-for="file in files" :key="file.id" class="_formGroup wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
|
||||
<div class="name">{{ file.name }}</div>
|
||||
<button v-tooltip="$ts.remove" class="remove _button" @click="remove(file)"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
@ -24,19 +24,17 @@
|
||||
|
||||
<FormButton v-if="postId" danger @click="del"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormTuple from '@/components/debobigego/tuple.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormGroup from '@/components/form/group.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import { selectFiles } from '@/scripts/select-file';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
@ -47,7 +45,6 @@ export default defineComponent({
|
||||
FormInput,
|
||||
FormTextarea,
|
||||
FormSwitch,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormSuspense,
|
||||
},
|
||||
|
@ -81,19 +81,19 @@ export default defineComponent({
|
||||
},
|
||||
tab: 'explore',
|
||||
recentPostsPagination: {
|
||||
endpoint: 'gallery/posts',
|
||||
endpoint: 'gallery/posts' as const,
|
||||
limit: 6,
|
||||
},
|
||||
popularPostsPagination: {
|
||||
endpoint: 'gallery/featured',
|
||||
endpoint: 'gallery/featured' as const,
|
||||
limit: 5,
|
||||
},
|
||||
myPostsPagination: {
|
||||
endpoint: 'i/gallery/posts',
|
||||
endpoint: 'i/gallery/posts' as const,
|
||||
limit: 5,
|
||||
},
|
||||
likedPostsPagination: {
|
||||
endpoint: 'i/gallery/likes',
|
||||
endpoint: 'i/gallery/likes' as const,
|
||||
limit: 5,
|
||||
},
|
||||
tags: [],
|
||||
@ -106,7 +106,7 @@ export default defineComponent({
|
||||
},
|
||||
tagUsers(): any {
|
||||
return {
|
||||
endpoint: 'hashtags/users',
|
||||
endpoint: 'hashtags/users' as const,
|
||||
limit: 30,
|
||||
params: {
|
||||
tag: this.tag,
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="_root">
|
||||
<transition name="fade" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
|
||||
<div v-if="post" class="rkxwuolj">
|
||||
<div class="files">
|
||||
<div v-for="file in post.files" :key="file.id" class="file">
|
||||
@ -93,11 +93,11 @@ export default defineComponent({
|
||||
}]
|
||||
} : null),
|
||||
otherPostsPagination: {
|
||||
endpoint: 'users/gallery/posts',
|
||||
endpoint: 'users/gallery/posts' as const,
|
||||
limit: 6,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
userId: this.post.user.id
|
||||
})
|
||||
})),
|
||||
},
|
||||
post: null,
|
||||
error: null,
|
||||
|
@ -1,70 +1,71 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormGroup v-if="instance">
|
||||
<template #label>{{ instance.host }}</template>
|
||||
<FormGroup>
|
||||
<div class="_debobigegoItem">
|
||||
<div class="_debobigegoPanel fnfelxur">
|
||||
<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
|
||||
</div>
|
||||
</div>
|
||||
<FormKeyValueView>
|
||||
<template #key>Name</template>
|
||||
<template #value><span class="_monospace">{{ instance.name || `(${$ts.unknown})` }}</span></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<MkSpacer :content-max="600" :margin-min="16" :margin-max="32">
|
||||
<div v-if="instance" class="_formRoot">
|
||||
<div class="fnfelxur">
|
||||
<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
|
||||
</div>
|
||||
<MkKeyValue :copy="host" oneline style="margin: 1em 0;">
|
||||
<template #key>Host</template>
|
||||
<template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>Name</template>
|
||||
<template #value>{{ instance.name || `(${$ts.unknown})` }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ $ts.description }}</template>
|
||||
<template #value>{{ instance.description }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.software }}</template>
|
||||
<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }} / {{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.administrator }}</template>
|
||||
<template #value>{{ instance.maintainerName || `(${$ts.unknown})` }} ({{ instance.maintainerEmail || `(${$ts.unknown})` }})</template>
|
||||
</MkKeyValue>
|
||||
|
||||
<FormButton v-if="$i.isAdmin || $i.isModerator" primary @click="info">{{ $ts.settings }}</FormButton>
|
||||
<FormSection v-if="iAmModerator">
|
||||
<template #label>Moderation</template>
|
||||
<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.stopActivityDelivery }}</FormSwitch>
|
||||
<FormSwitch v-model="isBlocked" class="_formBlock" @update:modelValue="toggleBlock">{{ $ts.blockThisInstance }}</FormSwitch>
|
||||
</FormSection>
|
||||
|
||||
<FormTextarea readonly :value="instance.description">
|
||||
<span>{{ $ts.description }}</span>
|
||||
</FormTextarea>
|
||||
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.software }}</template>
|
||||
<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }}</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.version }}</template>
|
||||
<template #value><span class="_monospace">{{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.administrator }}</template>
|
||||
<template #value><span class="_monospace">{{ instance.maintainerName || `(${$ts.unknown})` }}</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.contact }}</template>
|
||||
<template #value><span class="_monospace">{{ instance.maintainerEmail || `(${$ts.unknown})` }}</span></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<FormSection>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.registeredAt }}</template>
|
||||
<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.updatedAt }}</template>
|
||||
<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.latestRequestSentAt }}</template>
|
||||
<template #value><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.latestStatus }}</template>
|
||||
<template #value>{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.latestRequestReceivedAt }}</template>
|
||||
<template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>Open Registrations</template>
|
||||
<template #value>{{ instance.openRegistrations ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<div class="_debobigegoItem">
|
||||
<div class="_debobigegoLabel">{{ $ts.statistics }}</div>
|
||||
<div class="_debobigegoPanel cmhjzshl">
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ $ts.statistics }}</template>
|
||||
<div class="cmhjzshl">
|
||||
<div class="selects">
|
||||
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
|
||||
<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
|
||||
<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
|
||||
<option value="instance-users">{{ $ts._instanceCharts.users }}</option>
|
||||
<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
|
||||
@ -83,147 +84,100 @@
|
||||
</MkSelect>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart>
|
||||
<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :args="{ host: host }" :detailed="true"></MkChart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.registeredAt }}</template>
|
||||
<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.updatedAt }}</template>
|
||||
<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
<FormObjectView tall :value="instance">
|
||||
<span>Raw</span>
|
||||
</FormObjectView>
|
||||
<FormGroup>
|
||||
</FormSection>
|
||||
|
||||
<MkObjectView tall :value="instance">
|
||||
</MkObjectView>
|
||||
|
||||
<FormSection>
|
||||
<template #label>Well-known resources</template>
|
||||
<FormLink :to="`https://${host}/.well-known/host-meta`" external>host-meta</FormLink>
|
||||
<FormLink :to="`https://${host}/.well-known/host-meta.json`" external>host-meta.json</FormLink>
|
||||
<FormLink :to="`https://${host}/.well-known/nodeinfo`" external>nodeinfo</FormLink>
|
||||
<FormLink :to="`https://${host}/robots.txt`" external>robots.txt</FormLink>
|
||||
<FormLink :to="`https://${host}/manifest.json`" external>manifest.json</FormLink>
|
||||
</FormGroup>
|
||||
<FormSuspense v-slot="{ result: dns }" :p="dnsPromiseFactory">
|
||||
<FormGroup>
|
||||
<template #label>DNS</template>
|
||||
<FormKeyValueView v-for="record in dns.a" :key="record">
|
||||
<template #key>A</template>
|
||||
<template #value><span class="_monospace">{{ record }}</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView v-for="record in dns.aaaa" :key="record">
|
||||
<template #key>AAAA</template>
|
||||
<template #value><span class="_monospace">{{ record }}</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView v-for="record in dns.cname" :key="record">
|
||||
<template #key>CNAME</template>
|
||||
<template #value><span class="_monospace">{{ record }}</span></template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView v-for="record in dns.txt">
|
||||
<template #key>TXT</template>
|
||||
<template #value><span class="_monospace">{{ record[0] }}</span></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
</FormSuspense>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
<FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink>
|
||||
<FormLink :to="`https://${host}/.well-known/host-meta.json`" external style="margin-bottom: 8px;">host-meta.json</FormLink>
|
||||
<FormLink :to="`https://${host}/.well-known/nodeinfo`" external style="margin-bottom: 8px;">nodeinfo</FormLink>
|
||||
<FormLink :to="`https://${host}/robots.txt`" external style="margin-bottom: 8px;">robots.txt</FormLink>
|
||||
<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink>
|
||||
</FormSection>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkChart from '@/components/chart.vue';
|
||||
import FormObjectView from '@/components/debobigego/object-view.vue';
|
||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import MkObjectView from '@/components/object-view.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import MkLink from '@/components/link.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
import bytes from '@/filters/bytes';
|
||||
import * as symbols from '@/symbols';
|
||||
import MkInstanceInfo from '@/pages/admin/instance.vue';
|
||||
import { iAmModerator } from '@/account';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormTextarea,
|
||||
FormObjectView,
|
||||
FormButton,
|
||||
FormLink,
|
||||
FormGroup,
|
||||
FormKeyValueView,
|
||||
FormSuspense,
|
||||
MkSelect,
|
||||
MkChart,
|
||||
const props = defineProps<{
|
||||
host: string;
|
||||
}>();
|
||||
|
||||
let meta = $ref<misskey.entities.DetailedInstanceMetadata | null>(null);
|
||||
let instance = $ref<misskey.entities.Instance | null>(null);
|
||||
let suspended = $ref(false);
|
||||
let isBlocked = $ref(false);
|
||||
let chartSrc = $ref('instance-requests');
|
||||
let chartSpan = $ref('hour');
|
||||
|
||||
async function fetch() {
|
||||
meta = await os.api('meta', { detail: true });
|
||||
instance = await os.api('federation/show-instance', {
|
||||
host: props.host,
|
||||
});
|
||||
suspended = instance.isSuspended;
|
||||
isBlocked = meta.blockedHosts.includes(instance.host);
|
||||
}
|
||||
|
||||
async function toggleBlock(ev) {
|
||||
if (meta == null) return;
|
||||
await os.api('admin/update-meta', {
|
||||
blockedHosts: isBlocked ? meta.blockedHosts.concat([instance.host]) : meta.blockedHosts.filter(x => x !== instance.host)
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleSuspend(v) {
|
||||
await os.api('admin/federation/update-instance', {
|
||||
host: instance.host,
|
||||
isSuspended: suspended,
|
||||
});
|
||||
}
|
||||
|
||||
fetch();
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: props.host,
|
||||
icon: 'fas fa-info-circle',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
text: `https://${props.host}`,
|
||||
icon: 'fas fa-external-link-alt',
|
||||
handler: () => {
|
||||
window.open(`https://${props.host}`, '_blank');
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
props: {
|
||||
host: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.instanceInfo,
|
||||
icon: 'fas fa-info-circle',
|
||||
actions: [{
|
||||
text: `https://${this.host}`,
|
||||
icon: 'fas fa-external-link-alt',
|
||||
handler: () => {
|
||||
window.open(`https://${this.host}`, '_blank');
|
||||
}
|
||||
}],
|
||||
},
|
||||
instance: null,
|
||||
dnsPromiseFactory: () => os.api('federation/dns', {
|
||||
host: this.host
|
||||
}),
|
||||
chartSrc: 'instance-requests',
|
||||
chartSpan: 'hour',
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
number,
|
||||
bytes,
|
||||
|
||||
async fetch() {
|
||||
this.instance = await os.api('federation/show-instance', {
|
||||
host: this.host
|
||||
});
|
||||
},
|
||||
|
||||
info() {
|
||||
os.popup(MkInstanceInfo, {
|
||||
instance: this.instance
|
||||
}, {}, 'closed');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fnfelxur {
|
||||
padding: 16px;
|
||||
|
||||
> .icon {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin: 0;
|
||||
height: 64px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
@ -232,7 +186,7 @@ export default defineComponent({
|
||||
.cmhjzshl {
|
||||
> .selects {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -4,28 +4,21 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'notes/mentions' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.mentions,
|
||||
icon: 'fas fa-at',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.mentions,
|
||||
icon: 'fas fa-at',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -4,31 +4,24 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'notes/mentions' as const,
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
visibility: 'specified'
|
||||
}),
|
||||
};
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.directNotes,
|
||||
icon: 'fas fa-envelope',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
visibility: 'specified'
|
||||
})
|
||||
},
|
||||
};
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.directNotes,
|
||||
icon: 'fas fa-envelope',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -7,7 +7,7 @@
|
||||
ref="text"
|
||||
v-model="text"
|
||||
:placeholder="$ts.inputMessageHere"
|
||||
@keypress="onKeypress"
|
||||
@keydown="onKeydown"
|
||||
@compositionupdate="onCompositionUpdate"
|
||||
@paste="onPaste"
|
||||
></textarea>
|
||||
@ -141,7 +141,7 @@ export default defineComponent({
|
||||
//#endregion
|
||||
},
|
||||
|
||||
onKeypress(e) {
|
||||
onKeydown(e) {
|
||||
this.typing();
|
||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canSend) {
|
||||
this.send();
|
||||
|
@ -24,7 +24,7 @@
|
||||
</I18n>
|
||||
<MkEllipsis/>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<transition :name="$store.state.animation ? 'fade' : ''">
|
||||
<div v-show="showIndicator" class="new-message">
|
||||
<button class="_buttonPrimary" @click="onIndicatorClick"><i class="fas fa-arrow-circle-down"></i>{{ $ts.newMessageExists }}</button>
|
||||
</div>
|
||||
@ -162,7 +162,7 @@ const Component = defineComponent({
|
||||
// もっと見るの交差検知を発火させないためにfetchは
|
||||
// スクロールが終わるまでfalseにしておく
|
||||
// scrollendのようなイベントはないのでsetTimeoutで
|
||||
setTimeout(() => this.fetching = false, 300);
|
||||
window.setTimeout(() => this.fetching = false, 300);
|
||||
});
|
||||
},
|
||||
|
||||
@ -300,9 +300,9 @@ const Component = defineComponent({
|
||||
this.showIndicator = false;
|
||||
});
|
||||
|
||||
if (this.timer) clearTimeout(this.timer);
|
||||
if (this.timer) window.clearTimeout(this.timer);
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
this.timer = window.setTimeout(() => {
|
||||
this.showIndicator = false;
|
||||
}, 4000);
|
||||
},
|
||||
|
@ -4,45 +4,37 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import XAntenna from './editor.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
import { router } from '@/router';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
XAntenna,
|
||||
let draft = $ref({
|
||||
name: '',
|
||||
src: 'all',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: [],
|
||||
keywords: [],
|
||||
excludeKeywords: [],
|
||||
withReplies: false,
|
||||
caseSensitive: false,
|
||||
withFile: false,
|
||||
notify: false
|
||||
});
|
||||
|
||||
function onAntennaCreated() {
|
||||
router.push('/my/antennas');
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.manageAntennas,
|
||||
icon: 'fas fa-satellite',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.manageAntennas,
|
||||
icon: 'fas fa-satellite',
|
||||
},
|
||||
draft: {
|
||||
name: '',
|
||||
src: 'all',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: [],
|
||||
keywords: [],
|
||||
excludeKeywords: [],
|
||||
withReplies: false,
|
||||
caseSensitive: false,
|
||||
withFile: false,
|
||||
notify: false
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
onAntennaCreated() {
|
||||
this.$router.push('/my/antennas');
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -38,7 +38,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'antennas/list',
|
||||
endpoint: 'antennas/list' as const,
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="qtcaoidl">
|
||||
<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
|
||||
|
||||
<MkPagination v-slot="{items}" ref="list" :pagination="pagination" class="list">
|
||||
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="list">
|
||||
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
|
||||
<b>{{ item.name }}</b>
|
||||
<div v-if="item.description" class="description">{{ item.description }}</div>
|
||||
@ -13,71 +13,64 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import i18n from '@/components/global/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkPagination,
|
||||
MkButton,
|
||||
const pagination = {
|
||||
endpoint: 'clips/list' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
async function create() {
|
||||
const { canceled, result } = await os.form(i18n.locale.createNewClip, {
|
||||
name: {
|
||||
type: 'string',
|
||||
label: i18n.locale.name,
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
multiline: true,
|
||||
label: i18n.locale.description,
|
||||
},
|
||||
isPublic: {
|
||||
type: 'boolean',
|
||||
label: i18n.locale.public,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
os.apiWithDialog('clips/create', result);
|
||||
|
||||
pagingComponent.reload();
|
||||
}
|
||||
|
||||
function onClipCreated() {
|
||||
pagingComponent.reload();
|
||||
}
|
||||
|
||||
function onClipDeleted() {
|
||||
pagingComponent.reload();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.clip,
|
||||
icon: 'fas fa-paperclip',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: create
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.clip,
|
||||
icon: 'fas fa-paperclip',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: this.create
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'clips/list',
|
||||
limit: 10,
|
||||
},
|
||||
draft: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async create() {
|
||||
const { canceled, result } = await os.form(this.$ts.createNewClip, {
|
||||
name: {
|
||||
type: 'string',
|
||||
label: this.$ts.name
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
multiline: true,
|
||||
label: this.$ts.description
|
||||
},
|
||||
isPublic: {
|
||||
type: 'boolean',
|
||||
label: this.$ts.public,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
os.apiWithDialog('clips/create', result);
|
||||
},
|
||||
|
||||
onClipCreated() {
|
||||
this.$refs.list.reload();
|
||||
this.draft = null;
|
||||
},
|
||||
|
||||
onClipDeleted() {
|
||||
this.$refs.list.reload();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-group-page">
|
||||
<transition name="zoom" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
|
||||
<div v-if="group" class="_section">
|
||||
<div class="_content" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
|
||||
<MkButton inline @click="invite()">{{ $ts.invite }}</MkButton>
|
||||
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<transition name="zoom" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
|
||||
<div v-if="group" class="_section members _gap">
|
||||
<div class="_title">{{ $ts.members }}</div>
|
||||
<div class="_content">
|
||||
|
@ -87,15 +87,15 @@ export default defineComponent({
|
||||
})),
|
||||
tab: 'owned',
|
||||
ownedPagination: {
|
||||
endpoint: 'users/groups/owned',
|
||||
endpoint: 'users/groups/owned' as const,
|
||||
limit: 10,
|
||||
},
|
||||
joinedPagination: {
|
||||
endpoint: 'users/groups/joined',
|
||||
endpoint: 'users/groups/joined' as const,
|
||||
limit: 10,
|
||||
},
|
||||
invitationPagination: {
|
||||
endpoint: 'i/user-group-invites',
|
||||
endpoint: 'i/user-group-invites' as const,
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="qkcjvfiv">
|
||||
<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
|
||||
|
||||
<MkPagination v-slot="{items}" ref="list" :pagination="pagination" class="lists _content">
|
||||
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists _content">
|
||||
<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
|
||||
<div class="name">{{ list.name }}</div>
|
||||
<MkAvatars :user-ids="list.userIds"/>
|
||||
@ -13,50 +13,41 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkAvatars from '@/components/avatars.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkPagination,
|
||||
MkButton,
|
||||
MkAvatars,
|
||||
},
|
||||
const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.manageLists,
|
||||
icon: 'fas fa-list-ul',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: this.create
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'users/lists/list',
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'users/lists/list' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
methods: {
|
||||
async create() {
|
||||
const { canceled, result: name } = await os.inputText({
|
||||
title: this.$ts.enterListName,
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.api('users/lists/create', { name: name });
|
||||
this.$refs.list.reload();
|
||||
os.success();
|
||||
async function create() {
|
||||
const { canceled, result: name } = await os.inputText({
|
||||
title: i18n.locale.enterListName,
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('users/lists/create', { name: name });
|
||||
pagingComponent.reload();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.manageLists,
|
||||
icon: 'fas fa-list-ul',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: create,
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<MkSpacer :content-max="700">
|
||||
<div class="mk-list-page">
|
||||
<transition name="zoom" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
|
||||
<div v-if="list" class="_section">
|
||||
<div class="_content">
|
||||
<MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
|
||||
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<transition name="zoom" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
|
||||
<div v-if="list" class="_section members _gap">
|
||||
<div class="_title">{{ $ts.members }}</div>
|
||||
<div class="_content">
|
||||
|
@ -7,19 +7,15 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
<script lang="ts" setup>
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.notFound,
|
||||
icon: 'fas fa-exclamation-triangle'
|
||||
},
|
||||
}
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.notFound,
|
||||
icon: 'fas fa-exclamation-triangle',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<MkSpacer :content-max="800">
|
||||
<div class="fcuexfpr">
|
||||
<transition name="fade" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
|
||||
<div v-if="note" class="note">
|
||||
<div v-if="showNext" class="_gap">
|
||||
<XNotes class="_content" :pagination="next" :no-gap="true"/>
|
||||
@ -82,21 +82,21 @@ export default defineComponent({
|
||||
showNext: false,
|
||||
error: null,
|
||||
prev: {
|
||||
endpoint: 'users/notes',
|
||||
endpoint: 'users/notes' as const,
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
params: computed(() => ({
|
||||
userId: this.note.userId,
|
||||
untilId: this.note.id,
|
||||
})
|
||||
})),
|
||||
},
|
||||
next: {
|
||||
reversed: true,
|
||||
endpoint: 'users/notes',
|
||||
endpoint: 'users/notes' as const,
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
params: computed(() => ({
|
||||
userId: this.note.userId,
|
||||
sinceId: this.note.id,
|
||||
})
|
||||
})),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -6,70 +6,62 @@
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import XNotifications from '@/components/notifications.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { notificationTypes } from 'misskey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotifications
|
||||
},
|
||||
let tab = $ref('all');
|
||||
let includeTypes = $ref<string[] | null>(null);
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: this.$ts.notifications,
|
||||
icon: 'fas fa-bell',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
text: this.$ts.filter,
|
||||
icon: 'fas fa-filter',
|
||||
highlighted: this.includeTypes != null,
|
||||
handler: this.setFilter,
|
||||
}, {
|
||||
text: this.$ts.markAllAsRead,
|
||||
icon: 'fas fa-check',
|
||||
handler: () => {
|
||||
os.apiWithDialog('notifications/mark-all-as-read');
|
||||
},
|
||||
}],
|
||||
tabs: [{
|
||||
active: this.tab === 'all',
|
||||
title: this.$ts.all,
|
||||
onClick: () => { this.tab = 'all'; },
|
||||
}, {
|
||||
active: this.tab === 'unread',
|
||||
title: this.$ts.unread,
|
||||
onClick: () => { this.tab = 'unread'; },
|
||||
},]
|
||||
})),
|
||||
tab: 'all',
|
||||
includeTypes: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
setFilter(ev) {
|
||||
const typeItems = notificationTypes.map(t => ({
|
||||
text: this.$t(`_notification._types.${t}`),
|
||||
active: this.includeTypes && this.includeTypes.includes(t),
|
||||
action: () => {
|
||||
this.includeTypes = [t];
|
||||
}
|
||||
}));
|
||||
const items = this.includeTypes != null ? [{
|
||||
icon: 'fas fa-times',
|
||||
text: this.$ts.clear,
|
||||
action: () => {
|
||||
this.includeTypes = null;
|
||||
}
|
||||
}, null, ...typeItems] : typeItems;
|
||||
os.popupMenu(items, ev.currentTarget || ev.target);
|
||||
function setFilter(ev) {
|
||||
const typeItems = notificationTypes.map(t => ({
|
||||
text: i18n.t(`_notification._types.${t}`),
|
||||
active: includeTypes && includeTypes.includes(t),
|
||||
action: () => {
|
||||
includeTypes = [t];
|
||||
}
|
||||
}
|
||||
}));
|
||||
const items = includeTypes != null ? [{
|
||||
icon: 'fas fa-times',
|
||||
text: i18n.locale.clear,
|
||||
action: () => {
|
||||
includeTypes = null;
|
||||
}
|
||||
}, null, ...typeItems] : typeItems;
|
||||
os.popupMenu(items, ev.currentTarget || ev.target);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: i18n.locale.notifications,
|
||||
icon: 'fas fa-bell',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
text: i18n.locale.filter,
|
||||
icon: 'fas fa-filter',
|
||||
highlighted: includeTypes != null,
|
||||
handler: setFilter,
|
||||
}, {
|
||||
text: i18n.locale.markAllAsRead,
|
||||
icon: 'fas fa-check',
|
||||
handler: () => {
|
||||
os.apiWithDialog('notifications/mark-all-as-read');
|
||||
},
|
||||
}],
|
||||
tabs: [{
|
||||
active: tab === 'all',
|
||||
title: i18n.locale.all,
|
||||
onClick: () => { tab = 'all'; },
|
||||
}, {
|
||||
active: tab === 'unread',
|
||||
title: i18n.locale.unread,
|
||||
onClick: () => { tab = 'unread'; },
|
||||
},]
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<MkSpacer :content-max="700">
|
||||
<transition name="fade" mode="out-in">
|
||||
<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
|
||||
<div v-if="page" :key="page.id" v-size="{ max: [450] }" class="xcukqgmh">
|
||||
<div class="_block main">
|
||||
<!--
|
||||
@ -106,11 +106,11 @@ export default defineComponent({
|
||||
page: null,
|
||||
error: null,
|
||||
otherPostsPagination: {
|
||||
endpoint: 'users/pages',
|
||||
endpoint: 'users/pages' as const,
|
||||
limit: 6,
|
||||
params: () => ({
|
||||
params: computed(() => ({
|
||||
userId: this.page.user.id
|
||||
})
|
||||
})),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -62,15 +62,15 @@ export default defineComponent({
|
||||
})),
|
||||
tab: 'featured',
|
||||
featuredPagesPagination: {
|
||||
endpoint: 'pages/featured',
|
||||
endpoint: 'pages/featured' as const,
|
||||
noPaging: true,
|
||||
},
|
||||
myPagesPagination: {
|
||||
endpoint: 'i/pages',
|
||||
endpoint: 'i/pages' as const,
|
||||
limit: 5,
|
||||
},
|
||||
likedPagesPagination: {
|
||||
endpoint: 'i/page-likes',
|
||||
endpoint: 'i/page-likes' as const,
|
||||
limit: 5,
|
||||
},
|
||||
};
|
||||
|
@ -4,24 +4,18 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import MkSample from '@/components/sample.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkSample,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.preview,
|
||||
icon: 'fas fa-eye',
|
||||
},
|
||||
}
|
||||
},
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: i18n.locale.preview,
|
||||
icon: 'fas fa-eye',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,67 +1,53 @@
|
||||
<template>
|
||||
<FormBase v-if="token">
|
||||
<FormInput v-model="password" type="password">
|
||||
<template #prefix><i class="fas fa-lock"></i></template>
|
||||
<span>{{ $ts.newPassword }}</span>
|
||||
</FormInput>
|
||||
|
||||
<FormButton primary @click="save">{{ $ts.save }}</FormButton>
|
||||
</FormBase>
|
||||
<MkSpacer v-if="token" :content-max="700" :margin-min="16" :margin-max="32">
|
||||
<div class="_formRoot">
|
||||
<FormInput v-model="password" type="password" class="_formBlock">
|
||||
<template #prefix><i class="fas fa-lock"></i></template>
|
||||
<template #label>{{ i18n.locale.newPassword }}</template>
|
||||
</FormInput>
|
||||
|
||||
<FormButton primary class="_formBlock" @click="save">{{ i18n.locale.save }}</FormButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
import { router } from '@/router';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
FormInput,
|
||||
FormButton,
|
||||
},
|
||||
const props = defineProps<{
|
||||
token?: string;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
token: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
let password = $ref('');
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.resetPassword,
|
||||
icon: 'fas fa-lock'
|
||||
},
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
async function save() {
|
||||
await os.apiWithDialog('reset-password', {
|
||||
token: props.token,
|
||||
password: password,
|
||||
});
|
||||
router.push('/');
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.token == null) {
|
||||
os.popup(import('@/components/forgot-password.vue'), {}, {}, 'closed');
|
||||
this.$router.push('/');
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async save() {
|
||||
await os.apiWithDialog('reset-password', {
|
||||
token: this.token,
|
||||
password: this.password,
|
||||
});
|
||||
this.$router.push('/');
|
||||
}
|
||||
onMounted(() => {
|
||||
if (props.token == null) {
|
||||
os.popup(import('@/components/forgot-password.vue'), {}, {}, 'closed');
|
||||
router.push('/');
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.resetPassword,
|
||||
icon: 'fas fa-lock',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,528 +0,0 @@
|
||||
<template>
|
||||
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
|
||||
<header><b><MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA></b>({{ $ts._reversi.black }}) vs <b><MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA></b>({{ $ts._reversi.white }})</header>
|
||||
|
||||
<div style="overflow: hidden; line-height: 28px;">
|
||||
<p v-if="!iAmPlayer && !game.isEnded" class="turn">
|
||||
<Mfm :key="'turn:' + turnUser().name" :text="$t('_reversi.turnOf', { name: turnUser().name })" :plain="true" :custom-emojis="turnUser().emojis"/>
|
||||
<MkEllipsis/>
|
||||
</p>
|
||||
<p v-if="logPos != logs.length" class="turn">
|
||||
<Mfm :key="'past-turn-of:' + turnUser().name" :text="$t('_reversi.pastTurnOf', { name: turnUser().name })" :plain="true" :custom-emojis="turnUser().emojis"/>
|
||||
</p>
|
||||
<p v-if="iAmPlayer && !game.isEnded && !isMyTurn()" class="turn1">{{ $ts._reversi.opponentTurn }}<MkEllipsis/></p>
|
||||
<p v-if="iAmPlayer && !game.isEnded && isMyTurn()" class="turn2" style="animation: tada 1s linear infinite both;">{{ $ts._reversi.myTurn }}</p>
|
||||
<p v-if="game.isEnded && logPos == logs.length" class="result">
|
||||
<template v-if="game.winner">
|
||||
<Mfm :key="'won'" :text="$t('_reversi.won', { name: game.winner.name })" :plain="true" :custom-emojis="game.winner.emojis"/>
|
||||
<span v-if="game.surrendered != null"> ({{ $ts._reversi.surrendered }})</span>
|
||||
</template>
|
||||
<template v-else>{{ $ts._reversi.drawn }}</template>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="board">
|
||||
<div v-if="$store.state.gamesReversiShowBoardLabels" class="labels-x">
|
||||
<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div v-if="$store.state.gamesReversiShowBoardLabels" class="labels-y">
|
||||
<div v-for="i in game.map.length">{{ i }}</div>
|
||||
</div>
|
||||
<div class="cells" :style="cellsStyle">
|
||||
<div v-for="(stone, i) in o.board"
|
||||
:class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn(), can: turnUser() ? o.canPut(turnUser().id == blackUser.id, i) : null, prev: o.prevPos == i }"
|
||||
:title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`"
|
||||
@click="set(i)"
|
||||
>
|
||||
<template v-if="$store.state.gamesReversiUseAvatarStones || true">
|
||||
<img v-if="stone === true" :src="blackUser.avatarUrl" alt="black">
|
||||
<img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white">
|
||||
</template>
|
||||
<template v-else>
|
||||
<i v-if="stone === true" class="fas fa-circle"></i>
|
||||
<i v-if="stone === false" class="far fa-circle"></i>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$store.state.gamesReversiShowBoardLabels" class="labels-y">
|
||||
<div v-for="i in game.map.length">{{ i }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$store.state.gamesReversiShowBoardLabels" class="labels-x">
|
||||
<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="status"><b>{{ $t('_reversi.turnCount', { count: logPos }) }}</b> {{ $ts._reversi.black }}:{{ o.blackCount }} {{ $ts._reversi.white }}:{{ o.whiteCount }} {{ $ts._reversi.total }}:{{ o.blackCount + o.whiteCount }}</p>
|
||||
|
||||
<div v-if="!game.isEnded && iAmPlayer" class="actions">
|
||||
<MkButton inline @click="surrender">{{ $ts._reversi.surrender }}</MkButton>
|
||||
</div>
|
||||
|
||||
<div v-if="game.isEnded" class="player">
|
||||
<span>{{ logPos }} / {{ logs.length }}</span>
|
||||
<div v-if="!autoplaying" class="buttons">
|
||||
<MkButton inline :disabled="logPos == 0" @click="logPos = 0"><i class="fas fa-angle-double-left"></i></MkButton>
|
||||
<MkButton inline :disabled="logPos == 0" @click="logPos--"><i class="fas fa-angle-left"></i></MkButton>
|
||||
<MkButton inline :disabled="logPos == logs.length" @click="logPos++"><i class="fas fa-angle-right"></i></MkButton>
|
||||
<MkButton inline :disabled="logPos == logs.length" @click="logPos = logs.length"><i class="fas fa-angle-double-right"></i></MkButton>
|
||||
</div>
|
||||
<MkButton :disabled="autoplaying" style="margin: var(--margin) auto 0 auto;" @click="autoplay()"><i class="fas fa-play"></i></MkButton>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p v-if="game.isLlotheo">{{ $ts._reversi.isLlotheo }}</p>
|
||||
<p v-if="game.loopedBoard">{{ $ts._reversi.loopedMap }}</p>
|
||||
<p v-if="game.canPutEverywhere">{{ $ts._reversi.canPutEverywhere }}</p>
|
||||
</div>
|
||||
|
||||
<div class="watchers">
|
||||
<MkAvatar v-for="user in watchers" :key="user.id" :user="user" class="avatar"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as CRC32 from 'crc-32';
|
||||
import Reversi, { Color } from '@/scripts/games/reversi/core';
|
||||
import { url } from '@/config';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import * as sound from '@/scripts/sound';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton
|
||||
},
|
||||
|
||||
props: {
|
||||
initGame: {
|
||||
type: Object,
|
||||
require: true
|
||||
},
|
||||
connection: {
|
||||
type: Object,
|
||||
require: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
game: JSON.parse(JSON.stringify(this.initGame)),
|
||||
o: null as Reversi,
|
||||
logs: [],
|
||||
logPos: 0,
|
||||
watchers: [],
|
||||
pollingClock: null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
iAmPlayer(): boolean {
|
||||
if (!this.$i) return false;
|
||||
return this.game.user1Id == this.$i.id || this.game.user2Id == this.$i.id;
|
||||
},
|
||||
|
||||
myColor(): Color {
|
||||
if (!this.iAmPlayer) return null;
|
||||
if (this.game.user1Id == this.$i.id && this.game.black == 1) return true;
|
||||
if (this.game.user2Id == this.$i.id && this.game.black == 2) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
opColor(): Color {
|
||||
if (!this.iAmPlayer) return null;
|
||||
return this.myColor === true ? false : true;
|
||||
},
|
||||
|
||||
blackUser(): any {
|
||||
return this.game.black == 1 ? this.game.user1 : this.game.user2;
|
||||
},
|
||||
|
||||
whiteUser(): any {
|
||||
return this.game.black == 1 ? this.game.user2 : this.game.user1;
|
||||
},
|
||||
|
||||
cellsStyle(): any {
|
||||
return {
|
||||
'grid-template-rows': `repeat(${this.game.map.length}, 1fr)`,
|
||||
'grid-template-columns': `repeat(${this.game.map[0].length}, 1fr)`
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
logPos(v) {
|
||||
if (!this.game.isEnded) return;
|
||||
const o = new Reversi(this.game.map, {
|
||||
isLlotheo: this.game.isLlotheo,
|
||||
canPutEverywhere: this.game.canPutEverywhere,
|
||||
loopedBoard: this.game.loopedBoard
|
||||
});
|
||||
for (const log of this.logs.slice(0, v)) {
|
||||
o.put(log.color, log.pos);
|
||||
}
|
||||
this.o = o;
|
||||
//this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.o = new Reversi(this.game.map, {
|
||||
isLlotheo: this.game.isLlotheo,
|
||||
canPutEverywhere: this.game.canPutEverywhere,
|
||||
loopedBoard: this.game.loopedBoard
|
||||
});
|
||||
|
||||
for (const log of this.game.logs) {
|
||||
this.o.put(log.color, log.pos);
|
||||
}
|
||||
|
||||
this.logs = this.game.logs;
|
||||
this.logPos = this.logs.length;
|
||||
|
||||
// 通信を取りこぼしてもいいように定期的にポーリングさせる
|
||||
if (this.game.isStarted && !this.game.isEnded) {
|
||||
this.pollingClock = setInterval(() => {
|
||||
if (this.game.isEnded) return;
|
||||
const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join(''));
|
||||
this.connection.send('check', {
|
||||
crc32: crc32
|
||||
});
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection.on('set', this.onSet);
|
||||
this.connection.on('rescue', this.onRescue);
|
||||
this.connection.on('ended', this.onEnded);
|
||||
this.connection.on('watchers', this.onWatchers);
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
this.connection.off('set', this.onSet);
|
||||
this.connection.off('rescue', this.onRescue);
|
||||
this.connection.off('ended', this.onEnded);
|
||||
this.connection.off('watchers', this.onWatchers);
|
||||
|
||||
clearInterval(this.pollingClock);
|
||||
},
|
||||
|
||||
methods: {
|
||||
userPage,
|
||||
|
||||
// this.o がリアクティブになった折にはcomputedにできる
|
||||
turnUser(): any {
|
||||
if (this.o.turn === true) {
|
||||
return this.game.black == 1 ? this.game.user1 : this.game.user2;
|
||||
} else if (this.o.turn === false) {
|
||||
return this.game.black == 1 ? this.game.user2 : this.game.user1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// this.o がリアクティブになった折にはcomputedにできる
|
||||
isMyTurn(): boolean {
|
||||
if (!this.iAmPlayer) return false;
|
||||
if (this.turnUser() == null) return false;
|
||||
return this.turnUser().id == this.$i.id;
|
||||
},
|
||||
|
||||
set(pos) {
|
||||
if (this.game.isEnded) return;
|
||||
if (!this.iAmPlayer) return;
|
||||
if (!this.isMyTurn()) return;
|
||||
if (!this.o.canPut(this.myColor, pos)) return;
|
||||
|
||||
this.o.put(this.myColor, pos);
|
||||
|
||||
// サウンドを再生する
|
||||
sound.play(this.myColor ? 'reversiPutBlack' : 'reversiPutWhite');
|
||||
|
||||
this.connection.send('set', {
|
||||
pos: pos
|
||||
});
|
||||
|
||||
this.checkEnd();
|
||||
|
||||
this.$forceUpdate();
|
||||
},
|
||||
|
||||
onSet(x) {
|
||||
this.logs.push(x);
|
||||
this.logPos++;
|
||||
this.o.put(x.color, x.pos);
|
||||
this.checkEnd();
|
||||
this.$forceUpdate();
|
||||
|
||||
// サウンドを再生する
|
||||
if (x.color !== this.myColor) {
|
||||
sound.play(x.color ? 'reversiPutBlack' : 'reversiPutWhite');
|
||||
}
|
||||
},
|
||||
|
||||
onEnded(x) {
|
||||
this.game = JSON.parse(JSON.stringify(x.game));
|
||||
},
|
||||
|
||||
checkEnd() {
|
||||
this.game.isEnded = this.o.isEnded;
|
||||
if (this.game.isEnded) {
|
||||
if (this.o.winner === true) {
|
||||
this.game.winnerId = this.game.black == 1 ? this.game.user1Id : this.game.user2Id;
|
||||
this.game.winner = this.game.black == 1 ? this.game.user1 : this.game.user2;
|
||||
} else if (this.o.winner === false) {
|
||||
this.game.winnerId = this.game.black == 1 ? this.game.user2Id : this.game.user1Id;
|
||||
this.game.winner = this.game.black == 1 ? this.game.user2 : this.game.user1;
|
||||
} else {
|
||||
this.game.winnerId = null;
|
||||
this.game.winner = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 正しいゲーム情報が送られてきたとき
|
||||
onRescue(game) {
|
||||
this.game = JSON.parse(JSON.stringify(game));
|
||||
|
||||
this.o = new Reversi(this.game.map, {
|
||||
isLlotheo: this.game.isLlotheo,
|
||||
canPutEverywhere: this.game.canPutEverywhere,
|
||||
loopedBoard: this.game.loopedBoard
|
||||
});
|
||||
|
||||
for (const log of this.game.logs) {
|
||||
this.o.put(log.color, log.pos, true);
|
||||
}
|
||||
|
||||
this.logs = this.game.logs;
|
||||
this.logPos = this.logs.length;
|
||||
|
||||
this.checkEnd();
|
||||
this.$forceUpdate();
|
||||
},
|
||||
|
||||
onWatchers(users) {
|
||||
this.watchers = users;
|
||||
},
|
||||
|
||||
surrender() {
|
||||
os.api('games/reversi/games/surrender', {
|
||||
gameId: this.game.id
|
||||
});
|
||||
},
|
||||
|
||||
autoplay() {
|
||||
this.autoplaying = true;
|
||||
this.logPos = 0;
|
||||
|
||||
setTimeout(() => {
|
||||
this.logPos = 1;
|
||||
|
||||
let i = 1;
|
||||
let previousLog = this.game.logs[0];
|
||||
const tick = () => {
|
||||
const log = this.game.logs[i];
|
||||
const time = new Date(log.at).getTime() - new Date(previousLog.at).getTime()
|
||||
setTimeout(() => {
|
||||
i++;
|
||||
this.logPos++;
|
||||
previousLog = log;
|
||||
|
||||
if (i < this.game.logs.length) {
|
||||
tick();
|
||||
} else {
|
||||
this.autoplaying = false;
|
||||
}
|
||||
}, time);
|
||||
};
|
||||
|
||||
tick();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
.xqnhankfuuilcwvhgsopeqncafzsquya {
|
||||
text-align: center;
|
||||
|
||||
> .go-index {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 42px;
|
||||
height :42px;
|
||||
}
|
||||
|
||||
> header {
|
||||
padding: 8px;
|
||||
border-bottom: dashed 1px var(--divider);
|
||||
}
|
||||
|
||||
> .board {
|
||||
width: calc(100% - 16px);
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
|
||||
$label-size: 16px;
|
||||
$gap: 4px;
|
||||
|
||||
> .labels-x {
|
||||
height: $label-size;
|
||||
padding: 0 $label-size;
|
||||
display: flex;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.8em;
|
||||
|
||||
&:first-child {
|
||||
margin-left: -(math.div($gap, 2));
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: -(math.div($gap, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .flex {
|
||||
display: flex;
|
||||
|
||||
> .labels-y {
|
||||
width: $label-size;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: -(math.div($gap, 2));
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: -(math.div($gap, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .cells {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-gap: $gap;
|
||||
|
||||
> div {
|
||||
background: transparent;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
border: solid 2px var(--divider);
|
||||
}
|
||||
|
||||
&.empty.can {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
&.empty.myTurn {
|
||||
border-color: var(--divider);
|
||||
|
||||
&.can {
|
||||
border-color: var(--accent);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.prev {
|
||||
box-shadow: 0 0 0 4px var(--accent);
|
||||
}
|
||||
|
||||
&.isEnded {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
|
||||
&.none {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
> svg, > img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .status {
|
||||
margin: 0;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
> .actions {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
> .player {
|
||||
padding: 0 16px 32px 16px;
|
||||
margin: 0 auto;
|
||||
max-width: 500px;
|
||||
|
||||
> span {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
> .buttons {
|
||||
display: flex;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .watchers {
|
||||
padding: 0 0 16px 0;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,390 +0,0 @@
|
||||
<template>
|
||||
<div class="urbixznjwwuukfsckrwzwsqzsxornqij">
|
||||
<header><b><MkUserName :user="game.user1"/></b> vs <b><MkUserName :user="game.user2"/></b></header>
|
||||
|
||||
<div>
|
||||
<p>{{ $ts._reversi.gameSettings }}</p>
|
||||
|
||||
<div class="card map _panel">
|
||||
<header>
|
||||
<select v-model="mapName" :placeholder="$ts._reversi.chooseBoard" @change="onMapChange">
|
||||
<option v-if="mapName == '-Custom-'" label="-Custom-" :value="mapName"/>
|
||||
<option :label="$ts.random" :value="null"/>
|
||||
<optgroup v-for="c in mapCategories" :key="c" :label="c">
|
||||
<option v-for="m in Object.values(maps).filter(m => m.category == c)" :key="m.name" :label="m.name" :value="m.name">{{ m.name }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<div v-if="game.map == null" class="random"><i class="fas fa-dice"></i></div>
|
||||
<div v-else class="board" :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }">
|
||||
<div v-for="(x, i) in game.map.join('')" :class="{ none: x == ' ' }" @click="onPixelClick(i, x)">
|
||||
<i v-if="x === 'b'" class="fas fa-circle"></i>
|
||||
<i v-if="x === 'w'" class="far fa-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card _panel">
|
||||
<header>
|
||||
<span>{{ $ts._reversi.blackOrWhite }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<MkRadio v-model="game.bw" value="random" @update:modelValue="updateSettings('bw')">{{ $ts.random }}</MkRadio>
|
||||
<MkRadio v-model="game.bw" :value="'1'" @update:modelValue="updateSettings('bw')">
|
||||
<I18n :src="$ts._reversi.blackIs" tag="span">
|
||||
<template #name>
|
||||
<b><MkUserName :user="game.user1"/></b>
|
||||
</template>
|
||||
</I18n>
|
||||
</MkRadio>
|
||||
<MkRadio v-model="game.bw" :value="'2'" @update:modelValue="updateSettings('bw')">
|
||||
<I18n :src="$ts._reversi.blackIs" tag="span">
|
||||
<template #name>
|
||||
<b><MkUserName :user="game.user2"/></b>
|
||||
</template>
|
||||
</I18n>
|
||||
</MkRadio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card _panel">
|
||||
<header>
|
||||
<span>{{ $ts._reversi.rules }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<MkSwitch v-model="game.isLlotheo" @update:modelValue="updateSettings('isLlotheo')">{{ $ts._reversi.isLlotheo }}</MkSwitch>
|
||||
<MkSwitch v-model="game.loopedBoard" @update:modelValue="updateSettings('loopedBoard')">{{ $ts._reversi.loopedMap }}</MkSwitch>
|
||||
<MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ $ts._reversi.canPutEverywhere }}</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="form" class="card form _panel">
|
||||
<header>
|
||||
<span>{{ $ts._reversi.botSettings }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<template v-for="item in form">
|
||||
<MkSwitch v-if="item.type == 'switch'" :key="item.id" v-model="item.value" @change="onChangeForm(item)">{{ item.label || item.desc || '' }}</MkSwitch>
|
||||
|
||||
<div v-if="item.type == 'radio'" :key="item.id" class="card">
|
||||
<header>
|
||||
<span>{{ item.label }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<MkRadio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :value="r.value" @update:modelValue="onChangeForm(item)">{{ r.label }}</MkRadio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.type == 'slider'" :key="item.id" class="card">
|
||||
<header>
|
||||
<span>{{ item.label }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<input v-model="item.value" type="range" :min="item.min" :max="item.max" :step="item.step || 1" @change="onChangeForm(item)"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.type == 'textbox'" :key="item.id" class="card">
|
||||
<header>
|
||||
<span>{{ item.label }}</span>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<input v-model="item.value" @change="onChangeForm(item)"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="_acrylic">
|
||||
<p class="status">
|
||||
<template v-if="isAccepted && isOpAccepted">{{ $ts._reversi.thisGameIsStartedSoon }}<MkEllipsis/></template>
|
||||
<template v-if="isAccepted && !isOpAccepted">{{ $ts._reversi.waitingForOther }}<MkEllipsis/></template>
|
||||
<template v-if="!isAccepted && isOpAccepted">{{ $ts._reversi.waitingForMe }}</template>
|
||||
<template v-if="!isAccepted && !isOpAccepted">{{ $ts._reversi.waitingBoth }}<MkEllipsis/></template>
|
||||
</p>
|
||||
|
||||
<div class="actions">
|
||||
<MkButton inline @click="exit">{{ $ts.cancel }}</MkButton>
|
||||
<MkButton v-if="!isAccepted" inline primary @click="accept">{{ $ts._reversi.ready }}</MkButton>
|
||||
<MkButton v-if="isAccepted" inline primary @click="cancel">{{ $ts._reversi.cancelReady }}</MkButton>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as maps from '@/scripts/games/reversi/maps';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import MkRadio from '@/components/form/radio.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
MkSwitch,
|
||||
MkRadio,
|
||||
},
|
||||
|
||||
props: {
|
||||
initGame: {
|
||||
type: Object,
|
||||
require: true
|
||||
},
|
||||
connection: {
|
||||
type: Object,
|
||||
require: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
game: this.initGame,
|
||||
o: null,
|
||||
isLlotheo: false,
|
||||
mapName: maps.eighteight.name,
|
||||
maps: maps,
|
||||
form: null,
|
||||
messages: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
mapCategories(): string[] {
|
||||
const categories = Object.values(maps).map(x => x.category);
|
||||
return categories.filter((item, pos) => categories.indexOf(item) == pos);
|
||||
},
|
||||
isAccepted(): boolean {
|
||||
if (this.game.user1Id == this.$i.id && this.game.user1Accepted) return true;
|
||||
if (this.game.user2Id == this.$i.id && this.game.user2Accepted) return true;
|
||||
return false;
|
||||
},
|
||||
isOpAccepted(): boolean {
|
||||
if (this.game.user1Id != this.$i.id && this.game.user1Accepted) return true;
|
||||
if (this.game.user2Id != this.$i.id && this.game.user2Accepted) return true;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.connection.on('changeAccepts', this.onChangeAccepts);
|
||||
this.connection.on('updateSettings', this.onUpdateSettings);
|
||||
this.connection.on('initForm', this.onInitForm);
|
||||
this.connection.on('message', this.onMessage);
|
||||
|
||||
if (this.game.user1Id != this.$i.id && this.game.form1) this.form = this.game.form1;
|
||||
if (this.game.user2Id != this.$i.id && this.game.form2) this.form = this.game.form2;
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
this.connection.off('changeAccepts', this.onChangeAccepts);
|
||||
this.connection.off('updateSettings', this.onUpdateSettings);
|
||||
this.connection.off('initForm', this.onInitForm);
|
||||
this.connection.off('message', this.onMessage);
|
||||
},
|
||||
|
||||
methods: {
|
||||
exit() {
|
||||
|
||||
},
|
||||
|
||||
accept() {
|
||||
this.connection.send('accept', {});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.connection.send('cancelAccept', {});
|
||||
},
|
||||
|
||||
onChangeAccepts(accepts) {
|
||||
this.game.user1Accepted = accepts.user1;
|
||||
this.game.user2Accepted = accepts.user2;
|
||||
},
|
||||
|
||||
updateSettings(key: string) {
|
||||
this.connection.send('updateSettings', {
|
||||
key: key,
|
||||
value: this.game[key]
|
||||
});
|
||||
},
|
||||
|
||||
onUpdateSettings({ key, value }) {
|
||||
this.game[key] = value;
|
||||
if (this.game.map == null) {
|
||||
this.mapName = null;
|
||||
} else {
|
||||
const found = Object.values(maps).find(x => x.data.join('') == this.game.map.join(''));
|
||||
this.mapName = found ? found.name : '-Custom-';
|
||||
}
|
||||
},
|
||||
|
||||
onInitForm(x) {
|
||||
if (x.userId == this.$i.id) return;
|
||||
this.form = x.form;
|
||||
},
|
||||
|
||||
onMessage(x) {
|
||||
if (x.userId == this.$i.id) return;
|
||||
this.messages.unshift(x.message);
|
||||
},
|
||||
|
||||
onChangeForm(item) {
|
||||
this.connection.send('updateForm', {
|
||||
id: item.id,
|
||||
value: item.value
|
||||
});
|
||||
},
|
||||
|
||||
onMapChange() {
|
||||
if (this.mapName == null) {
|
||||
this.game.map = null;
|
||||
} else {
|
||||
this.game.map = Object.values(maps).find(x => x.name == this.mapName).data;
|
||||
}
|
||||
this.updateSettings('map');
|
||||
},
|
||||
|
||||
onPixelClick(pos, pixel) {
|
||||
const x = pos % this.game.map[0].length;
|
||||
const y = Math.floor(pos / this.game.map[0].length);
|
||||
const newPixel =
|
||||
pixel == ' ' ? '-' :
|
||||
pixel == '-' ? 'b' :
|
||||
pixel == 'b' ? 'w' :
|
||||
' ';
|
||||
const line = this.game.map[y].split('');
|
||||
line[x] = newPixel;
|
||||
this.game.map[y] = line.join('');
|
||||
this.updateSettings('map');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.urbixznjwwuukfsckrwzwsqzsxornqij {
|
||||
text-align: center;
|
||||
background: var(--bg);
|
||||
|
||||
> header {
|
||||
padding: 8px;
|
||||
border-bottom: dashed 1px #c4cdd4;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: 0 16px;
|
||||
|
||||
> .card {
|
||||
margin: 0 auto 16px auto;
|
||||
|
||||
&.map {
|
||||
> header {
|
||||
> select {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
background: var(--face);
|
||||
border: 1px solid var(--inputBorder);
|
||||
border-radius: 4px;
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
|
||||
&:focus-visible,
|
||||
&:active {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
> .random {
|
||||
padding: 32px 0;
|
||||
font-size: 64px;
|
||||
color: var(--fg);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .board {
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 0 auto;
|
||||
color: var(--fg);
|
||||
|
||||
> div {
|
||||
background: transparent;
|
||||
border: solid 2px var(--divider);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.none {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.form {
|
||||
> div {
|
||||
> .card + .card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
input[type='range'] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
max-width: 400px;
|
||||
|
||||
> header {
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid var(--divider);
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: 20px;
|
||||
color: var(--fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
padding: 16px;
|
||||
border-top: solid 1px var(--divider);
|
||||
|
||||
> .status {
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<div v-if="game == null"><MkLoading/></div>
|
||||
<GameSetting v-else-if="!game.isStarted" :init-game="game" :connection="connection"/>
|
||||
<GameBoard v-else :init-game="game" :connection="connection"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import GameSetting from './game.setting.vue';
|
||||
import GameBoard from './game.board.vue';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
GameSetting,
|
||||
GameBoard,
|
||||
},
|
||||
|
||||
props: {
|
||||
gameId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts._reversi.reversi,
|
||||
icon: 'fas fa-gamepad'
|
||||
},
|
||||
game: null,
|
||||
connection: null,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
gameId() {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.connection) {
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
os.api('games/reversi/games/show', {
|
||||
gameId: this.gameId
|
||||
}).then(game => {
|
||||
this.game = game;
|
||||
|
||||
if (this.connection) {
|
||||
this.connection.dispose();
|
||||
}
|
||||
this.connection = markRaw(stream.useChannel('gamesReversiGame', {
|
||||
gameId: this.game.id
|
||||
}));
|
||||
this.connection.on('started', this.onStarted);
|
||||
});
|
||||
},
|
||||
|
||||
onStarted(game) {
|
||||
Object.assign(this.game, game);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,280 +0,0 @@
|
||||
<template>
|
||||
<div v-if="!matching" class="bgvwxkhb">
|
||||
<h1>Misskey {{ $ts._reversi.reversi }}</h1>
|
||||
|
||||
<div class="play">
|
||||
<MkButton primary round style="margin: var(--margin) auto 0 auto;" @click="match">{{ $ts.invite }}</MkButton>
|
||||
</div>
|
||||
|
||||
<div class="_section">
|
||||
<MkFolder v-if="invitations.length > 0">
|
||||
<template #header>{{ $ts.invitations }}</template>
|
||||
<div class="nfcacttm">
|
||||
<button v-for="invitation in invitations" class="invitation _panel _button" tabindex="-1" @click="accept(invitation)">
|
||||
<MkAvatar class="avatar" :user="invitation.parent" :show-indicator="true"/>
|
||||
<span class="name"><b><MkUserName :user="invitation.parent"/></b></span>
|
||||
<span class="username">@{{ invitation.parent.username }}</span>
|
||||
<MkTime :time="invitation.createdAt" class="time"/>
|
||||
</button>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="myGames.length > 0">
|
||||
<template #header>{{ $ts._reversi.myGames }}</template>
|
||||
<div class="knextgwz">
|
||||
<MkA v-for="g in myGames" :key="g.id" class="game _panel" tabindex="-1" :to="`/games/reversi/${g.id}`">
|
||||
<div class="players">
|
||||
<MkAvatar class="avatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar class="avatar" :user="g.user2"/>
|
||||
</div>
|
||||
<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $ts._reversi.ended : $ts._reversi.playing }}</span><MkTime class="time" :time="g.createdAt"/></footer>
|
||||
</MkA>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="games.length > 0">
|
||||
<template #header>{{ $ts._reversi.allGames }}</template>
|
||||
<div class="knextgwz">
|
||||
<MkA v-for="g in games" :key="g.id" class="game _panel" tabindex="-1" :to="`/games/reversi/${g.id}`">
|
||||
<div class="players">
|
||||
<MkAvatar class="avatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar class="avatar" :user="g.user2"/>
|
||||
</div>
|
||||
<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $ts._reversi.ended : $ts._reversi.playing }}</span><MkTime class="time" :time="g.createdAt"/></footer>
|
||||
</MkA>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="sazhgisb">
|
||||
<h1>
|
||||
<I18n :src="$ts.waitingFor" tag="span">
|
||||
<template #x>
|
||||
<b><MkUserName :user="matching"/></b>
|
||||
</template>
|
||||
</I18n>
|
||||
<MkEllipsis/>
|
||||
</h1>
|
||||
<div class="cancel">
|
||||
<MkButton inline round @click="cancel">{{ $ts.cancel }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkFolder from '@/components/ui/folder.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton, MkFolder,
|
||||
},
|
||||
|
||||
inject: ['navHook'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts._reversi.reversi,
|
||||
icon: 'fas fa-gamepad'
|
||||
},
|
||||
games: [],
|
||||
gamesFetching: true,
|
||||
gamesMoreFetching: false,
|
||||
myGames: [],
|
||||
matching: null,
|
||||
invitations: [],
|
||||
connection: null,
|
||||
pingClock: null,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.$i) {
|
||||
this.connection = markRaw(stream.useChannel('gamesReversi'));
|
||||
|
||||
this.connection.on('invited', this.onInvited);
|
||||
|
||||
this.connection.on('matched', this.onMatched);
|
||||
|
||||
this.pingClock = setInterval(() => {
|
||||
if (this.matching) {
|
||||
this.connection.send('ping', {
|
||||
id: this.matching.id
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
os.api('games/reversi/games', {
|
||||
my: true
|
||||
}).then(games => {
|
||||
this.myGames = games;
|
||||
});
|
||||
|
||||
os.api('games/reversi/invitations').then(invitations => {
|
||||
this.invitations = this.invitations.concat(invitations);
|
||||
});
|
||||
}
|
||||
|
||||
os.api('games/reversi/games').then(games => {
|
||||
this.games = games;
|
||||
this.gamesFetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.connection) {
|
||||
this.connection.dispose();
|
||||
clearInterval(this.pingClock);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
go(game) {
|
||||
const url = '/games/reversi/' + game.id;
|
||||
if (this.navHook) {
|
||||
this.navHook(url);
|
||||
} else {
|
||||
this.$router.push(url);
|
||||
}
|
||||
},
|
||||
|
||||
async match() {
|
||||
const user = await os.selectUser({ local: true });
|
||||
if (user == null) return;
|
||||
os.api('games/reversi/match', {
|
||||
userId: user.id
|
||||
}).then(res => {
|
||||
if (res == null) {
|
||||
this.matching = user;
|
||||
} else {
|
||||
this.go(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.matching = null;
|
||||
os.api('games/reversi/match/cancel');
|
||||
},
|
||||
|
||||
accept(invitation) {
|
||||
os.api('games/reversi/match', {
|
||||
userId: invitation.parent.id
|
||||
}).then(game => {
|
||||
if (game) {
|
||||
this.go(game);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onMatched(game) {
|
||||
this.go(game);
|
||||
},
|
||||
|
||||
onInvited(invite) {
|
||||
this.invitations.unshift(invite);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bgvwxkhb {
|
||||
> h1 {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
background: linear-gradient(0deg, #43c583, #438881);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
> .play {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.sazhgisb {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nfcacttm {
|
||||
> .invitation {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
line-height: 32px;
|
||||
text-align: left;
|
||||
|
||||
> .avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
> .name {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
> .username {
|
||||
margin-right: 8px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .time {
|
||||
margin-left: auto;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.knextgwz {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-gap: var(--margin);
|
||||
|
||||
> .game {
|
||||
> .players {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
line-height: 32px;
|
||||
|
||||
> .avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
border-top: solid 0.5px var(--divider);
|
||||
padding: 6px 8px;
|
||||
font-size: 0.9em;
|
||||
|
||||
> .state {
|
||||
&.playing {
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
> .time {
|
||||
margin-left: auto;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<canvas width="224" height="128"></canvas>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as THREE from 'three';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
selected: null,
|
||||
objectHeight: 0,
|
||||
orbitRadius: 5
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const canvas = this.$el;
|
||||
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas: canvas,
|
||||
antialias: true,
|
||||
alpha: false
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(width, height);
|
||||
renderer.setClearColor(0x000000);
|
||||
renderer.autoClear = false;
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.cullFace = THREE.CullFaceBack;
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
|
||||
camera.zoom = 10;
|
||||
camera.position.x = 0;
|
||||
camera.position.y = 2;
|
||||
camera.position.z = 0;
|
||||
camera.updateProjectionMatrix();
|
||||
scene.add(camera);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
|
||||
ambientLight.castShadow = false;
|
||||
scene.add(ambientLight);
|
||||
|
||||
const light = new THREE.PointLight(0xffffff, 1, 100);
|
||||
light.position.set(3, 3, 3);
|
||||
scene.add(light);
|
||||
|
||||
const grid = new THREE.GridHelper(5, 16, 0x444444, 0x222222);
|
||||
scene.add(grid);
|
||||
|
||||
const render = () => {
|
||||
const timer = Date.now() * 0.0004;
|
||||
requestAnimationFrame(render);
|
||||
|
||||
camera.position.y = Math.sin(Math.PI / 6) * this.orbitRadius; // Math.PI / 6 => 30deg
|
||||
camera.position.z = Math.cos(timer) * this.orbitRadius;
|
||||
camera.position.x = Math.sin(timer) * this.orbitRadius;
|
||||
camera.lookAt(new THREE.Vector3(0, this.objectHeight / 2, 0));
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
this.selected = selected => {
|
||||
const obj = selected.clone();
|
||||
|
||||
// Remove current object
|
||||
const current = scene.getObjectByName('obj');
|
||||
if (current != null) {
|
||||
scene.remove(current);
|
||||
}
|
||||
|
||||
// Add new object
|
||||
obj.name = 'obj';
|
||||
obj.position.x = 0;
|
||||
obj.position.y = 0;
|
||||
obj.position.z = 0;
|
||||
obj.rotation.x = 0;
|
||||
obj.rotation.y = 0;
|
||||
obj.rotation.z = 0;
|
||||
obj.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
child.material = child.material.clone();
|
||||
return child.material.emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
const objectBoundingBox = new THREE.Box3().setFromObject(obj);
|
||||
this.objectHeight = objectBoundingBox.max.y - objectBoundingBox.min.y;
|
||||
|
||||
const objectWidth = objectBoundingBox.max.x - objectBoundingBox.min.x;
|
||||
const objectDepth = objectBoundingBox.max.z - objectBoundingBox.min.z;
|
||||
|
||||
const horizontal = Math.hypot(objectWidth, objectDepth) / camera.aspect;
|
||||
this.orbitRadius = Math.max(horizontal, this.objectHeight) * camera.zoom * 0.625 / Math.tan(camera.fov * 0.5 * (Math.PI / 180));
|
||||
|
||||
scene.add(obj);
|
||||
};
|
||||
|
||||
render();
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,279 +0,0 @@
|
||||
<template>
|
||||
<div class="hveuntkp">
|
||||
<div v-if="objectSelected" class="controller _section">
|
||||
<div class="_content">
|
||||
<p class="name">{{ selectedFurnitureName }}</p>
|
||||
<XPreview ref="preview"/>
|
||||
<template v-if="selectedFurnitureInfo.props">
|
||||
<div v-for="k in Object.keys(selectedFurnitureInfo.props)" :key="k">
|
||||
<p>{{ k }}</p>
|
||||
<template v-if="selectedFurnitureInfo.props[k] === 'image'">
|
||||
<MkButton @click="chooseImage(k, $event)">{{ $ts._rooms.chooseImage }}</MkButton>
|
||||
</template>
|
||||
<template v-else-if="selectedFurnitureInfo.props[k] === 'color'">
|
||||
<input type="color" :value="selectedFurnitureProps ? selectedFurnitureProps[k] : null" @change="updateColor(k, $event)"/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<MkButton inline :primary="isTranslateMode" @click="translate()"><i class="fas fa-arrows-alt"></i> {{ $ts._rooms.translate }}</MkButton>
|
||||
<MkButton inline :primary="isRotateMode" @click="rotate()"><i class="fas fa-undo"></i> {{ $ts._rooms.rotate }}</MkButton>
|
||||
<MkButton v-if="isTranslateMode || isRotateMode" inline @click="exit()"><i class="fas fa-ban"></i> {{ $ts._rooms.exit }}</MkButton>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<MkButton @click="remove()"><i class="fas fa-trash-alt"></i> {{ $ts._rooms.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isMyRoom" class="menu _section">
|
||||
<div class="_content">
|
||||
<MkButton @click="add()"><i class="fas fa-box-open"></i> {{ $ts._rooms.addFurniture }}</MkButton>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<MkSelect :model-value="roomType" @update:modelValue="updateRoomType($event)">
|
||||
<template #label>{{ $ts._rooms.roomType }}</template>
|
||||
<option value="default">{{ $ts._rooms._roomType.default }}</option>
|
||||
<option value="washitsu">{{ $ts._rooms._roomType.washitsu }}</option>
|
||||
</MkSelect>
|
||||
<label v-if="roomType === 'default'">
|
||||
<span>{{ $ts._rooms.carpetColor }}</span>
|
||||
<input type="color" :value="carpetColor" @change="updateCarpetColor($event)"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<MkButton inline :disabled="!changed" primary @click="save()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
|
||||
<MkButton inline @click="clear()"><i class="fas fa-broom"></i> {{ $ts._rooms.clear }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { Room } from '@/scripts/room/room';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import XPreview from './preview.vue';
|
||||
const storeItems = require('@/scripts/room/furnitures.json5');
|
||||
import { query as urlQuery } from '@/scripts/url';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import { selectFile } from '@/scripts/select-file';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
let room: Room;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XPreview,
|
||||
MkButton,
|
||||
MkSelect,
|
||||
},
|
||||
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (this.changed) {
|
||||
os.confirm({
|
||||
type: 'warning',
|
||||
text: this.$ts.leaveConfirm,
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
next(false);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
acct: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: computed(() => this.user ? {
|
||||
title: this.$ts.room,
|
||||
avatar: this.user,
|
||||
} : null),
|
||||
user: null,
|
||||
objectSelected: false,
|
||||
selectedFurnitureName: null,
|
||||
selectedFurnitureInfo: null,
|
||||
selectedFurnitureProps: null,
|
||||
roomType: null,
|
||||
carpetColor: null,
|
||||
isTranslateMode: false,
|
||||
isRotateMode: false,
|
||||
isMyRoom: false,
|
||||
changed: false,
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
window.addEventListener('beforeunload', this.beforeunload);
|
||||
|
||||
this.user = await os.api('users/show', {
|
||||
...Acct.parse(this.acct)
|
||||
});
|
||||
|
||||
this.isMyRoom = this.$i && (this.$i.id === this.user.id);
|
||||
|
||||
const roomInfo = await os.api('room/show', {
|
||||
userId: this.user.id
|
||||
});
|
||||
|
||||
this.roomType = roomInfo.roomType;
|
||||
this.carpetColor = roomInfo.carpetColor;
|
||||
|
||||
room = new Room(this.user, this.isMyRoom, roomInfo, this.$el, {
|
||||
graphicsQuality: ColdDeviceStorage.get('roomGraphicsQuality'),
|
||||
onChangeSelect: obj => {
|
||||
this.objectSelected = obj != null;
|
||||
if (obj) {
|
||||
const f = room.findFurnitureById(obj.name);
|
||||
this.selectedFurnitureName = this.$t('_rooms._furnitures.' + f.type);
|
||||
this.selectedFurnitureInfo = storeItems.find(x => x.id === f.type);
|
||||
this.selectedFurnitureProps = f.props
|
||||
? JSON.parse(JSON.stringify(f.props)) // Disable reactivity
|
||||
: null;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.preview.selected(obj);
|
||||
});
|
||||
}
|
||||
},
|
||||
useOrthographicCamera: ColdDeviceStorage.get('roomUseOrthographicCamera'),
|
||||
});
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
room.destroy();
|
||||
window.removeEventListener('beforeunload', this.beforeunload);
|
||||
},
|
||||
|
||||
methods: {
|
||||
beforeunload(e: BeforeUnloadEvent) {
|
||||
if (this.changed) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
},
|
||||
|
||||
async add() {
|
||||
const { canceled, result: id } = await os.select({
|
||||
title: this.$ts._rooms.addFurniture,
|
||||
items: storeItems.map(item => ({
|
||||
value: item.id, text: this.$t('_rooms._furnitures.' + item.id)
|
||||
}))
|
||||
});
|
||||
if (canceled) return;
|
||||
room.addFurniture(id);
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
remove() {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = false;
|
||||
room.removeFurniture();
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
save() {
|
||||
os.api('room/update', {
|
||||
room: room.getRoomInfo()
|
||||
}).then(() => {
|
||||
this.changed = false;
|
||||
os.success();
|
||||
}).catch((e: any) => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e.message
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clear() {
|
||||
os.confirm({
|
||||
type: 'warning',
|
||||
text: this.$ts._rooms.clearConfirm,
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
room.removeAllFurnitures();
|
||||
this.changed = true;
|
||||
});
|
||||
},
|
||||
|
||||
chooseImage(key, e) {
|
||||
selectFile(e.currentTarget || e.target, null).then(file => {
|
||||
room.updateProp(key, `/proxy/?${urlQuery({ url: file.thumbnailUrl })}`);
|
||||
this.$refs.preview.selected(room.getSelectedObject());
|
||||
this.changed = true;
|
||||
});
|
||||
},
|
||||
|
||||
updateColor(key, ev) {
|
||||
room.updateProp(key, ev.target.value);
|
||||
this.$refs.preview.selected(room.getSelectedObject());
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
updateCarpetColor(ev) {
|
||||
room.updateCarpetColor(ev.target.value);
|
||||
this.carpetColor = ev.target.value;
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
updateRoomType(type) {
|
||||
room.changeRoomType(type);
|
||||
this.roomType = type;
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
translate() {
|
||||
if (this.isTranslateMode) {
|
||||
this.exit();
|
||||
} else {
|
||||
this.isRotateMode = false;
|
||||
this.isTranslateMode = true;
|
||||
room.enterTransformMode('translate');
|
||||
}
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
rotate() {
|
||||
if (this.isRotateMode) {
|
||||
this.exit();
|
||||
} else {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = true;
|
||||
room.enterTransformMode('rotate');
|
||||
}
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
exit() {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = false;
|
||||
room.exitTransformMode();
|
||||
this.changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hveuntkp {
|
||||
position: relative;
|
||||
min-height: 500px;
|
||||
|
||||
> ::v-deep(canvas) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,37 +6,31 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
const props = defineProps<{
|
||||
query: string;
|
||||
channel?: string;
|
||||
}>();
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
|
||||
icon: 'fas fa-search',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/search',
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
query: this.$route.query.q,
|
||||
channelId: this.$route.query.channel,
|
||||
})
|
||||
},
|
||||
};
|
||||
},
|
||||
const pagination = {
|
||||
endpoint: 'notes/search' as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
query: props.query,
|
||||
channelId: props.channel,
|
||||
}))
|
||||
};
|
||||
|
||||
watch: {
|
||||
$route() {
|
||||
(this.$refs.notes as any).reload();
|
||||
}
|
||||
},
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: computed(() => ({
|
||||
title: i18n.t('searchWith', { q: props.query }),
|
||||
icon: 'fas fa-search',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
@ -71,9 +71,6 @@ import MkButton from '@/components/ui/button.vue';
|
||||
import MkInfo from '@/components/ui/info.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
|
@ -1,144 +1,135 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormKeyValueView>
|
||||
<div class="_formRoot">
|
||||
<MkKeyValue>
|
||||
<template #key>ID</template>
|
||||
<template #value><span class="_monospace">{{ $i.id }}</span></template>
|
||||
</FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<FormSection>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ $ts.registeredDate }}</template>
|
||||
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
||||
<FormGroup v-if="stats">
|
||||
<FormSection v-if="stats">
|
||||
<template #label>{{ $ts.statistics }}</template>
|
||||
<FormKeyValueView>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.notesCount }}</template>
|
||||
<template #value>{{ number(stats.notesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.repliesCount }}</template>
|
||||
<template #value>{{ number(stats.repliesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.renotesCount }}</template>
|
||||
<template #value>{{ number(stats.renotesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.repliedCount }}</template>
|
||||
<template #value>{{ number(stats.repliedCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.renotedCount }}</template>
|
||||
<template #value>{{ number(stats.renotedCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.pollVotesCount }}</template>
|
||||
<template #value>{{ number(stats.pollVotesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.pollVotedCount }}</template>
|
||||
<template #value>{{ number(stats.pollVotedCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.sentReactionsCount }}</template>
|
||||
<template #value>{{ number(stats.sentReactionsCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.receivedReactionsCount }}</template>
|
||||
<template #value>{{ number(stats.receivedReactionsCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.noteFavoritesCount }}</template>
|
||||
<template #value>{{ number(stats.noteFavoritesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followingCount }}</template>
|
||||
<template #value>{{ number(stats.followingCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followingCount }} ({{ $ts.local }})</template>
|
||||
<template #value>{{ number(stats.localFollowingCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followingCount }} ({{ $ts.remote }})</template>
|
||||
<template #value>{{ number(stats.remoteFollowingCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followersCount }}</template>
|
||||
<template #value>{{ number(stats.followersCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followersCount }} ({{ $ts.local }})</template>
|
||||
<template #value>{{ number(stats.localFollowersCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.followersCount }} ({{ $ts.remote }})</template>
|
||||
<template #value>{{ number(stats.remoteFollowersCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.pageLikesCount }}</template>
|
||||
<template #value>{{ number(stats.pageLikesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.pageLikedCount }}</template>
|
||||
<template #value>{{ number(stats.pageLikedCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.driveFilesCount }}</template>
|
||||
<template #value>{{ number(stats.driveFilesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>{{ $ts.driveUsage }}</template>
|
||||
<template #value>{{ bytes(stats.driveUsage) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.reversiCount }}</template>
|
||||
<template #value>{{ number(stats.reversiCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
|
||||
<FormGroup>
|
||||
<FormSection>
|
||||
<template #label>{{ $ts.other }}</template>
|
||||
<FormKeyValueView>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>emailVerified</template>
|
||||
<template #value>{{ $i.emailVerified ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>twoFactorEnabled</template>
|
||||
<template #value>{{ $i.twoFactorEnabled ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>securityKeys</template>
|
||||
<template #value>{{ $i.securityKeys ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>usePasswordLessLogin</template>
|
||||
<template #value>{{ $i.usePasswordLessLogin ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>isModerator</template>
|
||||
<template #value>{{ $i.isModerator ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue oneline style="margin: 1em 0;">
|
||||
<template #key>isAdmin</template>
|
||||
<template #value>{{ $i.isAdmin ? $ts.yes : $ts.no }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
</MkKeyValue>
|
||||
</FormSection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
import bytes from '@/filters/bytes';
|
||||
@ -146,13 +137,8 @@ import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormSelect,
|
||||
FormSwitch,
|
||||
FormButton,
|
||||
FormLink,
|
||||
FormGroup,
|
||||
FormKeyValueView,
|
||||
FormSection,
|
||||
MkKeyValue,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -168,8 +154,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
|
||||
os.api('users/stats', {
|
||||
userId: this.$i.id
|
||||
}).then(stats => {
|
||||
|
@ -1,41 +1,35 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div class="_formRoot">
|
||||
<FormSuspense :p="init">
|
||||
<FormButton primary @click="addAccount"><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton>
|
||||
|
||||
<div v-for="account in accounts" :key="account.id" class="_debobigegoItem _button" @click="menu(account, $event)">
|
||||
<div class="_debobigegoPanel lcjjdxlm">
|
||||
<div class="avatar">
|
||||
<MkAvatar :user="account" class="avatar"/>
|
||||
<div v-for="account in accounts" :key="account.id" class="_panel _button lcjjdxlm" @click="menu(account, $event)">
|
||||
<div class="avatar">
|
||||
<MkAvatar :user="account" class="avatar"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="name">
|
||||
<MkUserName :user="account"/>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="name">
|
||||
<MkUserName :user="account"/>
|
||||
</div>
|
||||
<div class="acct">
|
||||
<MkAcct :user="account"/>
|
||||
</div>
|
||||
<div class="acct">
|
||||
<MkAcct :user="account"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { getAccounts, addAccount, login } from '@/account';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormSuspense,
|
||||
FormButton,
|
||||
},
|
||||
@ -59,10 +53,6 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
menu(account, ev) {
|
||||
os.popupMenu([{
|
||||
|
@ -1,25 +1,20 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormButton primary @click="generateToken">{{ $ts.generateAccessToken }}</FormButton>
|
||||
<FormLink to="/settings/apps">{{ $ts.manageAccessTokens }}</FormLink>
|
||||
<FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink>
|
||||
</FormBase>
|
||||
<div class="_formRoot">
|
||||
<FormButton primary class="_formBlock" @click="generateToken">{{ $ts.generateAccessToken }}</FormButton>
|
||||
<FormLink to="/settings/apps" class="_formBlock">{{ $ts.manageAccessTokens }}</FormLink>
|
||||
<FormLink to="/api-console" :behavior="isDesktop ? 'window' : null" class="_formBlock">API console</FormLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormButton,
|
||||
FormLink,
|
||||
},
|
||||
@ -37,10 +32,6 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateToken() {
|
||||
os.popup(import('@/components/token-generate-window.vue'), {}, {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div class="_formRoot">
|
||||
<FormPagination ref="list" :pagination="pagination">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
@ -8,7 +8,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot="{items}">
|
||||
<div v-for="token in items" :key="token.id" class="_debobigegoPanel bfomjevm">
|
||||
<div v-for="token in items" :key="token.id" class="_panel bfomjevm">
|
||||
<img v-if="token.iconUrl" class="icon" :src="token.iconUrl" alt=""/>
|
||||
<div class="body">
|
||||
<div class="name">{{ token.name }}</div>
|
||||
@ -34,23 +34,17 @@
|
||||
</div>
|
||||
</template>
|
||||
</FormPagination>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormPagination from '@/components/debobigego/pagination.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormPagination from '@/components/ui/pagination.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormPagination,
|
||||
},
|
||||
|
||||
@ -64,7 +58,7 @@ export default defineComponent({
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'i/apps',
|
||||
endpoint: 'i/apps' as const,
|
||||
limit: 100,
|
||||
params: {
|
||||
sort: '+lastUsedAt'
|
||||
@ -73,10 +67,6 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
revoke(token) {
|
||||
os.api('i/revoke-token', { tokenId: token.id }).then(() => {
|
||||
|
@ -1,25 +1,18 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormInfo warn>{{ $ts.customCssWarn }}</FormInfo>
|
||||
<div class="_formRoot">
|
||||
<FormInfo warn class="_formBlock">{{ $ts.customCssWarn }}</FormInfo>
|
||||
|
||||
<FormTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;">
|
||||
<span>{{ $ts.local }}</span>
|
||||
<FormTextarea v-model="localCustomCss" manual-save tall class="_monospace _formBlock" style="tab-size: 2;">
|
||||
<template #label>CSS</template>
|
||||
</FormTextarea>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
import * as symbols from '@/symbols';
|
||||
import { defaultStore } from '@/store';
|
||||
@ -27,12 +20,6 @@ import { defaultStore } from '@/store';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormTextarea,
|
||||
FormSelect,
|
||||
FormRadios,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
FormButton,
|
||||
FormInfo,
|
||||
},
|
||||
|
||||
@ -50,8 +37,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
|
||||
this.$watch('localCustomCss', this.apply);
|
||||
},
|
||||
|
||||
|
@ -1,42 +1,41 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div class="_formRoot">
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts.defaultNavigationBehaviour }}</template>
|
||||
<FormSwitch v-model="navWindow">{{ $ts.openInWindow }}</FormSwitch>
|
||||
</FormGroup>
|
||||
|
||||
<FormSwitch v-model="alwaysShowMainColumn">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
|
||||
<FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
|
||||
|
||||
<FormRadios v-model="columnAlign">
|
||||
<template #desc>{{ $ts._deck.columnAlign }}</template>
|
||||
<FormRadios v-model="columnAlign" class="_formBlock">
|
||||
<template #label>{{ $ts._deck.columnAlign }}</template>
|
||||
<option value="left">{{ $ts.left }}</option>
|
||||
<option value="center">{{ $ts.center }}</option>
|
||||
</FormRadios>
|
||||
|
||||
<FormRadios v-model="columnHeaderHeight">
|
||||
<template #desc>{{ $ts._deck.columnHeaderHeight }}</template>
|
||||
<FormRadios v-model="columnHeaderHeight" class="_formBlock">
|
||||
<template #label>{{ $ts._deck.columnHeaderHeight }}</template>
|
||||
<option :value="42">{{ $ts.narrow }}</option>
|
||||
<option :value="45">{{ $ts.medium }}</option>
|
||||
<option :value="48">{{ $ts.wide }}</option>
|
||||
</FormRadios>
|
||||
|
||||
<FormInput v-model="columnMargin" type="number">
|
||||
<span>{{ $ts._deck.columnMargin }}</span>
|
||||
<FormInput v-model="columnMargin" type="number" class="_formBlock">
|
||||
<template #label>{{ $ts._deck.columnMargin }}</template>
|
||||
<template #suffix>px</template>
|
||||
</FormInput>
|
||||
|
||||
<FormLink @click="setProfile">{{ $ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
|
||||
</FormBase>
|
||||
<FormLink class="_formBlock" @click="setProfile">{{ $ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormRadios from '@/components/debobigego/radios.vue';
|
||||
import FormInput from '@/components/debobigego/input.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormGroup from '@/components/form/group.vue';
|
||||
import { deckStore } from '@/ui/deck/deck-store';
|
||||
import * as os from '@/os';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
@ -48,7 +47,6 @@ export default defineComponent({
|
||||
FormLink,
|
||||
FormInput,
|
||||
FormRadios,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
},
|
||||
|
||||
@ -85,10 +83,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async setProfile() {
|
||||
const { canceled, result: name } = await os.inputText({
|
||||
|
@ -1,28 +1,23 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormInfo warn>{{ $ts._accountDelete.mayTakeTime }}</FormInfo>
|
||||
<FormInfo>{{ $ts._accountDelete.sendEmail }}</FormInfo>
|
||||
<FormButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ $ts._accountDelete.requestAccountDelete }}</FormButton>
|
||||
<div class="_formRoot">
|
||||
<FormInfo warn class="_formBlock">{{ $ts._accountDelete.mayTakeTime }}</FormInfo>
|
||||
<FormInfo class="_formBlock">{{ $ts._accountDelete.sendEmail }}</FormInfo>
|
||||
<FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ $ts._accountDelete.requestAccountDelete }}</FormButton>
|
||||
<FormButton v-else disabled>{{ $ts._accountDelete.inProgress }}</FormButton>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import { debug } from '@/config';
|
||||
import { signout } from '@/account';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormButton,
|
||||
FormGroup,
|
||||
FormInfo,
|
||||
},
|
||||
|
||||
@ -35,14 +30,9 @@ export default defineComponent({
|
||||
icon: 'fas fa-exclamation-triangle',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
debug,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async deleteAccount() {
|
||||
{
|
||||
|
@ -99,10 +99,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
chooseUploadFolder() {
|
||||
os.selectDriveFolder(false).then(async folder => {
|
||||
|
@ -41,8 +41,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, watch } from 'vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
@ -54,8 +52,6 @@ import { i18n } from '@/i18n';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSection,
|
||||
FormLink,
|
||||
FormButton,
|
||||
FormSwitch,
|
||||
FormInput,
|
||||
},
|
||||
@ -115,8 +111,6 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
context.emit('info', INFO);
|
||||
|
||||
watch(emailAddress, () => {
|
||||
saveEmailAddress();
|
||||
});
|
||||
|
@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormButton @click="error()">error test</FormButton>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormSelect,
|
||||
FormSwitch,
|
||||
FormButton,
|
||||
FormLink,
|
||||
FormGroup,
|
||||
FormKeyValueView,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.experimentalFeatures,
|
||||
icon: 'fas fa-flask'
|
||||
},
|
||||
stats: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
error() {
|
||||
throw new Error('Test error');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -195,10 +195,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async reloadAsk() {
|
||||
const { canceled } = await os.confirm({
|
||||
|
@ -133,10 +133,6 @@ export default defineComponent({
|
||||
os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
context.emit('info', INFO);
|
||||
});
|
||||
|
||||
return {
|
||||
[symbols.PAGE_INFO]: INFO,
|
||||
excludeMutingUsers,
|
||||
|
@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="bkzroven">
|
||||
<component :is="component" :key="page" v-bind="pageProps" @info="onInfo"/>
|
||||
<component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -215,19 +215,9 @@ export default defineComponent({
|
||||
case 'deck': return defineAsyncComponent(() => import('./deck.vue'));
|
||||
case 'plugin': return defineAsyncComponent(() => import('./plugin.vue'));
|
||||
case 'plugin/install': return defineAsyncComponent(() => import('./plugin.install.vue'));
|
||||
case 'plugin/manage': return defineAsyncComponent(() => import('./plugin.manage.vue'));
|
||||
case 'import-export': return defineAsyncComponent(() => import('./import-export.vue'));
|
||||
case 'account-info': return defineAsyncComponent(() => import('./account-info.vue'));
|
||||
case 'update': return defineAsyncComponent(() => import('./update.vue'));
|
||||
case 'registry': return defineAsyncComponent(() => import('./registry.vue'));
|
||||
case 'delete-account': return defineAsyncComponent(() => import('./delete-account.vue'));
|
||||
case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue'));
|
||||
}
|
||||
if (page.value.startsWith('registry/keys/system/')) {
|
||||
return defineAsyncComponent(() => import('./registry.keys.vue'));
|
||||
}
|
||||
if (page.value.startsWith('registry/value/system/')) {
|
||||
return defineAsyncComponent(() => import('./registry.value.vue'));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@ -235,17 +225,6 @@ export default defineComponent({
|
||||
watch(component, () => {
|
||||
pageProps.value = {};
|
||||
|
||||
if (page.value) {
|
||||
if (page.value.startsWith('registry/keys/system/')) {
|
||||
pageProps.value.scope = page.value.replace('registry/keys/system/', '').split('/');
|
||||
}
|
||||
if (page.value.startsWith('registry/value/system/')) {
|
||||
const path = page.value.replace('registry/value/system/', '').split('/');
|
||||
pageProps.value.xKey = path.pop();
|
||||
pageProps.value.scope = path;
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
scroll(el.value, { top: 0 });
|
||||
});
|
||||
@ -271,8 +250,9 @@ export default defineComponent({
|
||||
|
||||
const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified));
|
||||
|
||||
const onInfo = (info) => {
|
||||
childInfo.value = info;
|
||||
const pageChanged = (page) => {
|
||||
if (page == null) return;
|
||||
childInfo.value = page[symbols.PAGE_INFO];
|
||||
};
|
||||
|
||||
return {
|
||||
@ -285,7 +265,7 @@ export default defineComponent({
|
||||
pageProps,
|
||||
component,
|
||||
emailNotConfigured,
|
||||
onInfo,
|
||||
pageChanged,
|
||||
childInfo,
|
||||
};
|
||||
},
|
||||
|
@ -47,11 +47,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
|
||||
async created() {
|
||||
this.instanceMutes = this.$i.mutedInstances.join('\n');
|
||||
},
|
||||
|
@ -1,45 +1,39 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div v-if="enableTwitterIntegration" class="_debobigegoItem">
|
||||
<div class="_debobigegoLabel"><i class="fab fa-twitter"></i> Twitter</div>
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
|
||||
<MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectTwitter">{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_formRoot">
|
||||
<FormSection v-if="enableTwitterIntegration">
|
||||
<template #label><i class="fab fa-twitter"></i> Twitter</template>
|
||||
<p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
|
||||
<MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectTwitter">{{ $ts.connectService }}</MkButton>
|
||||
</FormSection>
|
||||
|
||||
<div v-if="enableDiscordIntegration" class="_debobigegoItem">
|
||||
<div class="_debobigegoLabel"><i class="fab fa-discord"></i> Discord</div>
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
||||
<MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectDiscord">{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<FormSection v-if="enableDiscordIntegration">
|
||||
<template #label><i class="fab fa-discord"></i> Discord</template>
|
||||
<p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
||||
<MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectDiscord">{{ $ts.connectService }}</MkButton>
|
||||
</FormSection>
|
||||
|
||||
<div v-if="enableGithubIntegration" class="_debobigegoItem">
|
||||
<div class="_debobigegoLabel"><i class="fab fa-github"></i> GitHub</div>
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
|
||||
<MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectGithub">{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</FormBase>
|
||||
<FormSection v-if="enableGithubIntegration">
|
||||
<template #label><i class="fab fa-github"></i> GitHub</template>
|
||||
<p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
|
||||
<MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else primary @click="connectGithub">{{ $ts.connectService }}</MkButton>
|
||||
</FormSection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { apiUrl } from '@/config';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormSection,
|
||||
MkButton
|
||||
},
|
||||
|
||||
@ -79,8 +73,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
|
||||
document.cookie = `igi=${this.$i.token}; path=/;` +
|
||||
` max-age=31536000;` +
|
||||
(document.location.protocol.startsWith('https') ? ' secure' : '');
|
||||
|
@ -67,10 +67,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async addItem() {
|
||||
const menu = Object.keys(this.menuDef).filter(k => !this.$store.state.menu.includes(k));
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div class="_formRoot">
|
||||
<MkTab v-model="tab" style="margin-bottom: var(--margin);">
|
||||
<option value="mute">{{ $ts.mutedUsers }}</option>
|
||||
<option value="block">{{ $ts.blockedUsers }}</option>
|
||||
@ -8,11 +8,9 @@
|
||||
<MkPagination :pagination="mutingPagination" class="muting">
|
||||
<template #empty><FormInfo>{{ $ts.noUsers }}</FormInfo></template>
|
||||
<template v-slot="{items}">
|
||||
<FormGroup>
|
||||
<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
|
||||
<MkAcct :user="mute.mutee"/>
|
||||
</FormLink>
|
||||
</FormGroup>
|
||||
<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
|
||||
<MkAcct :user="mute.mutee"/>
|
||||
</FormLink>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</div>
|
||||
@ -20,66 +18,43 @@
|
||||
<MkPagination :pagination="blockingPagination" class="blocking">
|
||||
<template #empty><FormInfo>{{ $ts.noUsers }}</FormInfo></template>
|
||||
<template v-slot="{items}">
|
||||
<FormGroup>
|
||||
<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
|
||||
<MkAcct :user="block.blockee"/>
|
||||
</FormLink>
|
||||
</FormGroup>
|
||||
<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
|
||||
<MkAcct :user="block.blockee"/>
|
||||
</FormLink>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</FormBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import MkTab from '@/components/tab.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkPagination,
|
||||
MkTab,
|
||||
FormInfo,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
let tab = $ref('mute');
|
||||
|
||||
const mutingPagination = {
|
||||
endpoint: 'mute/list' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
const blockingPagination = {
|
||||
endpoint: 'blocking/list' as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.muteAndBlock,
|
||||
icon: 'fas fa-ban',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.muteAndBlock,
|
||||
icon: 'fas fa-ban',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
tab: 'mute',
|
||||
mutingPagination: {
|
||||
endpoint: 'mute/list',
|
||||
limit: 10,
|
||||
},
|
||||
blockingPagination: {
|
||||
endpoint: 'blocking/list',
|
||||
limit: 10,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
userPage
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -37,10 +37,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
readAllUnreadNotes() {
|
||||
os.api('i/read-all-unread-notes');
|
||||
|
@ -1,30 +1,12 @@
|
||||
<template>
|
||||
<div class="_formRoot">
|
||||
<FormLink to="/settings/update" class="_formBlock">Misskey Update</FormLink>
|
||||
|
||||
<FormSwitch :value="$i.injectFeaturedNote" @update:modelValue="onChangeInjectFeaturedNote" class="_formBlock">
|
||||
<FormSwitch :value="$i.injectFeaturedNote" class="_formBlock" @update:modelValue="onChangeInjectFeaturedNote">
|
||||
{{ $ts.showFeaturedNotesInTimeline }}
|
||||
</FormSwitch>
|
||||
|
||||
<FormSwitch v-model="reportError" class="_formBlock">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
|
||||
<FormSwitch v-model="reportError" class="_formBlock">{{ $ts.sendErrorReports }}<template #caption>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
|
||||
|
||||
<FormLink to="/settings/account-info" class="_formBlock">{{ $ts.accountInfo }}</FormLink>
|
||||
<FormLink to="/settings/experimental-features" class="_formBlock">{{ $ts.experimentalFeatures }}</FormLink>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ $ts.developer }}</template>
|
||||
<FormSwitch v-model="debug" @update:modelValue="changeDebug" class="_formBlock">
|
||||
DEBUG MODE
|
||||
</FormSwitch>
|
||||
<template v-if="debug">
|
||||
<FormButton @click="taskmanager">Task Manager</FormButton>
|
||||
</template>
|
||||
</FormSection>
|
||||
|
||||
<FormLink to="/settings/registry" class="_formBlock"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.registry }}</FormLink>
|
||||
|
||||
<FormLink to="/bios" behavior="browser" class="_formBlock"><template #icon><i class="fas fa-door-open"></i></template>BIOS</FormLink>
|
||||
<FormLink to="/cli" behavior="browser" class="_formBlock"><template #icon><i class="fas fa-door-open"></i></template>CLI</FormLink>
|
||||
|
||||
<FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink>
|
||||
</div>
|
||||
@ -33,10 +15,8 @@
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import * as os from '@/os';
|
||||
import { debug } from '@/config';
|
||||
import { defaultStore } from '@/store';
|
||||
@ -45,10 +25,8 @@ import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSelect,
|
||||
FormSection,
|
||||
FormSwitch,
|
||||
FormButton,
|
||||
FormLink,
|
||||
},
|
||||
|
||||
@ -69,10 +47,6 @@ export default defineComponent({
|
||||
reportError: defaultStore.makeGetterSetter('reportError'),
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeDebug(v) {
|
||||
console.log(v);
|
||||
@ -85,11 +59,6 @@ export default defineComponent({
|
||||
injectFeaturedNote: v
|
||||
});
|
||||
},
|
||||
|
||||
taskmanager() {
|
||||
os.popup(import('@/components/taskmanager.vue'), {
|
||||
}, {}, 'closed');
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormInfo warn>{{ $ts._plugin.installWarn }}</FormInfo>
|
||||
<div class="_formRoot">
|
||||
<FormInfo warn class="_formBlock">{{ $ts._plugin.installWarn }}</FormInfo>
|
||||
|
||||
<FormGroup>
|
||||
<FormTextarea v-model="code" tall>
|
||||
<span>{{ $ts.code }}</span>
|
||||
</FormTextarea>
|
||||
</FormGroup>
|
||||
<FormTextarea v-model="code" tall class="_formBlock">
|
||||
<template #label>{{ $ts.code }}</template>
|
||||
</FormTextarea>
|
||||
|
||||
<FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ $ts.install }}</FormButton>
|
||||
</FormBase>
|
||||
<div class="_formBlock">
|
||||
<FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ $ts.install }}</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -18,13 +18,8 @@ import { AiScript, parse } from '@syuilo/aiscript';
|
||||
import { serialize } from '@syuilo/aiscript/built/serializer';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormInfo from '@/components/debobigego/info.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormInfo from '@/components/ui/info.vue';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
@ -33,11 +28,6 @@ import * as symbols from '@/symbols';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormTextarea,
|
||||
FormSelect,
|
||||
FormRadios,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
FormButton,
|
||||
FormInfo,
|
||||
},
|
||||
@ -55,10 +45,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
installPlugin({ id, meta, ast, token }) {
|
||||
ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
|
||||
|
@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormGroup v-for="plugin in plugins" :key="plugin.id">
|
||||
<template #label><span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span></template>
|
||||
|
||||
<FormSwitch :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
|
||||
<div class="_debobigegoItem">
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<div class="_keyValue">
|
||||
<div>{{ $ts.author }}:</div>
|
||||
<div>{{ plugin.author }}</div>
|
||||
</div>
|
||||
<div class="_keyValue">
|
||||
<div>{{ $ts.description }}:</div>
|
||||
<div>{{ plugin.description }}</div>
|
||||
</div>
|
||||
<div class="_keyValue">
|
||||
<div>{{ $ts.permission }}:</div>
|
||||
<div>{{ plugin.permissions }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_debobigegoItem">
|
||||
<div class="_debobigegoPanel" style="padding: 16px;">
|
||||
<MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton>
|
||||
<MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkTextarea from '@/components/form/textarea.vue';
|
||||
import MkSelect from '@/components/form/select.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import * as symbols from '@/symbols';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
MkTextarea,
|
||||
MkSelect,
|
||||
FormSwitch,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts._plugin.manage,
|
||||
icon: 'fas fa-plug',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
plugins: ColdDeviceStorage.get('plugins'),
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
uninstall(plugin) {
|
||||
ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== plugin.id));
|
||||
os.success();
|
||||
this.$nextTick(() => {
|
||||
unisonReload();
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする
|
||||
async config(plugin) {
|
||||
const config = plugin.config;
|
||||
for (const key in plugin.configData) {
|
||||
config[key].default = plugin.configData[key];
|
||||
}
|
||||
|
||||
const { canceled, result } = await os.form(plugin.name, config);
|
||||
if (canceled) return;
|
||||
|
||||
const plugins = ColdDeviceStorage.get('plugins');
|
||||
plugins.find(p => p.id === plugin.id).configData = result;
|
||||
ColdDeviceStorage.set('plugins', plugins);
|
||||
|
||||
this.$nextTick(() => {
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
|
||||
changeActive(plugin, active) {
|
||||
const plugins = ColdDeviceStorage.get('plugins');
|
||||
plugins.find(p => p.id === plugin.id).active = active;
|
||||
ColdDeviceStorage.set('plugins', plugins);
|
||||
|
||||
this.$nextTick(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
@ -1,23 +1,54 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<div class="_formRoot">
|
||||
<FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._plugin.install }}</FormLink>
|
||||
<FormLink to="/settings/plugin/manage"><template #icon><i class="fas fa-folder-open"></i></template>{{ $ts._plugin.manage }}<template #suffix>{{ plugins }}</template></FormLink>
|
||||
</FormBase>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ $ts.manage }}</template>
|
||||
<div v-for="plugin in plugins" :key="plugin.id" class="_formBlock _panel" style="padding: 20px;">
|
||||
<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
|
||||
|
||||
<FormSwitch class="_formBlock" :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
|
||||
|
||||
<MkKeyValue class="_formBlock">
|
||||
<template #key>{{ $ts.author }}</template>
|
||||
<template #value>{{ plugin.author }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue class="_formBlock">
|
||||
<template #key>{{ $ts.description }}</template>
|
||||
<template #value>{{ plugin.description }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue class="_formBlock">
|
||||
<template #key>{{ $ts.permission }}</template>
|
||||
<template #value>{{ plugin.permission }}</template>
|
||||
</MkKeyValue>
|
||||
|
||||
<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
|
||||
<MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton>
|
||||
<MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkKeyValue from '@/components/key-value.vue';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormLink,
|
||||
FormSwitch,
|
||||
FormSection,
|
||||
MkButton,
|
||||
MkKeyValue,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -29,12 +60,47 @@ export default defineComponent({
|
||||
icon: 'fas fa-plug',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
plugins: ColdDeviceStorage.get('plugins').length,
|
||||
plugins: ColdDeviceStorage.get('plugins'),
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
methods: {
|
||||
uninstall(plugin) {
|
||||
ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== plugin.id));
|
||||
os.success();
|
||||
this.$nextTick(() => {
|
||||
unisonReload();
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする
|
||||
async config(plugin) {
|
||||
const config = plugin.config;
|
||||
for (const key in plugin.configData) {
|
||||
config[key].default = plugin.configData[key];
|
||||
}
|
||||
|
||||
const { canceled, result } = await os.form(plugin.name, config);
|
||||
if (canceled) return;
|
||||
|
||||
const plugins = ColdDeviceStorage.get('plugins');
|
||||
plugins.find(p => p.id === plugin.id).configData = result;
|
||||
ColdDeviceStorage.set('plugins', plugins);
|
||||
|
||||
this.$nextTick(() => {
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
|
||||
changeActive(plugin, active) {
|
||||
const plugins = ColdDeviceStorage.get('plugins');
|
||||
plugins.find(p => p.id === plugin.id).active = active;
|
||||
ColdDeviceStorage.set('plugins', plugins);
|
||||
|
||||
this.$nextTick(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -47,8 +47,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
@ -56,67 +56,39 @@ import FormGroup from '@/components/form/group.vue';
|
||||
import * as os from '@/os';
|
||||
import { defaultStore } from '@/store';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
import { $i } from '@/account';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormSelect,
|
||||
FormSection,
|
||||
FormGroup,
|
||||
FormSwitch,
|
||||
let isLocked = $ref($i.isLocked);
|
||||
let autoAcceptFollowed = $ref($i.autoAcceptFollowed);
|
||||
let noCrawle = $ref($i.noCrawle);
|
||||
let isExplorable = $ref($i.isExplorable);
|
||||
let hideOnlineStatus = $ref($i.hideOnlineStatus);
|
||||
let publicReactions = $ref($i.publicReactions);
|
||||
let ffVisibility = $ref($i.ffVisibility);
|
||||
|
||||
let defaultNoteVisibility = $computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
|
||||
let defaultNoteLocalOnly = $computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
|
||||
let rememberNoteVisibility = $computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
|
||||
let keepCw = $computed(defaultStore.makeGetterSetter('keepCw'));
|
||||
|
||||
function save() {
|
||||
os.api('i/update', {
|
||||
isLocked: !!isLocked,
|
||||
autoAcceptFollowed: !!autoAcceptFollowed,
|
||||
noCrawle: !!noCrawle,
|
||||
isExplorable: !!isExplorable,
|
||||
hideOnlineStatus: !!hideOnlineStatus,
|
||||
publicReactions: !!publicReactions,
|
||||
ffVisibility: ffVisibility,
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.privacy,
|
||||
icon: 'fas fa-lock-open',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.privacy,
|
||||
icon: 'fas fa-lock-open',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
isLocked: false,
|
||||
autoAcceptFollowed: false,
|
||||
noCrawle: false,
|
||||
isExplorable: false,
|
||||
hideOnlineStatus: false,
|
||||
publicReactions: false,
|
||||
ffVisibility: 'public',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
defaultNoteVisibility: defaultStore.makeGetterSetter('defaultNoteVisibility'),
|
||||
defaultNoteLocalOnly: defaultStore.makeGetterSetter('defaultNoteLocalOnly'),
|
||||
rememberNoteVisibility: defaultStore.makeGetterSetter('rememberNoteVisibility'),
|
||||
keepCw: defaultStore.makeGetterSetter('keepCw'),
|
||||
},
|
||||
|
||||
created() {
|
||||
this.isLocked = this.$i.isLocked;
|
||||
this.autoAcceptFollowed = this.$i.autoAcceptFollowed;
|
||||
this.noCrawle = this.$i.noCrawle;
|
||||
this.isExplorable = this.$i.isExplorable;
|
||||
this.hideOnlineStatus = this.$i.hideOnlineStatus;
|
||||
this.publicReactions = this.$i.publicReactions;
|
||||
this.ffVisibility = this.$i.ffVisibility;
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
save() {
|
||||
os.api('i/update', {
|
||||
isLocked: !!this.isLocked,
|
||||
autoAcceptFollowed: !!this.autoAcceptFollowed,
|
||||
noCrawle: !!this.noCrawle,
|
||||
isExplorable: !!this.isExplorable,
|
||||
hideOnlineStatus: !!this.hideOnlineStatus,
|
||||
publicReactions: !!this.publicReactions,
|
||||
ffVisibility: this.ffVisibility,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -3,50 +3,50 @@
|
||||
<div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
|
||||
<div class="avatar _acrylic">
|
||||
<MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/>
|
||||
<MkButton primary class="avatarEdit" @click="changeAvatar">{{ $ts._profile.changeAvatar }}</MkButton>
|
||||
<MkButton primary class="avatarEdit" @click="changeAvatar">{{ i18n.locale._profile.changeAvatar }}</MkButton>
|
||||
</div>
|
||||
<MkButton primary class="bannerEdit" @click="changeBanner">{{ $ts._profile.changeBanner }}</MkButton>
|
||||
<MkButton primary class="bannerEdit" @click="changeBanner">{{ i18n.locale._profile.changeBanner }}</MkButton>
|
||||
</div>
|
||||
|
||||
<FormInput v-model="name" :max="30" manual-save class="_formBlock">
|
||||
<template #label>{{ $ts._profile.name }}</template>
|
||||
<FormInput v-model="profile.name" :max="30" manual-save class="_formBlock">
|
||||
<template #label>{{ i18n.locale._profile.name }}</template>
|
||||
</FormInput>
|
||||
|
||||
<FormTextarea v-model="description" :max="500" tall manual-save class="_formBlock">
|
||||
<template #label>{{ $ts._profile.description }}</template>
|
||||
<template #caption>{{ $ts._profile.youCanIncludeHashtags }}</template>
|
||||
<FormTextarea v-model="profile.description" :max="500" tall manual-save class="_formBlock">
|
||||
<template #label>{{ i18n.locale._profile.description }}</template>
|
||||
<template #caption>{{ i18n.locale._profile.youCanIncludeHashtags }}</template>
|
||||
</FormTextarea>
|
||||
|
||||
<FormInput v-model="location" manual-save class="_formBlock">
|
||||
<template #label>{{ $ts.location }}</template>
|
||||
<FormInput v-model="profile.location" manual-save class="_formBlock">
|
||||
<template #label>{{ i18n.locale.location }}</template>
|
||||
<template #prefix><i class="fas fa-map-marker-alt"></i></template>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model="birthday" type="date" manual-save class="_formBlock">
|
||||
<template #label>{{ $ts.birthday }}</template>
|
||||
<FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock">
|
||||
<template #label>{{ i18n.locale.birthday }}</template>
|
||||
<template #prefix><i class="fas fa-birthday-cake"></i></template>
|
||||
</FormInput>
|
||||
|
||||
<FormSelect v-model="lang" class="_formBlock">
|
||||
<template #label>{{ $ts.language }}</template>
|
||||
<FormSelect v-model="profile.lang" class="_formBlock">
|
||||
<template #label>{{ i18n.locale.language }}</template>
|
||||
<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
|
||||
</FormSelect>
|
||||
|
||||
<FormSlot>
|
||||
<MkButton @click="editMetadata">{{ $ts._profile.metadataEdit }}</MkButton>
|
||||
<template #caption>{{ $ts._profile.metadataDescription }}</template>
|
||||
<MkButton @click="editMetadata">{{ i18n.locale._profile.metadataEdit }}</MkButton>
|
||||
<template #caption>{{ i18n.locale._profile.metadataDescription }}</template>
|
||||
</FormSlot>
|
||||
|
||||
<FormSwitch v-model="isCat" class="_formBlock">{{ $ts.flagAsCat }}<template #caption>{{ $ts.flagAsCatDescription }}</template></FormSwitch>
|
||||
<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.locale.flagAsCat }}<template #caption>{{ i18n.locale.flagAsCatDescription }}</template></FormSwitch>
|
||||
|
||||
<FormSwitch v-model="isBot" class="_formBlock">{{ $ts.flagAsBot }}<template #caption>{{ $ts.flagAsBotDescription }}</template></FormSwitch>
|
||||
<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.locale.flagAsBot }}<template #caption>{{ i18n.locale.flagAsBotDescription }}</template></FormSwitch>
|
||||
|
||||
<FormSwitch v-model="alwaysMarkNsfw" class="_formBlock">{{ $ts.alwaysMarkSensitive }}</FormSwitch>
|
||||
<FormSwitch v-model="profile.alwaysMarkNsfw" class="_formBlock">{{ i18n.locale.alwaysMarkSensitive }}</FormSwitch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, reactive, watch } from 'vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
@ -57,198 +57,149 @@ import { host, langs } from '@/config';
|
||||
import { selectFile } from '@/scripts/select-file';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
import { i18n } from '@/i18n';
|
||||
import { $i } from '@/account';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
FormInput,
|
||||
FormTextarea,
|
||||
FormSwitch,
|
||||
FormSelect,
|
||||
FormSlot,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
const profile = reactive({
|
||||
name: $i.name,
|
||||
description: $i.description,
|
||||
location: $i.location,
|
||||
birthday: $i.birthday,
|
||||
lang: $i.lang,
|
||||
isBot: $i.isBot,
|
||||
isCat: $i.isCat,
|
||||
alwaysMarkNsfw: $i.alwaysMarkNsfw,
|
||||
});
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.profile,
|
||||
icon: 'fas fa-user',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
host,
|
||||
langs,
|
||||
name: null,
|
||||
description: null,
|
||||
birthday: null,
|
||||
lang: null,
|
||||
location: null,
|
||||
fieldName0: null,
|
||||
fieldValue0: null,
|
||||
fieldName1: null,
|
||||
fieldValue1: null,
|
||||
fieldName2: null,
|
||||
fieldValue2: null,
|
||||
fieldName3: null,
|
||||
fieldValue3: null,
|
||||
avatarId: null,
|
||||
bannerId: null,
|
||||
isBot: false,
|
||||
isCat: false,
|
||||
alwaysMarkNsfw: false,
|
||||
saving: false,
|
||||
}
|
||||
},
|
||||
const additionalFields = reactive({
|
||||
fieldName0: $i.fields[0] ? $i.fields[0].name : null,
|
||||
fieldValue0: $i.fields[0] ? $i.fields[0].value : null,
|
||||
fieldName1: $i.fields[1] ? $i.fields[1].name : null,
|
||||
fieldValue1: $i.fields[1] ? $i.fields[1].value : null,
|
||||
fieldName2: $i.fields[2] ? $i.fields[2].name : null,
|
||||
fieldValue2: $i.fields[2] ? $i.fields[2].value : null,
|
||||
fieldName3: $i.fields[3] ? $i.fields[3].name : null,
|
||||
fieldValue3: $i.fields[3] ? $i.fields[3].value : null,
|
||||
});
|
||||
|
||||
created() {
|
||||
this.name = this.$i.name;
|
||||
this.description = this.$i.description;
|
||||
this.location = this.$i.location;
|
||||
this.birthday = this.$i.birthday;
|
||||
this.lang = this.$i.lang;
|
||||
this.avatarId = this.$i.avatarId;
|
||||
this.bannerId = this.$i.bannerId;
|
||||
this.isBot = this.$i.isBot;
|
||||
this.isCat = this.$i.isCat;
|
||||
this.alwaysMarkNsfw = this.$i.alwaysMarkNsfw;
|
||||
watch(() => profile, () => {
|
||||
save();
|
||||
}, {
|
||||
deep: true,
|
||||
});
|
||||
|
||||
this.fieldName0 = this.$i.fields[0] ? this.$i.fields[0].name : null;
|
||||
this.fieldValue0 = this.$i.fields[0] ? this.$i.fields[0].value : null;
|
||||
this.fieldName1 = this.$i.fields[1] ? this.$i.fields[1].name : null;
|
||||
this.fieldValue1 = this.$i.fields[1] ? this.$i.fields[1].value : null;
|
||||
this.fieldName2 = this.$i.fields[2] ? this.$i.fields[2].name : null;
|
||||
this.fieldValue2 = this.$i.fields[2] ? this.$i.fields[2].value : null;
|
||||
this.fieldName3 = this.$i.fields[3] ? this.$i.fields[3].name : null;
|
||||
this.fieldValue3 = this.$i.fields[3] ? this.$i.fields[3].value : null;
|
||||
function save() {
|
||||
os.apiWithDialog('i/update', {
|
||||
name: profile.name || null,
|
||||
description: profile.description || null,
|
||||
location: profile.location || null,
|
||||
birthday: profile.birthday || null,
|
||||
lang: profile.lang || null,
|
||||
isBot: !!profile.isBot,
|
||||
isCat: !!profile.isCat,
|
||||
alwaysMarkNsfw: !!profile.alwaysMarkNsfw,
|
||||
});
|
||||
}
|
||||
|
||||
this.$watch('name', this.save);
|
||||
this.$watch('description', this.save);
|
||||
this.$watch('location', this.save);
|
||||
this.$watch('birthday', this.save);
|
||||
this.$watch('lang', this.save);
|
||||
this.$watch('isBot', this.save);
|
||||
this.$watch('isCat', this.save);
|
||||
this.$watch('alwaysMarkNsfw', this.save);
|
||||
},
|
||||
function changeAvatar(ev) {
|
||||
selectFile(ev.currentTarget || ev.target, i18n.locale.avatar).then(async (file) => {
|
||||
const i = await os.apiWithDialog('i/update', {
|
||||
avatarId: file.id,
|
||||
});
|
||||
$i.avatarId = i.avatarId;
|
||||
$i.avatarUrl = i.avatarUrl;
|
||||
});
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
function changeBanner(ev) {
|
||||
selectFile(ev.currentTarget || ev.target, i18n.locale.banner).then(async (file) => {
|
||||
const i = await os.apiWithDialog('i/update', {
|
||||
bannerId: file.id,
|
||||
});
|
||||
$i.bannerId = i.bannerId;
|
||||
$i.bannerUrl = i.bannerUrl;
|
||||
});
|
||||
}
|
||||
|
||||
methods: {
|
||||
changeAvatar(e) {
|
||||
selectFile(e.currentTarget || e.target, this.$ts.avatar).then(file => {
|
||||
os.api('i/update', {
|
||||
avatarId: file.id,
|
||||
});
|
||||
});
|
||||
async function editMetadata() {
|
||||
const { canceled, result } = await os.form(i18n.locale._profile.metadata, {
|
||||
fieldName0: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataLabel + ' 1',
|
||||
default: additionalFields.fieldName0,
|
||||
},
|
||||
|
||||
changeBanner(e) {
|
||||
selectFile(e.currentTarget || e.target, this.$ts.banner).then(file => {
|
||||
os.api('i/update', {
|
||||
bannerId: file.id,
|
||||
});
|
||||
});
|
||||
fieldValue0: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataContent + ' 1',
|
||||
default: additionalFields.fieldValue0,
|
||||
},
|
||||
|
||||
async editMetadata() {
|
||||
const { canceled, result } = await os.form(this.$ts._profile.metadata, {
|
||||
fieldName0: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataLabel + ' 1',
|
||||
default: this.fieldName0,
|
||||
},
|
||||
fieldValue0: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataContent + ' 1',
|
||||
default: this.fieldValue0,
|
||||
},
|
||||
fieldName1: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataLabel + ' 2',
|
||||
default: this.fieldName1,
|
||||
},
|
||||
fieldValue1: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataContent + ' 2',
|
||||
default: this.fieldValue1,
|
||||
},
|
||||
fieldName2: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataLabel + ' 3',
|
||||
default: this.fieldName2,
|
||||
},
|
||||
fieldValue2: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataContent + ' 3',
|
||||
default: this.fieldValue2,
|
||||
},
|
||||
fieldName3: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataLabel + ' 4',
|
||||
default: this.fieldName3,
|
||||
},
|
||||
fieldValue3: {
|
||||
type: 'string',
|
||||
label: this.$ts._profile.metadataContent + ' 4',
|
||||
default: this.fieldValue3,
|
||||
},
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
this.fieldName0 = result.fieldName0;
|
||||
this.fieldValue0 = result.fieldValue0;
|
||||
this.fieldName1 = result.fieldName1;
|
||||
this.fieldValue1 = result.fieldValue1;
|
||||
this.fieldName2 = result.fieldName2;
|
||||
this.fieldValue2 = result.fieldValue2;
|
||||
this.fieldName3 = result.fieldName3;
|
||||
this.fieldValue3 = result.fieldValue3;
|
||||
|
||||
const fields = [
|
||||
{ name: this.fieldName0, value: this.fieldValue0 },
|
||||
{ name: this.fieldName1, value: this.fieldValue1 },
|
||||
{ name: this.fieldName2, value: this.fieldValue2 },
|
||||
{ name: this.fieldName3, value: this.fieldValue3 },
|
||||
];
|
||||
|
||||
os.api('i/update', {
|
||||
fields,
|
||||
}).then(i => {
|
||||
os.success();
|
||||
}).catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.id
|
||||
});
|
||||
});
|
||||
fieldName1: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataLabel + ' 2',
|
||||
default: additionalFields.fieldName1,
|
||||
},
|
||||
|
||||
save() {
|
||||
this.saving = true;
|
||||
|
||||
os.apiWithDialog('i/update', {
|
||||
name: this.name || null,
|
||||
description: this.description || null,
|
||||
location: this.location || null,
|
||||
birthday: this.birthday || null,
|
||||
lang: this.lang || null,
|
||||
isBot: !!this.isBot,
|
||||
isCat: !!this.isCat,
|
||||
alwaysMarkNsfw: !!this.alwaysMarkNsfw,
|
||||
}).then(i => {
|
||||
this.saving = false;
|
||||
this.$i.avatarId = i.avatarId;
|
||||
this.$i.avatarUrl = i.avatarUrl;
|
||||
this.$i.bannerId = i.bannerId;
|
||||
this.$i.bannerUrl = i.bannerUrl;
|
||||
}).catch(err => {
|
||||
this.saving = false;
|
||||
});
|
||||
fieldValue1: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataContent + ' 2',
|
||||
default: additionalFields.fieldValue1,
|
||||
},
|
||||
}
|
||||
fieldName2: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataLabel + ' 3',
|
||||
default: additionalFields.fieldName2,
|
||||
},
|
||||
fieldValue2: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataContent + ' 3',
|
||||
default: additionalFields.fieldValue2,
|
||||
},
|
||||
fieldName3: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataLabel + ' 4',
|
||||
default: additionalFields.fieldName3,
|
||||
},
|
||||
fieldValue3: {
|
||||
type: 'string',
|
||||
label: i18n.locale._profile.metadataContent + ' 4',
|
||||
default: additionalFields.fieldValue3,
|
||||
},
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
additionalFields.fieldName0 = result.fieldName0;
|
||||
additionalFields.fieldValue0 = result.fieldValue0;
|
||||
additionalFields.fieldName1 = result.fieldName1;
|
||||
additionalFields.fieldValue1 = result.fieldValue1;
|
||||
additionalFields.fieldName2 = result.fieldName2;
|
||||
additionalFields.fieldValue2 = result.fieldValue2;
|
||||
additionalFields.fieldName3 = result.fieldName3;
|
||||
additionalFields.fieldValue3 = result.fieldValue3;
|
||||
|
||||
const fields = [
|
||||
{ name: additionalFields.fieldName0, value: additionalFields.fieldValue0 },
|
||||
{ name: additionalFields.fieldName1, value: additionalFields.fieldValue1 },
|
||||
{ name: additionalFields.fieldName2, value: additionalFields.fieldValue2 },
|
||||
{ name: additionalFields.fieldName3, value: additionalFields.fieldValue3 },
|
||||
];
|
||||
|
||||
os.api('i/update', {
|
||||
fields,
|
||||
}).then(i => {
|
||||
os.success();
|
||||
}).catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: i18n.locale.profile,
|
||||
icon: 'fas fa-user',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -100,10 +100,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
},
|
||||
|
||||
methods: {
|
||||
save() {
|
||||
this.$store.set('reactions', this.reactions);
|
||||
|
@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormGroup>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts._registry.domain }}</template>
|
||||
<template #value>{{ $ts.system }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts._registry.scope }}</template>
|
||||
<template #value>{{ scope.join('/') }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup v-if="keys">
|
||||
<template #label>{{ $ts._registry.keys }}</template>
|
||||
<FormLink v-for="key in keys" :to="`/settings/registry/value/system/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink>
|
||||
</FormGroup>
|
||||
|
||||
<FormButton primary @click="createKey">{{ $ts._registry.createKey }}</FormButton>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import * as JSON5 from 'json5';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormLink from '@/components/debobigego/link.vue';
|
||||
import FormBase from '@/components/debobigego/base.vue';
|
||||
import FormGroup from '@/components/debobigego/group.vue';
|
||||
import FormButton from '@/components/debobigego/button.vue';
|
||||
import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
|
||||
import * as os from '@/os';
|
||||
import * as symbols from '@/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormBase,
|
||||
FormSelect,
|
||||
FormSwitch,
|
||||
FormButton,
|
||||
FormLink,
|
||||
FormGroup,
|
||||
FormKeyValueView,
|
||||
},
|
||||
|
||||
props: {
|
||||
scope: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.registry,
|
||||
icon: 'fas fa-cogs',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
keys: null,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
scope() {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
os.api('i/registry/keys-with-type', {
|
||||
scope: this.scope
|
||||
}).then(keys => {
|
||||
this.keys = Object.entries(keys).sort((a, b) => a[0].localeCompare(b[0]));
|
||||
});
|
||||
},
|
||||
|
||||
async createKey() {
|
||||
const { canceled, result } = await os.form(this.$ts._registry.createKey, {
|
||||
key: {
|
||||
type: 'string',
|
||||
label: this.$ts._registry.key,
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
multiline: true,
|
||||
label: this.$ts.value,
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
label: this.$ts._registry.scope,
|
||||
default: this.scope.join('/')
|
||||
}
|
||||
});
|
||||
if (canceled) return;
|
||||
os.apiWithDialog('i/registry/set', {
|
||||
scope: result.scope.split('/'),
|
||||
key: result.key,
|
||||
value: JSON5.parse(result.value),
|
||||
}).then(() => {
|
||||
this.fetch();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user