mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 15:34:13 +00:00 
			
		
		
		
	enhance(client): リスト、アンテナタイムラインを個別ページとして分割
This commit is contained in:
		
							parent
							
								
									b35ca3b739
								
							
						
					
					
						commit
						72a49f334a
					
				
					 9 changed files with 348 additions and 70 deletions
				
			
		| 
						 | 
				
			
			@ -8,13 +8,14 @@
 | 
			
		|||
-->
 | 
			
		||||
 | 
			
		||||
## 12.x.x (unreleased)
 | 
			
		||||
- ActivityPub: deliverキューのメモリ使用量を削減
 | 
			
		||||
 | 
			
		||||
### Improvements
 | 
			
		||||
- ActivityPub: リモートユーザーのDeleteアクティビティに対応
 | 
			
		||||
- ActivityPub: add resolver check for blocked instance
 | 
			
		||||
- ActivityPub: deliverキューのメモリ使用量を削減
 | 
			
		||||
- アカウントが凍結された場合に、凍結された旨を表示してからログアウトするように
 | 
			
		||||
- 凍結されたアカウントにログインしようとしたときに、凍結されている旨を表示するように
 | 
			
		||||
- リスト、アンテナタイムラインを個別ページとして分割
 | 
			
		||||
- UIの改善
 | 
			
		||||
 | 
			
		||||
### Bugfixes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, ref } from 'vue';
 | 
			
		||||
import { defineComponent, ref, unref } from 'vue';
 | 
			
		||||
import { focusPrev, focusNext } from '@client/scripts/focus';
 | 
			
		||||
import contains from '@client/scripts/contains';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -79,21 +79,26 @@ export default defineComponent({
 | 
			
		|||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		const items = ref(this.items.filter(item => item !== undefined));
 | 
			
		||||
	watch: {
 | 
			
		||||
		items: {
 | 
			
		||||
			handler() {
 | 
			
		||||
				const items = ref(unref(this.items).filter(item => item !== undefined));
 | 
			
		||||
 | 
			
		||||
		for (let i = 0; i < items.value.length; i++) {
 | 
			
		||||
			const item = items.value[i];
 | 
			
		||||
				for (let i = 0; i < items.value.length; i++) {
 | 
			
		||||
					const item = items.value[i];
 | 
			
		||||
					
 | 
			
		||||
			if (item && item.then) { // if item is Promise
 | 
			
		||||
				items.value[i] = { type: 'pending' };
 | 
			
		||||
				item.then(actualItem => {
 | 
			
		||||
					items.value[i] = actualItem;
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
					if (item && item.then) { // if item is Promise
 | 
			
		||||
						items.value[i] = { type: 'pending' };
 | 
			
		||||
						item.then(actualItem => {
 | 
			
		||||
							items.value[i] = actualItem;
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				this._items = items;
 | 
			
		||||
			},
 | 
			
		||||
			immediate: true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this._items = items;
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.viaKeyboard) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,10 @@
 | 
			
		|||
import { computed } from 'vue';
 | 
			
		||||
import { computed, ref } from 'vue';
 | 
			
		||||
import { search } from '@client/scripts/search';
 | 
			
		||||
import * as os from '@client/os';
 | 
			
		||||
import { i18n } from '@client/i18n';
 | 
			
		||||
import { $i } from './account';
 | 
			
		||||
import { unisonReload } from '@client/scripts/unison-reload';
 | 
			
		||||
import { router } from './router';
 | 
			
		||||
 | 
			
		||||
export const menuDef = {
 | 
			
		||||
	notifications: {
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,26 @@ export const menuDef = {
 | 
			
		|||
		title: 'lists',
 | 
			
		||||
		icon: 'fas fa-list-ul',
 | 
			
		||||
		show: computed(() => $i != null),
 | 
			
		||||
		to: '/my/lists',
 | 
			
		||||
		active: computed(() => router.currentRoute.value.path.startsWith('/timeline/list/') || router.currentRoute.value.path === '/my/lists' || router.currentRoute.value.path.startsWith('/my/lists/')),
 | 
			
		||||
		action: (ev) => {
 | 
			
		||||
			const items = ref([{
 | 
			
		||||
				type: 'pending'
 | 
			
		||||
			}]);
 | 
			
		||||
			os.api('users/lists/list').then(lists => {
 | 
			
		||||
				const _items = [...lists.map(list => ({
 | 
			
		||||
					type: 'link',
 | 
			
		||||
					text: list.name,
 | 
			
		||||
					to: `/timeline/list/${list.id}`
 | 
			
		||||
				})), null, {
 | 
			
		||||
					type: 'link',
 | 
			
		||||
					to: '/my/lists',
 | 
			
		||||
					text: i18n.locale.manageLists,
 | 
			
		||||
					icon: 'fas fa-cog',
 | 
			
		||||
				}];
 | 
			
		||||
				items.value = _items;
 | 
			
		||||
			});
 | 
			
		||||
			os.popupMenu(items, ev.currentTarget || ev.target);
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	groups: {
 | 
			
		||||
		title: 'groups',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -372,7 +372,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
 | 
			
		|||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function popupMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
 | 
			
		||||
export function popupMenu(items: any[] | Ref<any[]>, src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
 | 
			
		||||
	return new Promise((resolve, reject) => {
 | 
			
		||||
		let dispose;
 | 
			
		||||
		popup(import('@client/components/ui/popup-menu.vue'), {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										147
									
								
								src/client/pages/antenna-timeline.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/client/pages/antenna-timeline.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="tqmomfks" v-hotkey.global="keymap" v-size="{ min: [800] }">
 | 
			
		||||
	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
 | 
			
		||||
	<div class="tl _block">
 | 
			
		||||
		<XTimeline ref="tl" class="tl"
 | 
			
		||||
			:key="antennaId"
 | 
			
		||||
			src="antenna"
 | 
			
		||||
			:antenna="antennaId"
 | 
			
		||||
			:sound="true"
 | 
			
		||||
			@before="before()"
 | 
			
		||||
			@after="after()"
 | 
			
		||||
			@queue="queueUpdated"
 | 
			
		||||
		/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, defineAsyncComponent, computed } from 'vue';
 | 
			
		||||
import Progress from '@client/scripts/loading';
 | 
			
		||||
import XTimeline from '@client/components/timeline.vue';
 | 
			
		||||
import { scroll } from '@client/scripts/scroll';
 | 
			
		||||
import * as os from '@client/os';
 | 
			
		||||
import * as symbols from '@client/symbols';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XTimeline,
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		antennaId: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			antenna: null,
 | 
			
		||||
			queue: 0,
 | 
			
		||||
			[symbols.PAGE_INFO]: computed(() => this.antenna ? {
 | 
			
		||||
				title: this.antenna.name,
 | 
			
		||||
				icon: 'fas fa-satellite',
 | 
			
		||||
				bg: 'var(--bg)',
 | 
			
		||||
				actions: [{
 | 
			
		||||
					icon: 'fas fa-calendar-alt',
 | 
			
		||||
					text: this.$ts.jumpToSpecifiedDate,
 | 
			
		||||
					handler: this.timetravel
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: 'fas fa-cog',
 | 
			
		||||
					text: this.$ts.settings,
 | 
			
		||||
					handler: this.settings
 | 
			
		||||
				}],
 | 
			
		||||
			} : null),
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				't': this.focus
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		antennaId: {
 | 
			
		||||
			async handler() {
 | 
			
		||||
				this.antenna = await os.api('antennas/show', {
 | 
			
		||||
					antennaId: this.antennaId
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
			immediate: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		before() {
 | 
			
		||||
			Progress.start();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		after() {
 | 
			
		||||
			Progress.done();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		queueUpdated(q) {
 | 
			
		||||
			this.queue = q;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		top() {
 | 
			
		||||
			scroll(this.$el, 0);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		async timetravel() {
 | 
			
		||||
			const { canceled, result: date } = await os.dialog({
 | 
			
		||||
				title: this.$ts.date,
 | 
			
		||||
				input: {
 | 
			
		||||
					type: 'date'
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
 | 
			
		||||
			this.$refs.tl.timetravel(new Date(date));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		settings() {
 | 
			
		||||
			this.$router.push(`/my/antennas/${this.antennaId}`);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.tl as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.tqmomfks {
 | 
			
		||||
	padding: var(--margin);
 | 
			
		||||
 | 
			
		||||
	> .new {
 | 
			
		||||
		position: sticky;
 | 
			
		||||
		top: calc(var(--stickyTop, 0px) + 16px);
 | 
			
		||||
		z-index: 1000;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
 | 
			
		||||
		> button {
 | 
			
		||||
			display: block;
 | 
			
		||||
			margin: var(--margin) auto 0 auto;
 | 
			
		||||
			padding: 8px 16px;
 | 
			
		||||
			border-radius: 32px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> .tl {
 | 
			
		||||
		background: var(--bg);
 | 
			
		||||
		border-radius: var(--radius);
 | 
			
		||||
		overflow: clip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&.min-width_800px {
 | 
			
		||||
		max-width: 800px;
 | 
			
		||||
		margin: 0 auto;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -6,11 +6,8 @@
 | 
			
		|||
	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
 | 
			
		||||
	<div class="tl _block">
 | 
			
		||||
		<XTimeline ref="tl" class="tl"
 | 
			
		||||
			:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
 | 
			
		||||
			:key="src"
 | 
			
		||||
			:src="src"
 | 
			
		||||
			:list="list ? list.id : null"
 | 
			
		||||
			:antenna="antenna ? antenna.id : null"
 | 
			
		||||
			:channel="channel ? channel.id : null"
 | 
			
		||||
			:sound="true"
 | 
			
		||||
			@before="before()"
 | 
			
		||||
			@after="after()"
 | 
			
		||||
| 
						 | 
				
			
			@ -41,10 +38,6 @@ export default defineComponent({
 | 
			
		|||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			src: 'home',
 | 
			
		||||
			list: null,
 | 
			
		||||
			antenna: null,
 | 
			
		||||
			channel: null,
 | 
			
		||||
			menuOpened: false,
 | 
			
		||||
			queue: 0,
 | 
			
		||||
			[symbols.PAGE_INFO]: computed(() => ({
 | 
			
		||||
				title: this.$ts.timeline,
 | 
			
		||||
| 
						 | 
				
			
			@ -116,32 +109,10 @@ export default defineComponent({
 | 
			
		|||
		src() {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
		},
 | 
			
		||||
		list(x) {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
			if (x != null) this.antenna = null;
 | 
			
		||||
			if (x != null) this.channel = null;
 | 
			
		||||
		},
 | 
			
		||||
		antenna(x) {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
			if (x != null) this.list = null;
 | 
			
		||||
			if (x != null) this.channel = null;
 | 
			
		||||
		},
 | 
			
		||||
		channel(x) {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
			if (x != null) this.antenna = null;
 | 
			
		||||
			if (x != null) this.list = null;
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		this.src = this.$store.state.tl.src;
 | 
			
		||||
		if (this.src === 'list') {
 | 
			
		||||
			this.list = this.$store.state.tl.arg;
 | 
			
		||||
		} else if (this.src === 'antenna') {
 | 
			
		||||
			this.antenna = this.$store.state.tl.arg;
 | 
			
		||||
		} else if (this.src === 'channel') {
 | 
			
		||||
			this.channel = this.$store.state.tl.arg;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -164,12 +135,9 @@ export default defineComponent({
 | 
			
		|||
		async chooseList(ev) {
 | 
			
		||||
			const lists = await os.api('users/lists/list');
 | 
			
		||||
			const items = lists.map(list => ({
 | 
			
		||||
				type: 'link',
 | 
			
		||||
				text: list.name,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					this.list = list;
 | 
			
		||||
					this.src = 'list';
 | 
			
		||||
					this.saveSrc();
 | 
			
		||||
				}
 | 
			
		||||
				to: `/timeline/list/${list.id}`
 | 
			
		||||
			}));
 | 
			
		||||
			os.popupMenu(items, ev.currentTarget || ev.target);
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -177,13 +145,10 @@ export default defineComponent({
 | 
			
		|||
		async chooseAntenna(ev) {
 | 
			
		||||
			const antennas = await os.api('antennas/list');
 | 
			
		||||
			const items = antennas.map(antenna => ({
 | 
			
		||||
				type: 'link',
 | 
			
		||||
				text: antenna.name,
 | 
			
		||||
				indicate: antenna.hasUnreadNote,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					this.antenna = antenna;
 | 
			
		||||
					this.src = 'antenna';
 | 
			
		||||
					this.saveSrc();
 | 
			
		||||
				}
 | 
			
		||||
				to: `/timeline/antenna/${antenna.id}`
 | 
			
		||||
			}));
 | 
			
		||||
			os.popupMenu(items, ev.currentTarget || ev.target);
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -191,15 +156,10 @@ export default defineComponent({
 | 
			
		|||
		async chooseChannel(ev) {
 | 
			
		||||
			const channels = await os.api('channels/followed');
 | 
			
		||||
			const items = channels.map(channel => ({
 | 
			
		||||
				type: 'link',
 | 
			
		||||
				text: channel.name,
 | 
			
		||||
				indicate: channel.hasUnreadNote,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					// NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で
 | 
			
		||||
					//this.channel = channel;
 | 
			
		||||
					//this.src = 'channel';
 | 
			
		||||
					//this.saveSrc();
 | 
			
		||||
					this.$router.push(`/channels/${channel.id}`);
 | 
			
		||||
				}
 | 
			
		||||
				to: `/channels/${channel.id}`
 | 
			
		||||
			}));
 | 
			
		||||
			os.popupMenu(items, ev.currentTarget || ev.target);
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -207,10 +167,6 @@ export default defineComponent({
 | 
			
		|||
		saveSrc() {
 | 
			
		||||
			this.$store.set('tl', {
 | 
			
		||||
				src: this.src,
 | 
			
		||||
				arg:
 | 
			
		||||
					this.src === 'list' ? this.list :
 | 
			
		||||
					this.src === 'antenna' ? this.antenna :
 | 
			
		||||
					this.channel
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										147
									
								
								src/client/pages/user-list-timeline.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/client/pages/user-list-timeline.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="eqqrhokj" v-hotkey.global="keymap" v-size="{ min: [800] }">
 | 
			
		||||
	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
 | 
			
		||||
	<div class="tl _block">
 | 
			
		||||
		<XTimeline ref="tl" class="tl"
 | 
			
		||||
			:key="listId"
 | 
			
		||||
			src="list"
 | 
			
		||||
			:list="listId"
 | 
			
		||||
			:sound="true"
 | 
			
		||||
			@before="before()"
 | 
			
		||||
			@after="after()"
 | 
			
		||||
			@queue="queueUpdated"
 | 
			
		||||
		/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, defineAsyncComponent, computed } from 'vue';
 | 
			
		||||
import Progress from '@client/scripts/loading';
 | 
			
		||||
import XTimeline from '@client/components/timeline.vue';
 | 
			
		||||
import { scroll } from '@client/scripts/scroll';
 | 
			
		||||
import * as os from '@client/os';
 | 
			
		||||
import * as symbols from '@client/symbols';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XTimeline,
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		listId: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			list: null,
 | 
			
		||||
			queue: 0,
 | 
			
		||||
			[symbols.PAGE_INFO]: computed(() => this.list ? {
 | 
			
		||||
				title: this.list.name,
 | 
			
		||||
				icon: 'fas fa-list-ul',
 | 
			
		||||
				bg: 'var(--bg)',
 | 
			
		||||
				actions: [{
 | 
			
		||||
					icon: 'fas fa-calendar-alt',
 | 
			
		||||
					text: this.$ts.jumpToSpecifiedDate,
 | 
			
		||||
					handler: this.timetravel
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: 'fas fa-cog',
 | 
			
		||||
					text: this.$ts.settings,
 | 
			
		||||
					handler: this.settings
 | 
			
		||||
				}],
 | 
			
		||||
			} : null),
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				't': this.focus
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		listId: {
 | 
			
		||||
			async handler() {
 | 
			
		||||
				this.list = await os.api('users/lists/show', {
 | 
			
		||||
					listId: this.listId
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
			immediate: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		before() {
 | 
			
		||||
			Progress.start();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		after() {
 | 
			
		||||
			Progress.done();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		queueUpdated(q) {
 | 
			
		||||
			this.queue = q;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		top() {
 | 
			
		||||
			scroll(this.$el, 0);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		settings() {
 | 
			
		||||
			this.$router.push(`/my/lists/${this.listId}`);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		async timetravel() {
 | 
			
		||||
			const { canceled, result: date } = await os.dialog({
 | 
			
		||||
				title: this.$ts.date,
 | 
			
		||||
				input: {
 | 
			
		||||
					type: 'date'
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
 | 
			
		||||
			this.$refs.tl.timetravel(new Date(date));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.tl as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.eqqrhokj {
 | 
			
		||||
	padding: var(--margin);
 | 
			
		||||
 | 
			
		||||
	> .new {
 | 
			
		||||
		position: sticky;
 | 
			
		||||
		top: calc(var(--stickyTop, 0px) + 16px);
 | 
			
		||||
		z-index: 1000;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
 | 
			
		||||
		> button {
 | 
			
		||||
			display: block;
 | 
			
		||||
			margin: var(--margin) auto 0 auto;
 | 
			
		||||
			padding: 8px 16px;
 | 
			
		||||
			border-radius: 32px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> .tl {
 | 
			
		||||
		background: var(--bg);
 | 
			
		||||
		border-radius: var(--radius);
 | 
			
		||||
		overflow: clip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&.min-width_800px {
 | 
			
		||||
		max-width: 800px;
 | 
			
		||||
		margin: 0 auto;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +48,8 @@ const defaultRoutes = [
 | 
			
		|||
	{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true },
 | 
			
		||||
	{ path: '/channels/:channelId', component: page('channel'), props: route => ({ channelId: route.params.channelId }) },
 | 
			
		||||
	{ path: '/clips/:clipId', component: page('clip'), props: route => ({ clipId: route.params.clipId }) },
 | 
			
		||||
	{ path: '/timeline/list/:listId', component: page('user-list-timeline'), props: route => ({ listId: route.params.listId }) },
 | 
			
		||||
	{ path: '/timeline/antenna/:antennaId', component: page('antenna-timeline'), props: route => ({ antennaId: route.params.antennaId }) },
 | 
			
		||||
	{ path: '/my/notifications', component: page('notifications') },
 | 
			
		||||
	{ path: '/my/favorites', component: page('favorites') },
 | 
			
		||||
	{ path: '/my/messages', component: page('messages') },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
				</MkA>
 | 
			
		||||
				<template v-for="item in menu">
 | 
			
		||||
					<div v-if="item === '-'" class="divider"></div>
 | 
			
		||||
					<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to" v-click-anime>
 | 
			
		||||
					<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="[item, { active: menuDef[item].active }]" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to" v-click-anime>
 | 
			
		||||
						<i class="fa-fw" :class="menuDef[item].icon"></i><span class="text">{{ $ts[menuDef[item].title] }}</span>
 | 
			
		||||
						<span v-if="menuDef[item].indicated" class="indicator"><i class="fas fa-circle"></i></span>
 | 
			
		||||
					</component>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue