feat(client): status bar (experimental)

This commit is contained in:
syuilo
2022-07-03 14:40:02 +09:00
parent f8f3ecbf02
commit 44c85aff86
12 changed files with 658 additions and 51 deletions

View File

@@ -113,6 +113,11 @@ const menuDef = computed(() => [{
text: i18n.ts.theme,
to: '/settings/theme',
active: props.initialPage === 'theme',
}, {
icon: 'fas fa-list-ul',
text: i18n.ts.statusbar,
to: '/settings/statusbars',
active: props.initialPage === 'statusbars',
}, {
icon: 'fas fa-list-ul',
text: i18n.ts.menu,
@@ -221,6 +226,7 @@ const component = computed(() => {
case 'theme/install': return defineAsyncComponent(() => import('./theme.install.vue'));
case 'theme/manage': return defineAsyncComponent(() => import('./theme.manage.vue'));
case 'menu': return defineAsyncComponent(() => import('./menu.vue'));
case 'statusbars': return defineAsyncComponent(() => import('./statusbars.vue'));
case 'sounds': return defineAsyncComponent(() => import('./sounds.vue'));
case 'custom-css': return defineAsyncComponent(() => import('./custom-css.vue'));
case 'deck': return defineAsyncComponent(() => import('./deck.vue'));

View File

@@ -0,0 +1,122 @@
<template>
<div class="_formRoot">
<FormSelect v-model="statusbar.type" placeholder="Please select" class="_formBlock">
<template #label>{{ i18n.ts.type }}</template>
<option value="rss">RSS</option>
<option value="federation">Federation</option>
<option value="userList">User list timeline</option>
</FormSelect>
<MkInput v-model="statusbar.name" class="_formBlock">
<template #label>Name</template>
</MkInput>
<MkSwitch v-model="statusbar.black" class="_formBlock">
<template #label>Black</template>
</MkSwitch>
<template v-if="statusbar.type === 'rss'">
<MkInput v-model="statusbar.props.url" class="_formBlock" type="url">
<template #label>URL</template>
</MkInput>
<MkInput v-model="statusbar.props.refreshIntervalSec" class="_formBlock" type="number">
<template #label>Refresh interval</template>
</MkInput>
<MkInput v-model="statusbar.props.marqueeDuration" class="_formBlock" type="number">
<template #label>Duration</template>
</MkInput>
<MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock">
<template #label>Reverse</template>
</MkSwitch>
</template>
<template v-else-if="statusbar.type === 'federation'">
<MkInput v-model="statusbar.props.refreshIntervalSec" class="_formBlock" type="number">
<template #label>Refresh interval</template>
</MkInput>
<MkInput v-model="statusbar.props.marqueeDuration" class="_formBlock" type="number">
<template #label>Duration</template>
</MkInput>
<MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock">
<template #label>Reverse</template>
</MkSwitch>
<MkSwitch v-model="statusbar.props.colored" class="_formBlock">
<template #label>Colored</template>
</MkSwitch>
</template>
<template v-else-if="statusbar.type === 'userList' && userLists != null">
<FormSelect v-model="statusbar.props.userListId" class="_formBlock">
<template #label>{{ i18n.ts.userList }}</template>
<option v-for="list in userLists" :value="list.id">{{ list.name }}</option>
</FormSelect>
<MkInput v-model="statusbar.props.refreshIntervalSec" class="_formBlock" type="number">
<template #label>Refresh interval</template>
</MkInput>
<MkInput v-model="statusbar.props.marqueeDuration" class="_formBlock" type="number">
<template #label>Duration</template>
</MkInput>
<MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock">
<template #label>Reverse</template>
</MkSwitch>
</template>
<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
<FormButton @click="save">save</FormButton>
<FormButton danger @click="del">Delete</FormButton>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import FormSelect from '@/components/form/select.vue';
import MkInput from '@/components/form/input.vue';
import MkSwitch from '@/components/form/switch.vue';
import FormRadios from '@/components/form/radios.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { menuDef } from '@/menu';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
const props = defineProps<{
_id: string;
userLists: any[] | null;
}>();
const statusbar = reactive(JSON.parse(JSON.stringify(defaultStore.state.statusbars.find(x => x.id === props._id))));
watch(() => statusbar.type, () => {
if (statusbar.type === 'rss') {
statusbar.name = 'NEWS';
statusbar.props.url = 'http://feeds.afpbb.com/rss/afpbb/afpbbnews';
statusbar.props.refreshIntervalSec = 120;
statusbar.props.display = 'marquee';
statusbar.props.marqueeDuration = 100;
statusbar.props.marqueeReverse = false;
} else if (statusbar.type === 'federation') {
statusbar.name = 'FEDERATION';
statusbar.props.refreshIntervalSec = 120;
statusbar.props.display = 'marquee';
statusbar.props.marqueeDuration = 100;
statusbar.props.marqueeReverse = false;
statusbar.props.colored = false;
} else if (statusbar.type === 'userList') {
statusbar.name = 'LIST TL';
statusbar.props.refreshIntervalSec = 120;
statusbar.props.display = 'marquee';
statusbar.props.marqueeDuration = 100;
statusbar.props.marqueeReverse = false;
}
});
async function save() {
const i = defaultStore.state.statusbars.findIndex(x => x.id === props._id);
const statusbars = JSON.parse(JSON.stringify(defaultStore.state.statusbars));
statusbars[i] = JSON.parse(JSON.stringify(statusbar));
defaultStore.set('statusbars', statusbars);
}
function del() {
defaultStore.set('statusbars', defaultStore.state.statusbars.filter(x => x.id !== props._id));
}
</script>

View File

@@ -0,0 +1,61 @@
<template>
<div class="_formRoot">
<FormFolder v-for="x in statusbars" :key="x.id" class="_formBlock">
<template #label>{{ x.type ?? i18n.ts.notSet }}</template>
<template #suffix>{{ x.name }}</template>
<XStatusbar :_id="x.id" :user-lists="userLists"/>
</FormFolder>
<FormButton @click="add">add</FormButton>
<FormRadios v-model="statusbarSize" class="_formBlock">
<template #label>Size</template>
<option value="verySmall">{{ i18n.ts.small }}+</option>
<option value="small">{{ i18n.ts.small }}</option>
<option value="medium">{{ i18n.ts.medium }}</option>
<option value="large">{{ i18n.ts.large }}</option>
</FormRadios>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import XStatusbar from './statusbars.statusbar.vue';
import FormRadios from '@/components/form/radios.vue';
import FormFolder from '@/components/form/folder.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { unisonReload } from '@/scripts/unison-reload';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
const statusbarSize = computed(defaultStore.makeGetterSetter('statusbarSize'));
const statusbars = defaultStore.reactiveState.statusbars;
let userLists = $ref();
onMounted(() => {
os.api('users/lists/list').then(res => {
userLists = res;
});
});
async function add() {
defaultStore.push('statusbars', {
id: uuid(),
type: null,
black: false,
props: {},
});
}
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: i18n.ts.statusbar,
icon: 'fas fa-list-ul',
bg: 'var(--bg)',
});
</script>