Merge branch 'notification-read-api' into swn

This commit is contained in:
tamaina 2022-03-04 15:54:03 +09:00
commit 30915d587a
912 changed files with 6686 additions and 4904 deletions

View file

@ -57,7 +57,7 @@ const lib = emojilist.filter(x => x.category !== 'flags');
const char2file = (char: string) => {
let codes = Array.from(char).map(x => x.codePointAt(0)?.toString(16));
if (!codes.includes('200d')) codes = codes.filter(x => x != 'fe0f');
if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f');
return codes.filter(x => x && x.length).join('-');
};
@ -192,8 +192,7 @@ function exec() {
const cache = sessionStorage.getItem(cacheKey);
if (cache) {
const users = JSON.parse(cache);
users.value = users;
users.value = JSON.parse(cache);
fetching.value = false;
} else {
os.api('users/search-by-username-and-host', {
@ -208,7 +207,7 @@ function exec() {
});
}
} else if (props.type === 'hashtag') {
if (!props.q || props.q == '') {
if (!props.q || props.q === '') {
hashtags.value = JSON.parse(localStorage.getItem('hashtags') || '[]');
fetching.value = false;
} else {
@ -231,9 +230,9 @@ function exec() {
}
}
} else if (props.type === 'emoji') {
if (!props.q || props.q == '') {
if (!props.q || props.q === '') {
// 使
emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x) as EmojiDef[];
emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji === emoji)).filter(x => x) as EmojiDef[];
return;
}
@ -241,37 +240,37 @@ function exec() {
const max = 30;
emojiDb.some(x => {
if (x.name.startsWith(props.q || '') && !x.aliasOf && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == max;
if (x.name.startsWith(props.q ?? '') && !x.aliasOf && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
return matched.length === max;
});
if (matched.length < max) {
emojiDb.some(x => {
if (x.name.startsWith(props.q || '') && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == max;
if (x.name.startsWith(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
return matched.length === max;
});
}
if (matched.length < max) {
emojiDb.some(x => {
if (x.name.includes(props.q || '') && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == max;
if (x.name.includes(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
return matched.length === max;
});
}
emojis.value = matched;
} else if (props.type === 'mfmTag') {
if (!props.q || props.q == '') {
if (!props.q || props.q === '') {
mfmTags.value = MFM_TAGS;
return;
}
mfmTags.value = MFM_TAGS.filter(tag => tag.startsWith(props.q || ''));
mfmTags.value = MFM_TAGS.filter(tag => tag.startsWith(props.q ?? ''));
}
}
function onMousedown(e: Event) {
if (!contains(rootEl.value, e.target) && (rootEl.value != e.target)) props.close();
if (!contains(rootEl.value, e.target) && (rootEl.value !== e.target)) props.close();
}
function onKeydown(e: KeyboardEvent) {
@ -348,7 +347,7 @@ function chooseUser() {
onUpdated(() => {
setPosition();
items.value = suggests.value?.children || [];
items.value = suggests.value?.children ?? [];
});
onMounted(() => {

View file

@ -93,7 +93,7 @@ function requestRender() {
}
function callback(response?: string) {
emit('update:modelValue', typeof response == 'string' ? response : null);
emit('update:modelValue', typeof response === 'string' ? response : null);
}
onMounted(() => {

View file

@ -70,6 +70,7 @@ const colors = {
red: '#FF4560',
purple: '#e300db',
orange: '#fe6919',
lime: '#c7f400',
};
const colorSets = [colors.blue, colors.green, colors.yellow, colors.red, colors.purple];
const getColor = (i) => {
@ -224,7 +225,7 @@ export default defineComponent({
axis: 'y',
colors: {
0: alpha(x.color ? x.color : getColor(i), 0),
[maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.15),
[maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.175),
},
},
},
@ -373,16 +374,6 @@ export default defineComponent({
const raw = await os.api('charts/federation', { limit: props.limit, span: props.span });
return {
series: [{
name: 'Sub',
type: 'area',
data: format(raw.sub),
color: colors.orange,
}, {
name: 'Pub',
type: 'area',
data: format(raw.pub),
color: colors.purple,
}, {
name: 'Received',
type: 'area',
data: format(raw.inboxInstances),
@ -397,6 +388,21 @@ export default defineComponent({
type: 'area',
data: format(raw.stalled),
color: colors.red,
}, {
name: 'Pub & Sub',
type: 'area',
data: format(raw.pubsub),
color: colors.lime,
}, {
name: 'Pub',
type: 'area',
data: format(raw.pub),
color: colors.purple,
}, {
name: 'Sub',
type: 'area',
data: format(raw.sub),
color: colors.orange,
}],
};
};
@ -529,12 +535,12 @@ export default defineComponent({
name: 'Write',
type: 'area',
data: format(raw.write),
color: colors.blue,
color: colors.lime,
}, {
name: 'Read',
type: 'area',
data: format(raw.read),
color: '#888888',
color: colors.blue,
}, {
name: '< Week',
type: 'area',

View file

@ -53,8 +53,8 @@ export default defineComponent({
if (el.key == null && item.id) el.key = item.id;
if (
i != props.items.length - 1 &&
new Date(item.createdAt).getDate() != new Date(props.items[i + 1].createdAt).getDate()
i !== props.items.length - 1 &&
new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate()
) {
const separator = h('div', {
class: 'separator',

View file

@ -1,7 +1,7 @@
<template>
<div class="mk-google">
<input v-model="query" type="search" :placeholder="q">
<button @click="search"><i class="fas fa-search"></i> {{ $ts.search }}</button>
<button @click="search"><i class="fas fa-search"></i> {{ $ts.searchByGoogle }}</button>
</div>
</template>

View file

@ -1,6 +1,6 @@
<template>
<MkModal ref="modal" :prefer-type="'dialog'" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="szkkfdyq _popup">
<MkModal ref="modal" v-slot="{ type, maxHeight }" :prefer-type="preferedModalType" :transparent-bg="true" :src="src" @click="modal.close()" @closed="emit('closed')">
<div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }">
<div class="main">
<template v-for="item in items">
<button v-if="item.action" v-click-anime class="_button" @click="$event => { item.action($event); close(); }">
@ -33,97 +33,94 @@
</MkModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
<script lang="ts" setup>
import { } from 'vue';
import MkModal from '@/components/ui/modal.vue';
import { menuDef } from '@/menu';
import { instanceName } from '@/config';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { deviceKind } from '@/scripts/device-kind';
export default defineComponent({
components: {
MkModal,
},
emits: ['closed'],
data() {
return {
menuDef: menuDef,
items: [],
instanceName,
};
},
computed: {
menu(): string[] {
return this.$store.state.menu;
},
},
created() {
this.items = Object.keys(this.menuDef).filter(k => !this.menu.includes(k)).map(k => this.menuDef[k]).filter(def => def.show == null ? true : def.show).map(def => ({
type: def.to ? 'link' : 'button',
text: this.$ts[def.title],
icon: def.icon,
to: def.to,
action: def.action,
indicate: def.indicated,
}));
},
methods: {
close() {
this.$refs.modal.close();
}
}
const props = withDefaults(defineProps<{
src?: HTMLElement;
}>(), {
});
const emit = defineEmits<{
(ev: 'closed'): void;
}>();
const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'popup' :
deviceKind === 'smartphone' ? 'drawer' :
'dialog';
const modal = $ref<InstanceType<typeof MkModal>>();
const menu = defaultStore.state.menu;
const items = Object.keys(menuDef).filter(k => !menu.includes(k)).map(k => menuDef[k]).filter(def => def.show == null ? true : def.show).map(def => ({
type: def.to ? 'link' : 'button',
text: i18n.ts[def.title],
icon: def.icon,
to: def.to,
action: def.action,
indicate: def.indicated,
}));
function close() {
modal.close();
}
</script>
<style lang="scss" scoped>
.szkkfdyq {
width: 100%;
max-height: 100%;
max-width: 800px;
padding: 32px;
width: min(460px, 100vw);
padding: 24px;
box-sizing: border-box;
overflow: auto;
text-align: center;
overscroll-behavior: contain;
text-align: left;
border-radius: 16px;
@media (max-width: 500px) {
padding: 16px;
&.asDrawer {
width: 100%;
padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 16px) 16px;
border-radius: 24px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
text-align: center;
}
> .main, > .sub {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
> * {
position: relative;
display: inline-flex;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
vertical-align: bottom;
width: 128px;
height: 128px;
border-radius: var(--radius);
@media (max-width: 500px) {
width: 100px;
height: 100px;
}
height: 100px;
border-radius: 10px;
&:hover {
background: rgba(0, 0, 0, 0.05);
color: var(--accent);
background: var(--accentedBg);
text-decoration: none;
}
> .icon {
font-size: 26px;
height: 32px;
font-size: 24px;
height: 24px;
}
> .text {
margin-top: 8px;
font-size: 0.9em;
margin-top: 12px;
font-size: 0.8em;
line-height: 1.5em;
}

View file

@ -45,7 +45,7 @@ export default defineComponent({
},
render() {
if (this.text == null || this.text == '') return;
if (this.text == null || this.text === '') return;
const ast = (this.plain ? mfm.parsePlain : mfm.parse)(this.text, { fnNameList: MFM_TAGS });

View file

@ -6,33 +6,26 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
<script lang="ts" setup>
import { onMounted } from 'vue';
import XNotification from './notification.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XNotification
},
props: {
notification: {
type: Object,
required: true
}
},
emits: ['closed'],
data() {
return {
showing: true,
zIndex: os.claimZIndex('high'),
};
},
mounted() {
window.setTimeout(() => {
this.showing = false;
}, 6000);
}
defineProps<{
notification: any; // TODO
}>();
const emit = defineEmits<{
(ev: 'closed'): void;
}>();
const zIndex = os.claimZIndex('high');
let showing = $ref(true);
onMounted(() => {
window.setTimeout(() => {
showing = false;
}, 6000);
});
</script>

View file

@ -22,12 +22,12 @@ const emit = defineEmits<{
(e: 'closed'): void;
}>();
const showing = ref(true);
const zIndex = os.claimZIndex('high');
let showing = $ref(true);
onMounted(() => {
window.setTimeout(() => {
showing.value = false;
showing = false;
}, 4000);
});
</script>

View file

@ -88,7 +88,7 @@ const onBgClick = () => {
};
if (type.value === 'drawer') {
maxHeight.value = window.innerHeight / 2;
maxHeight.value = window.innerHeight / 1.5;
}
const keymap = {
@ -100,6 +100,7 @@ const MARGIN = 16;
const align = () => {
if (props.src == null) return;
if (type.value === 'drawer') return;
if (type.value === 'dialog') return;
const popover = content.value!;
if (popover == null) return;

View file

@ -67,7 +67,7 @@ let tweetHeight = $ref(150);
const requestUrl = new URL(props.url);
if (requestUrl.hostname == 'twitter.com') {
if (requestUrl.hostname === 'twitter.com') {
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
if (m) tweetId = m[1];
}

View file

@ -15,6 +15,7 @@ if (localStorage.getItem('accounts') != null) {
import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue';
import compareVersions from 'compare-versions';
import * as JSON5 from 'json5';
import widgets from '@/widgets';
import directives from '@/directives';
@ -158,7 +159,9 @@ if ($i && $i.token) {
}
//#endregion
fetchInstance().then(() => {
const fetchInstanceMetaPromise = fetchInstance();
fetchInstanceMetaPromise.then(() => {
localStorage.setItem('v', instance.version);
// Init service worker
@ -266,6 +269,14 @@ window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
});
//#endregion
fetchInstanceMetaPromise.then(() => {
if (defaultStore.state.themeInitial) {
if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON5.parse(instance.defaultLightTheme));
if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON5.parse(instance.defaultDarkTheme));
defaultStore.set('themeInitial', false);
}
});
// shortcut
document.addEventListener('keydown', makeHotkey({
'd': () => {

View file

@ -544,7 +544,7 @@ export const uploads = ref<{
}[]>([]);
export function upload(file: File, folder?: any, name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading): Promise<Misskey.entities.DriveFile> {
if (folder && typeof folder == 'object') folder = folder.id;
if (folder && typeof folder === 'object') folder = folder.id;
return new Promise((resolve, reject) => {
const id = Math.random().toString();

View file

@ -149,6 +149,7 @@ const patrons = [
'oss',
'Weeble',
'蝉暮せせせ',
'ThatOneCalculator',
];
let easterEggReady = false;

View file

@ -31,6 +31,16 @@
<template #caption>#RRGGBB</template>
</FormInput>
<FormTextarea v-model="defaultLightTheme" class="_formBlock">
<template #label>{{ $ts.instanceDefaultLightTheme }}</template>
<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormTextarea v-model="defaultDarkTheme" class="_formBlock">
<template #label>{{ $ts.instanceDefaultDarkTheme }}</template>
<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormInput v-model="tosUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
<template #label>{{ $ts.tosUrl }}</template>
@ -76,11 +86,6 @@
<template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
</FormSwitch>
<FormSwitch v-model="proxyRemoteFiles" class="_formBlock">
<template #label>{{ $ts.proxyRemoteFiles }}</template>
<template #caption>{{ $ts.proxyRemoteFilesDescription }}</template>
</FormSwitch>
<FormSplit :min-width="280">
<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
<template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
@ -181,11 +186,12 @@ export default defineComponent({
bannerUrl: null,
backgroundImageUrl: null,
themeColor: null,
defaultLightTheme: null,
defaultDarkTheme: null,
enableLocalTimeline: false,
enableGlobalTimeline: false,
pinnedUsers: '',
cacheRemoteFiles: false,
proxyRemoteFiles: false,
localDriveCapacityMb: 0,
remoteDriveCapacityMb: 0,
enableRegistration: false,
@ -208,13 +214,14 @@ export default defineComponent({
this.bannerUrl = meta.bannerUrl;
this.backgroundImageUrl = meta.backgroundImageUrl;
this.themeColor = meta.themeColor;
this.defaultLightTheme = meta.defaultLightTheme;
this.defaultDarkTheme = meta.defaultDarkTheme;
this.maintainerName = meta.maintainerName;
this.maintainerEmail = meta.maintainerEmail;
this.enableLocalTimeline = !meta.disableLocalTimeline;
this.enableGlobalTimeline = !meta.disableGlobalTimeline;
this.pinnedUsers = meta.pinnedUsers.join('\n');
this.cacheRemoteFiles = meta.cacheRemoteFiles;
this.proxyRemoteFiles = meta.proxyRemoteFiles;
this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
this.enableRegistration = !meta.disableRegistration;
@ -235,13 +242,14 @@ export default defineComponent({
bannerUrl: this.bannerUrl,
backgroundImageUrl: this.backgroundImageUrl,
themeColor: this.themeColor === '' ? null : this.themeColor,
defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme,
defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme,
maintainerName: this.maintainerName,
maintainerEmail: this.maintainerEmail,
disableLocalTimeline: !this.enableLocalTimeline,
disableGlobalTimeline: !this.enableGlobalTimeline,
pinnedUsers: this.pinnedUsers.split('\n'),
cacheRemoteFiles: this.cacheRemoteFiles,
proxyRemoteFiles: this.proxyRemoteFiles,
localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
disableRegistration: !this.enableRegistration,

View file

@ -121,6 +121,7 @@
</div>
</div>
</div>
<!-- deprecated
<div class="section _block">
<div class="title">{{ $ts._mfm.search }}</div>
<div class="content">
@ -131,6 +132,7 @@
</div>
</div>
</div>
-->
<div class="section _block">
<div class="title">{{ $ts._mfm.flip }}</div>
<div class="content">

View file

@ -10,7 +10,7 @@
<div class="main _gap">
<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton>
<div class="note _gap">
<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/>
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri" class="_isolated"/>
<XNoteDetailed :key="note.id" v-model:note="note" class="_isolated note"/>
</div>
<div v-if="clips && clips.length > 0" class="_content clips _gap">

View file

@ -29,11 +29,27 @@
<FormSelect v-model="profile.lang" class="_formBlock">
<template #label>{{ i18n.ts.language }}</template>
<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
<option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option>
</FormSelect>
<FormSlot>
<MkButton @click="editMetadata">{{ i18n.ts._profile.metadataEdit }}</MkButton>
<FormSlot class="_formBlock">
<FormFolder>
<template #icon><i class="fas fa-table-list"></i></template>
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
<div class="_formRoot">
<FormSplit v-for="(record, i) in fields" :min-width="250" class="_formBlock">
<FormInput v-model="record.name">
<template #label>{{ i18n.ts._profile.metadataLabel }} #{{ i + 1 }}</template>
</FormInput>
<FormInput v-model="record.value">
<template #label>{{ i18n.ts._profile.metadataContent }} #{{ i + 1 }}</template>
</FormInput>
</FormSplit>
<MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton inline primary @click="saveFields"><i class="fas fa-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</FormFolder>
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
</FormSlot>
@ -52,13 +68,16 @@ import FormInput from '@/components/form/input.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormSplit from '@/components/form/split.vue';
import FormFolder from '@/components/form/folder.vue';
import FormSlot from '@/components/form/slot.vue';
import { host, langs } from '@/config';
import { host } 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';
import { langmap } from '@/scripts/langmap';
const profile = reactive({
name: $i.name,
@ -72,23 +91,31 @@ const profile = reactive({
alwaysMarkNsfw: $i.alwaysMarkNsfw,
});
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,
});
watch(() => profile, () => {
save();
}, {
deep: true,
});
const fields = reactive($i.fields.map(field => ({ name: field.name, value: field.value })));
function addField() {
fields.push({
name: '',
value: '',
});
}
while (fields.length < 4) {
addField();
}
function saveFields() {
os.apiWithDialog('i/update', {
fields: fields.filter(field => field.name !== '' && field.value !== ''),
});
}
function save() {
os.apiWithDialog('i/update', {
name: profile.name || null,
@ -123,79 +150,6 @@ function changeBanner(ev) {
});
}
async function editMetadata() {
const { canceled, result } = await os.form(i18n.ts._profile.metadata, {
fieldName0: {
type: 'string',
label: i18n.ts._profile.metadataLabel + ' 1',
default: additionalFields.fieldName0,
},
fieldValue0: {
type: 'string',
label: i18n.ts._profile.metadataContent + ' 1',
default: additionalFields.fieldValue0,
},
fieldName1: {
type: 'string',
label: i18n.ts._profile.metadataLabel + ' 2',
default: additionalFields.fieldName1,
},
fieldValue1: {
type: 'string',
label: i18n.ts._profile.metadataContent + ' 2',
default: additionalFields.fieldValue1,
},
fieldName2: {
type: 'string',
label: i18n.ts._profile.metadataLabel + ' 3',
default: additionalFields.fieldName2,
},
fieldValue2: {
type: 'string',
label: i18n.ts._profile.metadataContent + ' 3',
default: additionalFields.fieldValue2,
},
fieldName3: {
type: 'string',
label: i18n.ts._profile.metadataLabel + ' 4',
default: additionalFields.fieldName3,
},
fieldValue3: {
type: 'string',
label: i18n.ts._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.ts.profile,

View file

@ -87,6 +87,7 @@
<script lang="ts">
import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue';
import * as JSON5 from 'json5';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormGroup from '@/components/form/group.vue';
@ -99,6 +100,8 @@ import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { ColdDeviceStorage } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { instance } from '@/instance';
import { concat } from '@/scripts/array';
import { fetchThemes, getThemes } from '@/theme-store';
import * as symbols from '@/symbols';
@ -122,9 +125,12 @@ export default defineComponent({
};
const installedThemes = ref(getThemes());
const themes = computed(() => builtinThemes.concat(installedThemes.value));
const darkThemes = computed(() => themes.value.filter(t => t.base == 'dark' || t.kind == 'dark'));
const lightThemes = computed(() => themes.value.filter(t => t.base == 'light' || t.kind == 'light'));
const instanceThemes = [];
if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
const themes = computed(() => instanceThemes.concat(builtinThemes.concat(installedThemes.value)));
const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
const darkTheme = ColdDeviceStorage.ref('darkTheme');
const darkThemeId = computed({
get() {

View file

@ -1,22 +1,22 @@
<template>
<form class="mk-setup" @submit.prevent="submit()">
<h1>Welcome to Misskey!</h1>
<div>
<div class="_formRoot">
<p>{{ $ts.intro }}</p>
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required data-cy-admin-username>
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required data-cy-admin-username class="_formBlock">
<template #label>{{ $ts.username }}</template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
<MkInput v-model="password" type="password" data-cy-admin-password>
<MkInput v-model="password" type="password" data-cy-admin-password class="_formBlock">
<template #label>{{ $ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
</MkInput>
<footer>
<MkButton primary type="submit" :disabled="submitting" data-cy-admin-ok>
<div class="bottom _formBlock">
<MkButton gradate type="submit" :disabled="submitting" data-cy-admin-ok>
{{ submitting ? $ts.processing : $ts.done }}<MkEllipsis v-if="submitting"/>
</MkButton>
</footer>
</div>
</div>
</form>
</template>
@ -92,7 +92,7 @@ export default defineComponent({
margin-top: 0;
}
> footer {
> .bottom {
> * {
margin: 0 auto;
}

View file

@ -5,34 +5,24 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
<script lang="ts" setup>
import { computed } from 'vue';
import XSetup from './welcome.setup.vue';
import XEntrance from './welcome.entrance.a.vue';
import { instanceName } from '@/config';
import * as os from '@/os';
import * as symbols from '@/symbols';
export default defineComponent({
components: {
XSetup,
XEntrance,
},
let meta = $ref(null);
data() {
return {
[symbols.PAGE_INFO]: {
title: instanceName,
icon: null
},
meta: null
}
},
os.api('meta', { detail: true }).then(res => {
meta = res;
});
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
}
defineExpose({
[symbols.PAGE_INFO]: computed(() => ({
title: instanceName,
icon: null,
})),
});
</script>

View file

@ -0,0 +1,666 @@
// TODO: sharedに置いてバックエンドのと統合したい
export const langmap = {
'ach': {
nativeName: 'Lwo',
},
'ady': {
nativeName: 'Адыгэбзэ',
},
'af': {
nativeName: 'Afrikaans',
},
'af-NA': {
nativeName: 'Afrikaans (Namibia)',
},
'af-ZA': {
nativeName: 'Afrikaans (South Africa)',
},
'ak': {
nativeName: 'Tɕɥi',
},
'ar': {
nativeName: 'العربية',
},
'ar-AR': {
nativeName: 'العربية',
},
'ar-MA': {
nativeName: 'العربية',
},
'ar-SA': {
nativeName: 'العربية (السعودية)',
},
'ay-BO': {
nativeName: 'Aymar aru',
},
'az': {
nativeName: 'Azərbaycan dili',
},
'az-AZ': {
nativeName: 'Azərbaycan dili',
},
'be-BY': {
nativeName: 'Беларуская',
},
'bg': {
nativeName: 'Български',
},
'bg-BG': {
nativeName: 'Български',
},
'bn': {
nativeName: 'বাংলা',
},
'bn-IN': {
nativeName: 'বাংলা (ভারত)',
},
'bn-BD': {
nativeName: 'বাংলা(বাংলাদেশ)',
},
'br': {
nativeName: 'Brezhoneg',
},
'bs-BA': {
nativeName: 'Bosanski',
},
'ca': {
nativeName: 'Català',
},
'ca-ES': {
nativeName: 'Català',
},
'cak': {
nativeName: 'Maya Kaqchikel',
},
'ck-US': {
nativeName: 'ᏣᎳᎩ (tsalagi)',
},
'cs': {
nativeName: 'Čeština',
},
'cs-CZ': {
nativeName: 'Čeština',
},
'cy': {
nativeName: 'Cymraeg',
},
'cy-GB': {
nativeName: 'Cymraeg',
},
'da': {
nativeName: 'Dansk',
},
'da-DK': {
nativeName: 'Dansk',
},
'de': {
nativeName: 'Deutsch',
},
'de-AT': {
nativeName: 'Deutsch (Österreich)',
},
'de-DE': {
nativeName: 'Deutsch (Deutschland)',
},
'de-CH': {
nativeName: 'Deutsch (Schweiz)',
},
'dsb': {
nativeName: 'Dolnoserbšćina',
},
'el': {
nativeName: 'Ελληνικά',
},
'el-GR': {
nativeName: 'Ελληνικά',
},
'en': {
nativeName: 'English',
},
'en-GB': {
nativeName: 'English (UK)',
},
'en-AU': {
nativeName: 'English (Australia)',
},
'en-CA': {
nativeName: 'English (Canada)',
},
'en-IE': {
nativeName: 'English (Ireland)',
},
'en-IN': {
nativeName: 'English (India)',
},
'en-PI': {
nativeName: 'English (Pirate)',
},
'en-SG': {
nativeName: 'English (Singapore)',
},
'en-UD': {
nativeName: 'English (Upside Down)',
},
'en-US': {
nativeName: 'English (US)',
},
'en-ZA': {
nativeName: 'English (South Africa)',
},
'en@pirate': {
nativeName: 'English (Pirate)',
},
'eo': {
nativeName: 'Esperanto',
},
'eo-EO': {
nativeName: 'Esperanto',
},
'es': {
nativeName: 'Español',
},
'es-AR': {
nativeName: 'Español (Argentine)',
},
'es-419': {
nativeName: 'Español (Latinoamérica)',
},
'es-CL': {
nativeName: 'Español (Chile)',
},
'es-CO': {
nativeName: 'Español (Colombia)',
},
'es-EC': {
nativeName: 'Español (Ecuador)',
},
'es-ES': {
nativeName: 'Español (España)',
},
'es-LA': {
nativeName: 'Español (Latinoamérica)',
},
'es-NI': {
nativeName: 'Español (Nicaragua)',
},
'es-MX': {
nativeName: 'Español (México)',
},
'es-US': {
nativeName: 'Español (Estados Unidos)',
},
'es-VE': {
nativeName: 'Español (Venezuela)',
},
'et': {
nativeName: 'eesti keel',
},
'et-EE': {
nativeName: 'Eesti (Estonia)',
},
'eu': {
nativeName: 'Euskara',
},
'eu-ES': {
nativeName: 'Euskara',
},
'fa': {
nativeName: 'فارسی',
},
'fa-IR': {
nativeName: 'فارسی',
},
'fb-LT': {
nativeName: 'Leet Speak',
},
'ff': {
nativeName: 'Fulah',
},
'fi': {
nativeName: 'Suomi',
},
'fi-FI': {
nativeName: 'Suomi',
},
'fo': {
nativeName: 'Føroyskt',
},
'fo-FO': {
nativeName: 'Føroyskt (Færeyjar)',
},
'fr': {
nativeName: 'Français',
},
'fr-CA': {
nativeName: 'Français (Canada)',
},
'fr-FR': {
nativeName: 'Français (France)',
},
'fr-BE': {
nativeName: 'Français (Belgique)',
},
'fr-CH': {
nativeName: 'Français (Suisse)',
},
'fy-NL': {
nativeName: 'Frysk',
},
'ga': {
nativeName: 'Gaeilge',
},
'ga-IE': {
nativeName: 'Gaeilge',
},
'gd': {
nativeName: 'Gàidhlig',
},
'gl': {
nativeName: 'Galego',
},
'gl-ES': {
nativeName: 'Galego',
},
'gn-PY': {
nativeName: 'Avañe\'ẽ',
},
'gu-IN': {
nativeName: 'ગુજરાતી',
},
'gv': {
nativeName: 'Gaelg',
},
'gx-GR': {
nativeName: 'Ἑλληνική ἀρχαία',
},
'he': {
nativeName: 'עברית‏',
},
'he-IL': {
nativeName: 'עברית‏',
},
'hi': {
nativeName: 'हिन्दी',
},
'hi-IN': {
nativeName: 'हिन्दी',
},
'hr': {
nativeName: 'Hrvatski',
},
'hr-HR': {
nativeName: 'Hrvatski',
},
'hsb': {
nativeName: 'Hornjoserbšćina',
},
'ht': {
nativeName: 'Kreyòl',
},
'hu': {
nativeName: 'Magyar',
},
'hu-HU': {
nativeName: 'Magyar',
},
'hy': {
nativeName: 'Հայերեն',
},
'hy-AM': {
nativeName: 'Հայերեն (Հայաստան)',
},
'id': {
nativeName: 'Bahasa Indonesia',
},
'id-ID': {
nativeName: 'Bahasa Indonesia',
},
'is': {
nativeName: 'Íslenska',
},
'is-IS': {
nativeName: 'Íslenska (Iceland)',
},
'it': {
nativeName: 'Italiano',
},
'it-IT': {
nativeName: 'Italiano',
},
'ja': {
nativeName: '日本語',
},
'ja-JP': {
nativeName: '日本語 (日本)',
},
'jv-ID': {
nativeName: 'Basa Jawa',
},
'ka-GE': {
nativeName: 'ქართული',
},
'kk-KZ': {
nativeName: 'Қазақша',
},
'km': {
nativeName: 'ភាសាខ្មែរ',
},
'kl': {
nativeName: 'kalaallisut',
},
'km-KH': {
nativeName: 'ភាសាខ្មែរ',
},
'kab': {
nativeName: 'Taqbaylit',
},
'kn': {
nativeName: 'ಕನ್ನಡ',
},
'kn-IN': {
nativeName: 'ಕನ್ನಡ (India)',
},
'ko': {
nativeName: '한국어',
},
'ko-KR': {
nativeName: '한국어 (한국)',
},
'ku-TR': {
nativeName: 'Kurdî',
},
'kw': {
nativeName: 'Kernewek',
},
'la': {
nativeName: 'Latin',
},
'la-VA': {
nativeName: 'Latin',
},
'lb': {
nativeName: 'Lëtzebuergesch',
},
'li-NL': {
nativeName: 'Lèmbörgs',
},
'lt': {
nativeName: 'Lietuvių',
},
'lt-LT': {
nativeName: 'Lietuvių',
},
'lv': {
nativeName: 'Latviešu',
},
'lv-LV': {
nativeName: 'Latviešu',
},
'mai': {
nativeName: 'मैथिली, মৈথিলী',
},
'mg-MG': {
nativeName: 'Malagasy',
},
'mk': {
nativeName: 'Македонски',
},
'mk-MK': {
nativeName: 'Македонски (Македонски)',
},
'ml': {
nativeName: 'മലയാളം',
},
'ml-IN': {
nativeName: 'മലയാളം',
},
'mn-MN': {
nativeName: 'Монгол',
},
'mr': {
nativeName: 'मराठी',
},
'mr-IN': {
nativeName: 'मराठी',
},
'ms': {
nativeName: 'Bahasa Melayu',
},
'ms-MY': {
nativeName: 'Bahasa Melayu',
},
'mt': {
nativeName: 'Malti',
},
'mt-MT': {
nativeName: 'Malti',
},
'my': {
nativeName: 'ဗမာစကာ',
},
'no': {
nativeName: 'Norsk',
},
'nb': {
nativeName: 'Norsk (bokmål)',
},
'nb-NO': {
nativeName: 'Norsk (bokmål)',
},
'ne': {
nativeName: 'नेपाली',
},
'ne-NP': {
nativeName: 'नेपाली',
},
'nl': {
nativeName: 'Nederlands',
},
'nl-BE': {
nativeName: 'Nederlands (België)',
},
'nl-NL': {
nativeName: 'Nederlands (Nederland)',
},
'nn-NO': {
nativeName: 'Norsk (nynorsk)',
},
'oc': {
nativeName: 'Occitan',
},
'or-IN': {
nativeName: 'ଓଡ଼ିଆ',
},
'pa': {
nativeName: 'ਪੰਜਾਬੀ',
},
'pa-IN': {
nativeName: 'ਪੰਜਾਬੀ (ਭਾਰਤ ਨੂੰ)',
},
'pl': {
nativeName: 'Polski',
},
'pl-PL': {
nativeName: 'Polski',
},
'ps-AF': {
nativeName: 'پښتو',
},
'pt': {
nativeName: 'Português',
},
'pt-BR': {
nativeName: 'Português (Brasil)',
},
'pt-PT': {
nativeName: 'Português (Portugal)',
},
'qu-PE': {
nativeName: 'Qhichwa',
},
'rm-CH': {
nativeName: 'Rumantsch',
},
'ro': {
nativeName: 'Română',
},
'ro-RO': {
nativeName: 'Română',
},
'ru': {
nativeName: 'Русский',
},
'ru-RU': {
nativeName: 'Русский',
},
'sa-IN': {
nativeName: 'संस्कृतम्',
},
'se-NO': {
nativeName: 'Davvisámegiella',
},
'sh': {
nativeName: 'српскохрватски',
},
'si-LK': {
nativeName: 'සිංහල',
},
'sk': {
nativeName: 'Slovenčina',
},
'sk-SK': {
nativeName: 'Slovenčina (Slovakia)',
},
'sl': {
nativeName: 'Slovenščina',
},
'sl-SI': {
nativeName: 'Slovenščina',
},
'so-SO': {
nativeName: 'Soomaaliga',
},
'sq': {
nativeName: 'Shqip',
},
'sq-AL': {
nativeName: 'Shqip',
},
'sr': {
nativeName: 'Српски',
},
'sr-RS': {
nativeName: 'Српски (Serbia)',
},
'su': {
nativeName: 'Basa Sunda',
},
'sv': {
nativeName: 'Svenska',
},
'sv-SE': {
nativeName: 'Svenska',
},
'sw': {
nativeName: 'Kiswahili',
},
'sw-KE': {
nativeName: 'Kiswahili',
},
'ta': {
nativeName: 'தமிழ்',
},
'ta-IN': {
nativeName: 'தமிழ்',
},
'te': {
nativeName: 'తెలుగు',
},
'te-IN': {
nativeName: 'తెలుగు',
},
'tg': {
nativeName: 'забо́ни тоҷикӣ́',
},
'tg-TJ': {
nativeName: 'тоҷикӣ',
},
'th': {
nativeName: 'ภาษาไทย',
},
'th-TH': {
nativeName: 'ภาษาไทย (ประเทศไทย)',
},
'fil': {
nativeName: 'Filipino',
},
'tlh': {
nativeName: 'tlhIngan-Hol',
},
'tr': {
nativeName: 'Türkçe',
},
'tr-TR': {
nativeName: 'Türkçe',
},
'tt-RU': {
nativeName: 'татарча',
},
'uk': {
nativeName: 'Українська',
},
'uk-UA': {
nativeName: 'Українська',
},
'ur': {
nativeName: 'اردو',
},
'ur-PK': {
nativeName: 'اردو',
},
'uz': {
nativeName: 'O\'zbek',
},
'uz-UZ': {
nativeName: 'O\'zbek',
},
'vi': {
nativeName: 'Tiếng Việt',
},
'vi-VN': {
nativeName: 'Tiếng Việt',
},
'xh-ZA': {
nativeName: 'isiXhosa',
},
'yi': {
nativeName: 'ייִדיש',
},
'yi-DE': {
nativeName: 'ייִדיש (German)',
},
'zh': {
nativeName: '中文',
},
'zh-Hans': {
nativeName: '中文简体',
},
'zh-Hant': {
nativeName: '中文繁體',
},
'zh-CN': {
nativeName: '中文(中国大陆)',
},
'zh-HK': {
nativeName: '中文(香港)',
},
'zh-SG': {
nativeName: '中文(新加坡)',
},
'zh-TW': {
nativeName: '中文(台灣)',
},
'zu-ZA': {
nativeName: 'isiZulu',
},
};

View file

@ -17,6 +17,7 @@ export const themeProps = Object.keys(lightTheme.props).filter(key => !key.start
export const builtinThemes = [
require('@/themes/l-light.json5'),
require('@/themes/l-coffee.json5'),
require('@/themes/l-apricot.json5'),
require('@/themes/l-rainy.json5'),
require('@/themes/l-vivid.json5'),
@ -29,6 +30,7 @@ export const builtinThemes = [
require('@/themes/d-future.json5'),
require('@/themes/d-botanical.json5'),
require('@/themes/d-cherry.json5'),
require('@/themes/d-ice.json5'),
require('@/themes/d-pumpkin.json5'),
require('@/themes/d-black.json5'),
] as Theme[];

View file

@ -17,7 +17,7 @@ export const defaultStore = markRaw(new Storage('base', {
},
keepCw: {
where: 'account',
default: false
default: true
},
showFullAcct: {
where: 'account',
@ -230,6 +230,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: ''
},
themeInitial: {
where: 'device',
default: true,
},
aiChanMode: {
where: 'device',
default: false

View file

@ -0,0 +1,13 @@
{
id: '66e7e5a9-cd43-42cd-837d-12f47841fa34',
name: 'Mi Ice Dark',
author: 'syuilo',
base: 'dark',
props: {
accent: '#47BFE8',
bg: '#212526',
},
}

View file

@ -0,0 +1,21 @@
{
id: '6ed80faa-74f0-42c2-98e4-a64d9e138eab',
name: 'Mi Coffee Light',
author: 'syuilo',
base: 'light',
props: {
accent: '#9f8989',
bg: '#f5f3f3',
fg: '#7f6666',
panel: '#fff',
divider: 'rgba(87, 68, 68, 0.1)',
renote: 'rgb(160, 172, 125)',
link: 'rgb(137, 151, 159)',
mention: '@accent',
mentionMe: 'rgb(170, 149, 98)',
hashtag: '@accent',
},
}

View file

@ -11,6 +11,8 @@
<XStreamIndicator/>
<div v-if="pendingApiRequestsCount > 0" id="wait"></div>
<div v-if="dev" id="devTicker"><span>DEV BUILD</span></div>
</template>
<script lang="ts">
@ -58,12 +60,28 @@ export default defineComponent({
uploads,
popups,
pendingApiRequestsCount,
dev: _DEV_,
};
},
});
</script>
<style lang="scss">
@keyframes dev-ticker-blink {
0% { opacity: 1; }
50% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes progress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#wait {
display: block;
position: fixed;
@ -85,12 +103,19 @@ export default defineComponent({
}
}
@keyframes progress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
#devTicker {
position: fixed;
top: 0;
left: 0;
z-index: 2147483647;
color: #ff0;
background: rgba(0, 0, 0, 0.5);
padding: 4px 5px;
font-size: 14px;
pointer-events: none;
> span {
animation: dev-ticker-blink 2s infinite;
}
}
</style>

View file

@ -25,69 +25,55 @@
<MkA v-click-anime class="item" active-class="active" to="/settings">
<i class="fas fa-cog fa-fw"></i><span class="text">{{ $ts.settings }}</span>
</MkA>
<button class="item _button post" data-cy-open-post-form @click="post">
<button class="item _button post" data-cy-open-post-form @click="os.post">
<i class="fas fa-pencil-alt fa-fw"></i><span class="text">{{ $ts.note }}</span>
</button>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, watch } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import * as os from '@/os';
import { menuDef } from '@/menu';
import { openAccountMenu } from '@/account';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
import { defaultStore } from '@/store';
export default defineComponent({
setup(props, context) {
const iconOnly = ref(false);
const iconOnly = ref(false);
const menu = computed(() => defaultStore.state.menu);
const otherMenuItemIndicated = computed(() => {
for (const def in menuDef) {
if (menu.value.includes(def)) continue;
if (menuDef[def].indicated) return true;
}
return false;
});
const calcViewState = () => {
iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon');
};
calcViewState();
window.addEventListener('resize', calcViewState);
watch(defaultStore.reactiveState.menuDisplay, () => {
calcViewState();
});
return {
host: host,
accounts: [],
connection: null,
menu,
menuDef: menuDef,
otherMenuItemIndicated,
iconOnly,
post: os.post,
search,
openAccountMenu:(ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
more: () => {
os.popup(import('@/components/launch-pad.vue'), {}, {
}, 'closed');
},
};
},
const menu = computed(() => defaultStore.state.menu);
const otherMenuItemIndicated = computed(() => {
for (const def in menuDef) {
if (menu.value.includes(def)) continue;
if (menuDef[def].indicated) return true;
}
return false;
});
const calcViewState = () => {
iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon');
};
calcViewState();
window.addEventListener('resize', calcViewState);
watch(defaultStore.reactiveState.menuDisplay, () => {
calcViewState();
});
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({
withExtraOperation: true,
}, ev);
}
function more(ev: MouseEvent) {
os.popup(import('@/components/launch-pad.vue'), {
src: ev.currentTarget ?? ev.target,
}, {
}, 'closed');
}
</script>
<style lang="scss" scoped>

View file

@ -101,11 +101,13 @@ export default defineComponent({
},
more(ev) {
os.popup(import('@/components/launch-pad.vue'), {}, {
os.popup(import('@/components/launch-pad.vue'), {
src: ev.currentTarget ?? ev.target,
}, {
}, 'closed');
},
openAccountMenu:(ev) => {
openAccountMenu: (ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);

View file

@ -122,6 +122,7 @@ export default defineComponent({
more(ev) {
os.popup(import('@/components/launch-pad.vue'), {}, {
src: ev.currentTarget ?? ev.target,
}, 'closed');
},