デッキまわりをCompositon API / Setup Sugarに (#8410)
* universal.widgets.vue * column.vue, antenna-column.vue * direct-column.vue, list-column.vue * main-column.vue * wip * ✌️ * fix * ✌️ * ✌️
This commit is contained in:
		
							parent
							
								
									eb9e6d230f
								
							
						
					
					
						commit
						78736c70f7
					
				
					 15 changed files with 639 additions and 756 deletions
				
			
		| 
						 | 
				
			
			@ -17,7 +17,8 @@
 | 
			
		|||
			:key="ids[0]"
 | 
			
		||||
			class="column"
 | 
			
		||||
			:column="columns.find(c => c.id === ids[0])"
 | 
			
		||||
			:style="columns.find(c => c.id === ids[0]).flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0]).width + 'px' }"
 | 
			
		||||
			 :is-stacked="false"
 | 
			
		||||
			:style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }"
 | 
			
		||||
			@parent-focus="moveFocus(ids[0], $event)"
 | 
			
		||||
		/>
 | 
			
		||||
	</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -25,8 +26,8 @@
 | 
			
		|||
	<div v-if="isMobile" class="buttons">
 | 
			
		||||
		<button class="button nav _button" @click="drawerMenuShowing = true"><i class="fas fa-bars"></i><span v-if="menuIndicated" class="indicator"><i class="fas fa-circle"></i></span></button>
 | 
			
		||||
		<button class="button home _button" @click="$router.push('/')"><i class="fas fa-home"></i></button>
 | 
			
		||||
		<button class="button notifications _button" @click="$router.push('/my/notifications')"><i class="fas fa-bell"></i><span v-if="$i.hasUnreadNotification" class="indicator"><i class="fas fa-circle"></i></span></button>
 | 
			
		||||
		<button class="button post _button" @click="post()"><i class="fas fa-pencil-alt"></i></button>
 | 
			
		||||
		<button class="button notifications _button" @click="$router.push('/my/notifications')"><i class="fas fa-bell"></i><span v-if="$i?.hasUnreadNotification" class="indicator"><i class="fas fa-circle"></i></span></button>
 | 
			
		||||
		<button class="button post _button" @click="os.post()"><i class="fas fa-pencil-alt"></i></button>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<transition :name="$store.state.animation ? 'menu-back' : ''">
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +46,8 @@
 | 
			
		|||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, provide, ref, watch } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, provide, ref, watch } from 'vue';
 | 
			
		||||
import { v4 as uuid } from 'uuid';
 | 
			
		||||
import DeckColumnCore from '@/ui/deck/column-core.vue';
 | 
			
		||||
import XSidebar from '@/ui/_common_/sidebar.vue';
 | 
			
		||||
| 
						 | 
				
			
			@ -60,102 +61,82 @@ import { useRoute } from 'vue-router';
 | 
			
		|||
import { $i } from '@/account';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XCommon,
 | 
			
		||||
		XSidebar,
 | 
			
		||||
		XDrawerMenu,
 | 
			
		||||
		DeckColumnCore,
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	setup() {
 | 
			
		||||
		const isMobile = ref(window.innerWidth <= 500);
 | 
			
		||||
		window.addEventListener('resize', () => {
 | 
			
		||||
			isMobile.value = window.innerWidth <= 500;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const drawerMenuShowing = ref(false);
 | 
			
		||||
 | 
			
		||||
		const route = useRoute();
 | 
			
		||||
		watch(route, () => {
 | 
			
		||||
			drawerMenuShowing.value = false;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const columns = deckStore.reactiveState.columns;
 | 
			
		||||
		const layout = deckStore.reactiveState.layout;
 | 
			
		||||
		const menuIndicated = computed(() => {
 | 
			
		||||
			if ($i == null) return false;
 | 
			
		||||
			for (const def in menuDef) {
 | 
			
		||||
				if (menuDef[def].indicated) return true;
 | 
			
		||||
			}
 | 
			
		||||
			return false;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const addColumn = async (ev) => {
 | 
			
		||||
			const columns = [
 | 
			
		||||
				'main',
 | 
			
		||||
				'widgets',
 | 
			
		||||
				'notifications',
 | 
			
		||||
				'tl',
 | 
			
		||||
				'antenna',
 | 
			
		||||
				'list',
 | 
			
		||||
				'mentions',
 | 
			
		||||
				'direct',
 | 
			
		||||
			];
 | 
			
		||||
 | 
			
		||||
			const { canceled, result: column } = await os.select({
 | 
			
		||||
				title: i18n.ts._deck.addColumn,
 | 
			
		||||
				items: columns.map(column => ({
 | 
			
		||||
					value: column, text: i18n.t('_deck._columns.' + column)
 | 
			
		||||
				}))
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
 | 
			
		||||
			addColumnToStore({
 | 
			
		||||
				type: column,
 | 
			
		||||
				id: uuid(),
 | 
			
		||||
				name: i18n.t('_deck._columns.' + column),
 | 
			
		||||
				width: 330,
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		const onContextmenu = (ev) => {
 | 
			
		||||
			os.contextMenu([{
 | 
			
		||||
				text: i18n.ts._deck.addColumn,
 | 
			
		||||
				icon: null,
 | 
			
		||||
				action: addColumn
 | 
			
		||||
			}], ev);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		provide('shouldSpacerMin', true);
 | 
			
		||||
		if (deckStore.state.navWindow) {
 | 
			
		||||
			provide('navHook', (url) => {
 | 
			
		||||
				os.pageWindow(url);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		document.documentElement.style.overflowY = 'hidden';
 | 
			
		||||
		document.documentElement.style.scrollBehavior = 'auto';
 | 
			
		||||
		window.addEventListener('wheel', (ev) => {
 | 
			
		||||
			if (getScrollContainer(ev.target) == null) {
 | 
			
		||||
				document.documentElement.scrollLeft += ev.deltaY > 0 ? 96 : -96;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		loadDeck();
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			isMobile,
 | 
			
		||||
			deckStore,
 | 
			
		||||
			drawerMenuShowing,
 | 
			
		||||
			columns,
 | 
			
		||||
			layout,
 | 
			
		||||
			menuIndicated,
 | 
			
		||||
			onContextmenu,
 | 
			
		||||
			wallpaper: localStorage.getItem('wallpaper') != null,
 | 
			
		||||
			post: os.post,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
const isMobile = ref(window.innerWidth <= 500);
 | 
			
		||||
window.addEventListener('resize', () => {
 | 
			
		||||
	isMobile.value = window.innerWidth <= 500;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const drawerMenuShowing = ref(false);
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
watch(route, () => {
 | 
			
		||||
	drawerMenuShowing.value = false;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const columns = deckStore.reactiveState.columns;
 | 
			
		||||
const layout = deckStore.reactiveState.layout;
 | 
			
		||||
const menuIndicated = computed(() => {
 | 
			
		||||
	if ($i == null) return false;
 | 
			
		||||
	for (const def in menuDef) {
 | 
			
		||||
		if (menuDef[def].indicated) return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const addColumn = async (ev) => {
 | 
			
		||||
	const columns = [
 | 
			
		||||
		'main',
 | 
			
		||||
		'widgets',
 | 
			
		||||
		'notifications',
 | 
			
		||||
		'tl',
 | 
			
		||||
		'antenna',
 | 
			
		||||
		'list',
 | 
			
		||||
		'mentions',
 | 
			
		||||
		'direct',
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
	const { canceled, result: column } = await os.select({
 | 
			
		||||
		title: i18n.ts._deck.addColumn,
 | 
			
		||||
		items: columns.map(column => ({
 | 
			
		||||
			value: column, text: i18n.t('_deck._columns.' + column)
 | 
			
		||||
		}))
 | 
			
		||||
	});
 | 
			
		||||
	if (canceled) return;
 | 
			
		||||
 | 
			
		||||
	addColumnToStore({
 | 
			
		||||
		type: column,
 | 
			
		||||
		id: uuid(),
 | 
			
		||||
		name: i18n.t('_deck._columns.' + column),
 | 
			
		||||
		width: 330,
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const onContextmenu = (ev) => {
 | 
			
		||||
	os.contextMenu([{
 | 
			
		||||
		text: i18n.ts._deck.addColumn,
 | 
			
		||||
		action: addColumn,
 | 
			
		||||
	}], ev);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
provide('shouldSpacerMin', true);
 | 
			
		||||
if (deckStore.state.navWindow) {
 | 
			
		||||
	provide('navHook', (url) => {
 | 
			
		||||
		os.pageWindow(url);
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.documentElement.style.overflowY = 'hidden';
 | 
			
		||||
document.documentElement.style.scrollBehavior = 'auto';
 | 
			
		||||
window.addEventListener('wheel', (ev) => {
 | 
			
		||||
	if (getScrollContainer(ev.target as HTMLElement) == null) {
 | 
			
		||||
		document.documentElement.scrollLeft += ev.deltaY > 0 ? 96 : -96;
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
loadDeck();
 | 
			
		||||
 | 
			
		||||
function moveFocus(id: string, direction: 'up' | 'down' | 'left' | 'right') {
 | 
			
		||||
	// TODO??
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,75 +1,62 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :func="{ handler: setAntenna, title: $ts.selectAntenna }" :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn :func="{ handler: setAntenna, title: $ts.selectAntenna }" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header>
 | 
			
		||||
		<i class="fas fa-satellite"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 | 
			
		||||
	</template>
 | 
			
		||||
 | 
			
		||||
	<XTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => $emit('loaded')"/>
 | 
			
		||||
	<XTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/>
 | 
			
		||||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XTimeline from '@/components/timeline.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { updateColumn } from './deck-store';
 | 
			
		||||
import { updateColumn, Column } from './deck-store';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XTimeline,
 | 
			
		||||
	},
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'loaded'): void;
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
let timeline = $ref<InstanceType<typeof XTimeline>>();
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		mediaOnly() {
 | 
			
		||||
			(this.$refs.timeline as any).reload();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.column.antennaId == null) {
 | 
			
		||||
			this.setAntenna();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		async setAntenna() {
 | 
			
		||||
			const antennas = await os.api('antennas/list');
 | 
			
		||||
			const { canceled, result: antenna } = await os.select({
 | 
			
		||||
				title: this.$ts.selectAntenna,
 | 
			
		||||
				items: antennas.map(x => ({
 | 
			
		||||
					value: x, text: x.name
 | 
			
		||||
				})),
 | 
			
		||||
				default: this.column.antennaId
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
			updateColumn(this.column.id, {
 | 
			
		||||
				antennaId: antenna.id
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.timeline as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	if (props.column.antennaId == null) {
 | 
			
		||||
		setAntenna();
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function setAntenna() {
 | 
			
		||||
	const antennas = await os.api('antennas/list');
 | 
			
		||||
	const { canceled, result: antenna } = await os.select({
 | 
			
		||||
		title: i18n.ts.selectAntenna,
 | 
			
		||||
		items: antennas.map(x => ({
 | 
			
		||||
			value: x, text: x.name
 | 
			
		||||
		})),
 | 
			
		||||
		default: props.column.antennaId
 | 
			
		||||
	});
 | 
			
		||||
	if (canceled) return;
 | 
			
		||||
	updateColumn(props.column.id, {
 | 
			
		||||
		antennaId: antenna.id
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
function focus() {
 | 
			
		||||
	timeline.focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
	focus,
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,18 @@
 | 
			
		|||
<template>
 | 
			
		||||
<!-- TODO: リファクタの余地がありそう -->
 | 
			
		||||
<XMainColumn v-if="column.type === 'main'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XWidgetsColumn v-else-if="column.type === 'widgets'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XNotificationsColumn v-else-if="column.type === 'notifications'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XTlColumn v-else-if="column.type === 'tl'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XListColumn v-else-if="column.type === 'list'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XAntennaColumn v-else-if="column.type === 'antenna'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XMentionsColumn v-else-if="column.type === 'mentions'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<XDirectColumn v-else-if="column.type === 'direct'" :column="column" :is-stacked="isStacked" @parent-focus="$emit('parent-focus', $event)"/>
 | 
			
		||||
<div v-if="!column">たぶん見えちゃいけないやつ</div>
 | 
			
		||||
<XMainColumn v-else-if="column.type === 'main'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XWidgetsColumn v-else-if="column.type === 'widgets'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XNotificationsColumn v-else-if="column.type === 'notifications'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XTlColumn v-else-if="column.type === 'tl'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XListColumn v-else-if="column.type === 'list'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XAntennaColumn v-else-if="column.type === 'antenna'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XMentionsColumn v-else-if="column.type === 'mentions'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
<XDirectColumn v-else-if="column.type === 'direct'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { } from 'vue';
 | 
			
		||||
import XMainColumn from './main-column.vue';
 | 
			
		||||
import XTlColumn from './tl-column.vue';
 | 
			
		||||
import XAntennaColumn from './antenna-column.vue';
 | 
			
		||||
| 
						 | 
				
			
			@ -20,33 +21,24 @@ import XNotificationsColumn from './notifications-column.vue';
 | 
			
		|||
import XWidgetsColumn from './widgets-column.vue';
 | 
			
		||||
import XMentionsColumn from './mentions-column.vue';
 | 
			
		||||
import XDirectColumn from './direct-column.vue';
 | 
			
		||||
import { Column } from './deck-store';
 | 
			
		||||
 | 
			
		||||
defineProps<{
 | 
			
		||||
	column?: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XMainColumn,
 | 
			
		||||
		XTlColumn,
 | 
			
		||||
		XAntennaColumn,
 | 
			
		||||
		XListColumn,
 | 
			
		||||
		XNotificationsColumn,
 | 
			
		||||
		XWidgetsColumn,
 | 
			
		||||
		XMentionsColumn,
 | 
			
		||||
		XDirectColumn
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		focus() {
 | 
			
		||||
			this.$children[0].focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,238 +31,211 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
export type DeckFunc = {
 | 
			
		||||
	title: string;
 | 
			
		||||
	handler: (payload: MouseEvent) => void;
 | 
			
		||||
	icon?: string;
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onBeforeUnmount, onMounted, provide, watch } from 'vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn } from './deck-store';
 | 
			
		||||
import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store';
 | 
			
		||||
import { deckStore } from './deck-store';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	provide: {
 | 
			
		||||
		shouldHeaderThin: true,
 | 
			
		||||
		shouldOmitHeaderTitle: true,
 | 
			
		||||
	},
 | 
			
		||||
provide('shouldHeaderThin', true);
 | 
			
		||||
provide('shouldOmitHeaderTitle', true);
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: null
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		func: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: null
 | 
			
		||||
		},
 | 
			
		||||
		naked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		indicated: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			deckStore,
 | 
			
		||||
			dragging: false,
 | 
			
		||||
			draghover: false,
 | 
			
		||||
			dropready: false,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		isMainColumn(): boolean {
 | 
			
		||||
			return this.column.type === 'main';
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		active(): boolean {
 | 
			
		||||
			return this.column.active !== false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'shift+up': () => this.$parent.$emit('parent-focus', 'up'),
 | 
			
		||||
				'shift+down': () => this.$parent.$emit('parent-focus', 'down'),
 | 
			
		||||
				'shift+left': () => this.$parent.$emit('parent-focus', 'left'),
 | 
			
		||||
				'shift+right': () => this.$parent.$emit('parent-focus', 'right'),
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		active(v) {
 | 
			
		||||
			this.$emit('change-active-state', v);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		dragging(v) {
 | 
			
		||||
			os.deckGlobalEvents.emit(v ? 'column.dragStart' : 'column.dragEnd');
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		os.deckGlobalEvents.on('column.dragStart', this.onOtherDragStart);
 | 
			
		||||
		os.deckGlobalEvents.on('column.dragEnd', this.onOtherDragEnd);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeUnmount() {
 | 
			
		||||
		os.deckGlobalEvents.off('column.dragStart', this.onOtherDragStart);
 | 
			
		||||
		os.deckGlobalEvents.off('column.dragEnd', this.onOtherDragEnd);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		onOtherDragStart() {
 | 
			
		||||
			this.dropready = true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onOtherDragEnd() {
 | 
			
		||||
			this.dropready = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		toggleActive() {
 | 
			
		||||
			if (!this.isStacked) return;
 | 
			
		||||
			updateColumn(this.column.id, {
 | 
			
		||||
				active: !this.column.active
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		getMenu() {
 | 
			
		||||
			const items = [{
 | 
			
		||||
				icon: 'fas fa-pencil-alt',
 | 
			
		||||
				text: this.$ts.edit,
 | 
			
		||||
				action: async () => {
 | 
			
		||||
					const { canceled, result } = await os.form(this.column.name, {
 | 
			
		||||
						name: {
 | 
			
		||||
							type: 'string',
 | 
			
		||||
							label: this.$ts.name,
 | 
			
		||||
							default: this.column.name
 | 
			
		||||
						},
 | 
			
		||||
						width: {
 | 
			
		||||
							type: 'number',
 | 
			
		||||
							label: this.$ts.width,
 | 
			
		||||
							default: this.column.width
 | 
			
		||||
						},
 | 
			
		||||
						flexible: {
 | 
			
		||||
							type: 'boolean',
 | 
			
		||||
							label: this.$ts.flexible,
 | 
			
		||||
							default: this.column.flexible
 | 
			
		||||
						}
 | 
			
		||||
					});
 | 
			
		||||
					if (canceled) return;
 | 
			
		||||
					updateColumn(this.column.id, result);
 | 
			
		||||
				}
 | 
			
		||||
			}, null, {
 | 
			
		||||
				icon: 'fas fa-arrow-left',
 | 
			
		||||
				text: this.$ts._deck.swapLeft,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					swapLeftColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			}, {
 | 
			
		||||
				icon: 'fas fa-arrow-right',
 | 
			
		||||
				text: this.$ts._deck.swapRight,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					swapRightColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			}, this.isStacked ? {
 | 
			
		||||
				icon: 'fas fa-arrow-up',
 | 
			
		||||
				text: this.$ts._deck.swapUp,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					swapUpColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			} : undefined, this.isStacked ? {
 | 
			
		||||
				icon: 'fas fa-arrow-down',
 | 
			
		||||
				text: this.$ts._deck.swapDown,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					swapDownColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			} : undefined, null, {
 | 
			
		||||
				icon: 'fas fa-window-restore',
 | 
			
		||||
				text: this.$ts._deck.stackLeft,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					stackLeftColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			}, this.isStacked ? {
 | 
			
		||||
				icon: 'fas fa-window-maximize',
 | 
			
		||||
				text: this.$ts._deck.popRight,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					popRightColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			} : undefined, null, {
 | 
			
		||||
				icon: 'fas fa-trash-alt',
 | 
			
		||||
				text: this.$ts.remove,
 | 
			
		||||
				danger: true,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					removeColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
 | 
			
		||||
			return items;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onContextmenu(ev: MouseEvent) {
 | 
			
		||||
			os.contextMenu(this.getMenu(), ev);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		goTop() {
 | 
			
		||||
			this.$refs.body.scrollTo({
 | 
			
		||||
				top: 0,
 | 
			
		||||
				behavior: 'smooth'
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragstart(e) {
 | 
			
		||||
			e.dataTransfer.effectAllowed = 'move';
 | 
			
		||||
			e.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, this.column.id);
 | 
			
		||||
 | 
			
		||||
			// Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう
 | 
			
		||||
			// SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
 | 
			
		||||
			window.setTimeout(() => {
 | 
			
		||||
				this.dragging = true;
 | 
			
		||||
			}, 10);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragend(e) {
 | 
			
		||||
			this.dragging = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragover(e) {
 | 
			
		||||
			// 自分自身がドラッグされている場合
 | 
			
		||||
			if (this.dragging) {
 | 
			
		||||
				// 自分自身にはドロップさせない
 | 
			
		||||
				e.dataTransfer.dropEffect = 'none';
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const isDeckColumn = e.dataTransfer.types[0] == _DATA_TRANSFER_DECK_COLUMN_;
 | 
			
		||||
 | 
			
		||||
			e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
 | 
			
		||||
 | 
			
		||||
			if (!this.dragging && isDeckColumn) this.draghover = true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragleave() {
 | 
			
		||||
			this.draghover = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDrop(e) {
 | 
			
		||||
			this.draghover = false;
 | 
			
		||||
			os.deckGlobalEvents.emit('column.dragEnd');
 | 
			
		||||
 | 
			
		||||
			const id = e.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
 | 
			
		||||
			if (id != null && id != '') {
 | 
			
		||||
				swapColumn(this.column.id, id);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
const props = withDefaults(defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked?: boolean;
 | 
			
		||||
	func?: DeckFunc | null;
 | 
			
		||||
	naked?: boolean;
 | 
			
		||||
	indicated?: boolean;
 | 
			
		||||
}>(), {
 | 
			
		||||
	isStacked: false,
 | 
			
		||||
	func: null,
 | 
			
		||||
	naked: false,
 | 
			
		||||
	indicated: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
	(e: 'change-active-state', v: boolean): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
let body = $ref<HTMLDivElement>();
 | 
			
		||||
 | 
			
		||||
let dragging = $ref(false);
 | 
			
		||||
watch($$(dragging), v => os.deckGlobalEvents.emit(v ? 'column.dragStart' : 'column.dragEnd'));
 | 
			
		||||
 | 
			
		||||
let draghover = $ref(false);
 | 
			
		||||
let dropready = $ref(false);
 | 
			
		||||
 | 
			
		||||
const isMainColumn = $computed(() => props.column.type === 'main');
 | 
			
		||||
const active = $computed(() => props.column.active !== false);
 | 
			
		||||
watch($$(active), v => emit('change-active-state', v));
 | 
			
		||||
 | 
			
		||||
const keymap = $computed(() => ({
 | 
			
		||||
	'shift+up': () => emit('parent-focus', 'up'),
 | 
			
		||||
	'shift+down': () => emit('parent-focus', 'down'),
 | 
			
		||||
	'shift+left': () => emit('parent-focus', 'left'),
 | 
			
		||||
	'shift+right': () => emit('parent-focus', 'right'),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	os.deckGlobalEvents.on('column.dragStart', onOtherDragStart);
 | 
			
		||||
	os.deckGlobalEvents.on('column.dragEnd', onOtherDragEnd);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
	os.deckGlobalEvents.off('column.dragStart', onOtherDragStart);
 | 
			
		||||
	os.deckGlobalEvents.off('column.dragEnd', onOtherDragEnd);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function onOtherDragStart() {
 | 
			
		||||
	dropready = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onOtherDragEnd() {
 | 
			
		||||
	dropready = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleActive() {
 | 
			
		||||
	if (!props.isStacked) return;
 | 
			
		||||
	updateColumn(props.column.id, {
 | 
			
		||||
		active: !props.column.active
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getMenu() {
 | 
			
		||||
	const items = [{
 | 
			
		||||
		icon: 'fas fa-pencil-alt',
 | 
			
		||||
		text: i18n.ts.edit,
 | 
			
		||||
		action: async () => {
 | 
			
		||||
			const { canceled, result } = await os.form(props.column.name, {
 | 
			
		||||
				name: {
 | 
			
		||||
					type: 'string',
 | 
			
		||||
					label: i18n.ts.name,
 | 
			
		||||
					default: props.column.name
 | 
			
		||||
				},
 | 
			
		||||
				width: {
 | 
			
		||||
					type: 'number',
 | 
			
		||||
					label: i18n.ts.width,
 | 
			
		||||
					default: props.column.width
 | 
			
		||||
				},
 | 
			
		||||
				flexible: {
 | 
			
		||||
					type: 'boolean',
 | 
			
		||||
					label: i18n.ts.flexible,
 | 
			
		||||
					default: props.column.flexible
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
			updateColumn(props.column.id, result);
 | 
			
		||||
		}
 | 
			
		||||
	}, null, {
 | 
			
		||||
		icon: 'fas fa-arrow-left',
 | 
			
		||||
		text: i18n.ts._deck.swapLeft,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			swapLeftColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	}, {
 | 
			
		||||
		icon: 'fas fa-arrow-right',
 | 
			
		||||
		text: i18n.ts._deck.swapRight,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			swapRightColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	}, props.isStacked ? {
 | 
			
		||||
		icon: 'fas fa-arrow-up',
 | 
			
		||||
		text: i18n.ts._deck.swapUp,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			swapUpColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	} : undefined, props.isStacked ? {
 | 
			
		||||
		icon: 'fas fa-arrow-down',
 | 
			
		||||
		text: i18n.ts._deck.swapDown,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			swapDownColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	} : undefined, null, {
 | 
			
		||||
		icon: 'fas fa-window-restore',
 | 
			
		||||
		text: i18n.ts._deck.stackLeft,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			stackLeftColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	}, props.isStacked ? {
 | 
			
		||||
		icon: 'fas fa-window-maximize',
 | 
			
		||||
		text: i18n.ts._deck.popRight,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			popRightColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	} : undefined, null, {
 | 
			
		||||
		icon: 'fas fa-trash-alt',
 | 
			
		||||
		text: i18n.ts.remove,
 | 
			
		||||
		danger: true,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			removeColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	}];
 | 
			
		||||
	return items;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onContextmenu(ev: MouseEvent) {
 | 
			
		||||
	os.contextMenu(getMenu(), ev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function goTop() {
 | 
			
		||||
	body.scrollTo({
 | 
			
		||||
		top: 0,
 | 
			
		||||
		behavior: 'smooth'
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onDragstart(e) {
 | 
			
		||||
	e.dataTransfer.effectAllowed = 'move';
 | 
			
		||||
	e.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, props.column.id);
 | 
			
		||||
 | 
			
		||||
	// Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう
 | 
			
		||||
	// SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
 | 
			
		||||
	window.setTimeout(() => {
 | 
			
		||||
		dragging = true;
 | 
			
		||||
	}, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onDragend(e) {
 | 
			
		||||
	dragging = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onDragover(e) {
 | 
			
		||||
	// 自分自身がドラッグされている場合
 | 
			
		||||
	if (dragging) {
 | 
			
		||||
		// 自分自身にはドロップさせない
 | 
			
		||||
		e.dataTransfer.dropEffect = 'none';
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const isDeckColumn = e.dataTransfer.types[0] == _DATA_TRANSFER_DECK_COLUMN_;
 | 
			
		||||
 | 
			
		||||
	e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
 | 
			
		||||
 | 
			
		||||
	if (!dragging && isDeckColumn) draghover = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onDragleave() {
 | 
			
		||||
	draghover = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onDrop(e) {
 | 
			
		||||
	draghover = false;
 | 
			
		||||
	os.deckGlobalEvents.emit('column.dragEnd');
 | 
			
		||||
 | 
			
		||||
	const id = e.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
 | 
			
		||||
	if (id != null && id != '') {
 | 
			
		||||
		swapColumn(props.column.id, id);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
import { throttle } from 'throttle-debounce';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
import { api } from '@/os';
 | 
			
		||||
import { markRaw, watch } from 'vue';
 | 
			
		||||
import { markRaw } from 'vue';
 | 
			
		||||
import { Storage } from '../../pizzax';
 | 
			
		||||
import { notificationTypes } from 'misskey-js';
 | 
			
		||||
 | 
			
		||||
type ColumnWidget = {
 | 
			
		||||
	name: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -10,13 +11,18 @@ type ColumnWidget = {
 | 
			
		|||
	data: Record<string, any>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type Column = {
 | 
			
		||||
export type Column = {
 | 
			
		||||
	id: string;
 | 
			
		||||
	type: string;
 | 
			
		||||
	name: string | null;
 | 
			
		||||
	width: number;
 | 
			
		||||
	widgets?: ColumnWidget[];
 | 
			
		||||
	active?: boolean;
 | 
			
		||||
	flexible?: boolean;
 | 
			
		||||
	antennaId?: string;
 | 
			
		||||
	listId?: string;
 | 
			
		||||
	includingTypes?: typeof notificationTypes[number][];
 | 
			
		||||
	tl?: 'home' | 'local' | 'social' | 'global';
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function copy<T>(x: T): T {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header><i class="fas fa-envelope" style="margin-right: 8px;"></i>{{ column.name }}</template>
 | 
			
		||||
 | 
			
		||||
	<XNotes :pagination="pagination"/>
 | 
			
		||||
| 
						 | 
				
			
			@ -7,21 +7,25 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
import { } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XNotes from '@/components/notes.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Column } from './deck-store';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Record<string, unknown>; // TODO
 | 
			
		||||
defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const pagination = {
 | 
			
		||||
	point: 'notes/mentions' as const,
 | 
			
		||||
	endpoint: 'notes/mentions' as const,
 | 
			
		||||
	limit: 10,
 | 
			
		||||
	params: computed(() => ({
 | 
			
		||||
		visibility: 'specified' as const,
 | 
			
		||||
	})),
 | 
			
		||||
	params: {
 | 
			
		||||
		visibility: 'specified'
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,75 +1,65 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :func="{ handler: setList, title: $ts.selectList }" :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn :func="{ handler: setList, title: $ts.selectList }" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header>
 | 
			
		||||
		<i class="fas fa-list-ul"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 | 
			
		||||
	</template>
 | 
			
		||||
 | 
			
		||||
	<XTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => $emit('loaded')"/>
 | 
			
		||||
	<XTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/>
 | 
			
		||||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import {  } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XTimeline from '@/components/timeline.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { updateColumn } from './deck-store';
 | 
			
		||||
import { updateColumn, Column } from './deck-store';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'loaded'): void;
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
let timeline = $ref<InstanceType<typeof XTimeline>>();
 | 
			
		||||
 | 
			
		||||
if (props.column.listId == null) {
 | 
			
		||||
	setList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function setList() {
 | 
			
		||||
	const lists = await os.api('users/lists/list');
 | 
			
		||||
	const { canceled, result: list } = await os.select({
 | 
			
		||||
		title: i18n.ts.selectList,
 | 
			
		||||
		items: lists.map(x => ({
 | 
			
		||||
			value: x, text: x.name
 | 
			
		||||
		})),
 | 
			
		||||
		default: props.column.listId
 | 
			
		||||
	});
 | 
			
		||||
	if (canceled) return;
 | 
			
		||||
	updateColumn(props.column.id, {
 | 
			
		||||
		listId: list.id
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
function focus() {
 | 
			
		||||
	timeline.focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XTimeline,
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		mediaOnly() {
 | 
			
		||||
			(this.$refs.timeline as any).reload();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.column.listId == null) {
 | 
			
		||||
			this.setList();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		async setList() {
 | 
			
		||||
			const lists = await os.api('users/lists/list');
 | 
			
		||||
			const { canceled, result: list } = await os.select({
 | 
			
		||||
				title: this.$ts.selectList,
 | 
			
		||||
				items: lists.map(x => ({
 | 
			
		||||
					value: x, text: x.name
 | 
			
		||||
				})),
 | 
			
		||||
				default: this.column.listId
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) return;
 | 
			
		||||
			updateColumn(this.column.id, {
 | 
			
		||||
				listId: list.id
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.timeline as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header>
 | 
			
		||||
		<template v-if="pageInfo">
 | 
			
		||||
			<i :class="pageInfo.icon"></i>
 | 
			
		||||
| 
						 | 
				
			
			@ -20,72 +20,59 @@
 | 
			
		|||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XNotes from '@/components/notes.vue';
 | 
			
		||||
import { deckStore } from '@/ui/deck/deck-store';
 | 
			
		||||
import { deckStore, Column } from '@/ui/deck/deck-store';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import * as symbols from '@/symbols';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
import { router } from '@/router';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XNotes
 | 
			
		||||
	},
 | 
			
		||||
defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			deckStore,
 | 
			
		||||
			pageInfo: null,
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
let pageInfo = $ref<Record<string, any> | null>(null);
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		changePage(page) {
 | 
			
		||||
			if (page == null) return;
 | 
			
		||||
			if (page[symbols.PAGE_INFO]) {
 | 
			
		||||
				this.pageInfo = page[symbols.PAGE_INFO];
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		back() {
 | 
			
		||||
			history.back();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onContextmenu(ev: MouseEvent) {
 | 
			
		||||
			const isLink = (el: HTMLElement) => {
 | 
			
		||||
				if (el.tagName === 'A') return true;
 | 
			
		||||
				if (el.parentElement) {
 | 
			
		||||
					return isLink(el.parentElement);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			if (isLink(ev.target)) return;
 | 
			
		||||
			if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
 | 
			
		||||
			if (window.getSelection().toString() !== '') return;
 | 
			
		||||
			const path = this.$route.path;
 | 
			
		||||
			os.contextMenu([{
 | 
			
		||||
				type: 'label',
 | 
			
		||||
				text: path,
 | 
			
		||||
			}, {
 | 
			
		||||
				icon: 'fas fa-window-maximize',
 | 
			
		||||
				text: this.$ts.openInWindow,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					os.pageWindow(path);
 | 
			
		||||
				}
 | 
			
		||||
			}], ev);
 | 
			
		||||
		},
 | 
			
		||||
function changePage(page) {
 | 
			
		||||
	if (page == null) return;
 | 
			
		||||
	if (page[symbols.PAGE_INFO]) {
 | 
			
		||||
		pageInfo = page[symbols.PAGE_INFO];
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
function back() {
 | 
			
		||||
	history.back();
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
function onContextmenu(ev: MouseEvent) {
 | 
			
		||||
	if (!ev.target) return;
 | 
			
		||||
 | 
			
		||||
	const isLink = (el: HTMLElement) => {
 | 
			
		||||
		if (el.tagName === 'A') return true;
 | 
			
		||||
		if (el.parentElement) {
 | 
			
		||||
			return isLink(el.parentElement);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	if (isLink(ev.target as HTMLElement)) return;
 | 
			
		||||
	if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes((ev.target as HTMLElement).tagName) || (ev.target as HTMLElement).attributes['contenteditable']) return;
 | 
			
		||||
	if (window.getSelection()?.toString() !== '') return;
 | 
			
		||||
	const path = router.currentRoute.value.path;
 | 
			
		||||
	os.contextMenu([{
 | 
			
		||||
		type: 'label',
 | 
			
		||||
		text: path,
 | 
			
		||||
	}, {
 | 
			
		||||
		icon: 'fas fa-window-maximize',
 | 
			
		||||
		text: i18n.ts.openInWindow,
 | 
			
		||||
		action: () => {
 | 
			
		||||
			os.pageWindow(path);
 | 
			
		||||
		}
 | 
			
		||||
	}], ev);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header><i class="fas fa-at" style="margin-right: 8px;"></i>{{ column.name }}</template>
 | 
			
		||||
 | 
			
		||||
	<XNotes :pagination="pagination"/>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,13 +10,17 @@
 | 
			
		|||
import { } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XNotes from '@/components/notes.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Column } from './deck-store';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Record<string, unknown>; // TODO
 | 
			
		||||
defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const pagination = {
 | 
			
		||||
	endpoint: 'notes/mentions' as const,
 | 
			
		||||
	limit: 10,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,53 +1,38 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked" :func="{ handler: func, title: $ts.notificationSetting }">
 | 
			
		||||
<XColumn :column="column" :is-stacked="isStacked" :func="{ handler: func, title: $ts.notificationSetting }" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header><i class="fas fa-bell" style="margin-right: 8px;"></i>{{ column.name }}</template>
 | 
			
		||||
 | 
			
		||||
	<XNotifications :include-types="column.includingTypes"/>
 | 
			
		||||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XNotifications from '@/components/notifications.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { updateColumn } from './deck-store';
 | 
			
		||||
import { Column } from './deck-store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XNotifications
 | 
			
		||||
	},
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
function func() {
 | 
			
		||||
	os.popup(import('@/components/notification-setting-window.vue'), {
 | 
			
		||||
		includingTypes: props.column.includingTypes,
 | 
			
		||||
	}, {
 | 
			
		||||
		done: async (res) => {
 | 
			
		||||
			const { includingTypes } = res;
 | 
			
		||||
			updateColumn(props.column.id, {
 | 
			
		||||
				includingTypes: includingTypes
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			os.popup(import('@/components/notification-setting-window.vue'), {
 | 
			
		||||
				includingTypes: this.column.includingTypes,
 | 
			
		||||
			}, {
 | 
			
		||||
				done: async (res) => {
 | 
			
		||||
					const { includingTypes } = res;
 | 
			
		||||
					updateColumn(this.column.id, {
 | 
			
		||||
						includingTypes: includingTypes
 | 
			
		||||
					});
 | 
			
		||||
				},
 | 
			
		||||
			}, 'closed');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
	}, 'closed');
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :func="{ handler: setType, title: $ts.timeline }" :column="column" :is-stacked="isStacked" :indicated="indicated" @change-active-state="onChangeActiveState">
 | 
			
		||||
<XColumn :func="{ handler: setType, title: $ts.timeline }" :column="column" :is-stacked="isStacked" :indicated="indicated" @change-active-state="onChangeActiveState" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header>
 | 
			
		||||
		<i v-if="column.tl === 'home'" class="fas fa-home"></i>
 | 
			
		||||
		<i v-else-if="column.tl === 'local'" class="fas fa-comments"></i>
 | 
			
		||||
| 
						 | 
				
			
			@ -15,108 +15,103 @@
 | 
			
		|||
		</p>
 | 
			
		||||
		<p class="desc">{{ $t('disabled-timeline.description') }}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<XTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => $emit('loaded')" @queue="queueUpdated" @note="onNote"/>
 | 
			
		||||
	<XTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')" @queue="queueUpdated" @note="onNote"/>
 | 
			
		||||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted } from 'vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import XTimeline from '@/components/timeline.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { removeColumn, updateColumn } from './deck-store';
 | 
			
		||||
import { removeColumn, updateColumn, Column } from './deck-store';
 | 
			
		||||
import { $i } from '@/account';
 | 
			
		||||
import { instance } from '@/instance';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XTimeline,
 | 
			
		||||
	},
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'loaded'): void;
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
let disabled = $ref(false);
 | 
			
		||||
let indicated = $ref(false);
 | 
			
		||||
let columnActive = $ref(true);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	if (props.column.tl == null) {
 | 
			
		||||
		setType();
 | 
			
		||||
	} else if ($i) {
 | 
			
		||||
		disabled = !$i.isModerator && !$i.isAdmin && (
 | 
			
		||||
			instance.disableLocalTimeline && ['local', 'social'].includes(props.column.tl) ||
 | 
			
		||||
			instance.disableGlobalTimeline && ['global'].includes(props.column.tl));
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function setType() {
 | 
			
		||||
	const { canceled, result: src } = await os.select({
 | 
			
		||||
		title: i18n.ts.timeline,
 | 
			
		||||
		items: [{
 | 
			
		||||
			value: 'home' as const, text: i18n.ts._timelines.home
 | 
			
		||||
		}, {
 | 
			
		||||
			value: 'local' as const, text: i18n.ts._timelines.local
 | 
			
		||||
		}, {
 | 
			
		||||
			value: 'social' as const, text: i18n.ts._timelines.social
 | 
			
		||||
		}, {
 | 
			
		||||
			value: 'global' as const, text: i18n.ts._timelines.global
 | 
			
		||||
		}],
 | 
			
		||||
	});
 | 
			
		||||
	if (canceled) {
 | 
			
		||||
		if (props.column.tl == null) {
 | 
			
		||||
			removeColumn(props.column.id);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	updateColumn(props.column.id, {
 | 
			
		||||
		tl: src
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			disabled: false,
 | 
			
		||||
			indicated: false,
 | 
			
		||||
			columnActive: true,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
function queueUpdated(q) {
 | 
			
		||||
	if (columnActive) {
 | 
			
		||||
		indicated = q !== 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onNote() {
 | 
			
		||||
	if (!columnActive) {
 | 
			
		||||
		indicated = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onChangeActiveState(state) {
 | 
			
		||||
	columnActive = state;
 | 
			
		||||
 | 
			
		||||
	if (columnActive) {
 | 
			
		||||
		indicated = false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	watch: {
 | 
			
		||||
		mediaOnly() {
 | 
			
		||||
			(this.$refs.timeline as any).reload();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.column.tl == null) {
 | 
			
		||||
			this.setType();
 | 
			
		||||
		} else {
 | 
			
		||||
			this.disabled = !this.$i.isModerator && !this.$i.isAdmin && (
 | 
			
		||||
				this.$instance.disableLocalTimeline && ['local', 'social'].includes(this.column.tl) ||
 | 
			
		||||
				this.$instance.disableGlobalTimeline && ['global'].includes(this.column.tl));
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		async setType() {
 | 
			
		||||
			const { canceled, result: src } = await os.select({
 | 
			
		||||
				title: this.$ts.timeline,
 | 
			
		||||
				items: [{
 | 
			
		||||
					value: 'home', text: this.$ts._timelines.home
 | 
			
		||||
				}, {
 | 
			
		||||
					value: 'local', text: this.$ts._timelines.local
 | 
			
		||||
				}, {
 | 
			
		||||
					value: 'social', text: this.$ts._timelines.social
 | 
			
		||||
				}, {
 | 
			
		||||
					value: 'global', text: this.$ts._timelines.global
 | 
			
		||||
				}]
 | 
			
		||||
			});
 | 
			
		||||
			if (canceled) {
 | 
			
		||||
				if (this.column.tl == null) {
 | 
			
		||||
					removeColumn(this.column.id);
 | 
			
		||||
				}
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			updateColumn(this.column.id, {
 | 
			
		||||
				tl: src
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		queueUpdated(q) {
 | 
			
		||||
			if (this.columnActive) {
 | 
			
		||||
				this.indicated = q !== 0;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNote() {
 | 
			
		||||
			if (!this.columnActive) {
 | 
			
		||||
				this.indicated = true;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeActiveState(state) {
 | 
			
		||||
			this.columnActive = state;
 | 
			
		||||
 | 
			
		||||
			if (this.columnActive) {
 | 
			
		||||
				this.indicated = false;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.timeline as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,64 +1,49 @@
 | 
			
		|||
<template>
 | 
			
		||||
<XColumn :func="{ handler: func, title: $ts.editWidgets }" :naked="true" :column="column" :is-stacked="isStacked">
 | 
			
		||||
<XColumn :func="{ handler: func, title: $ts.editWidgets }" :naked="true" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
 | 
			
		||||
	<template #header><i class="fas fa-window-maximize" style="margin-right: 8px;"></i>{{ column.name }}</template>
 | 
			
		||||
 | 
			
		||||
	<div class="wtdtxvec">
 | 
			
		||||
		<XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
 | 
			
		||||
		<XWidgets v-if="column.widgets" :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</XColumn>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, defineAsyncComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { } from 'vue';
 | 
			
		||||
import XWidgets from '@/components/widgets.vue';
 | 
			
		||||
import XColumn from './column.vue';
 | 
			
		||||
import { addColumnWidget, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store';
 | 
			
		||||
import { addColumnWidget, Column, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XWidgets,
 | 
			
		||||
	},
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	column: Column;
 | 
			
		||||
	isStacked: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			edit: false,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
let edit = $ref(false);
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		addWidget(widget) {
 | 
			
		||||
			addColumnWidget(this.column.id, widget);
 | 
			
		||||
		},
 | 
			
		||||
function addWidget(widget) {
 | 
			
		||||
	addColumnWidget(props.column.id, widget);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		removeWidget(widget) {
 | 
			
		||||
			removeColumnWidget(this.column.id, widget);
 | 
			
		||||
		},
 | 
			
		||||
function removeWidget(widget) {
 | 
			
		||||
	removeColumnWidget(props.column.id, widget);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		updateWidget({ id, data }) {
 | 
			
		||||
			updateColumnWidget(this.column.id, id, data);
 | 
			
		||||
		},
 | 
			
		||||
function updateWidget({ id, data }) {
 | 
			
		||||
	updateColumnWidget(props.column.id, id, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		updateWidgets(widgets) {
 | 
			
		||||
			setColumnWidgets(this.column.id, widgets);
 | 
			
		||||
		},
 | 
			
		||||
function updateWidgets(widgets) {
 | 
			
		||||
	setColumnWidgets(props.column.id, widgets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		func() {
 | 
			
		||||
			this.edit = !this.edit;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
function func() {
 | 
			
		||||
	edit = !edit;
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,58 +1,50 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="efzpzdvf">
 | 
			
		||||
	<XWidgets :edit="editMode" :widgets="$store.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 | 
			
		||||
	<XWidgets :edit="editMode" :widgets="defaultStore.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 | 
			
		||||
 | 
			
		||||
	<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="fas fa-check"></i> {{ $ts.editWidgetsExit }}</button>
 | 
			
		||||
	<button v-else class="_textButton" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ $ts.editWidgets }}</button>
 | 
			
		||||
	<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="fas fa-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
 | 
			
		||||
	<button v-else class="_textButton" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ i18n.ts.editWidgets }}</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, defineAsyncComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted } from 'vue';
 | 
			
		||||
import XWidgets from '@/components/widgets.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
import { defaultStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XWidgets
 | 
			
		||||
	},
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
	(e: 'mounted', el: Element): void;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
	emits: ['mounted'],
 | 
			
		||||
let editMode = $ref(false);
 | 
			
		||||
let rootEl = $ref<HTMLDivElement>();
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			editMode: false,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$emit('mounted', this.$el);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		addWidget(widget) {
 | 
			
		||||
			this.$store.set('widgets', [{
 | 
			
		||||
				...widget,
 | 
			
		||||
				place: null,
 | 
			
		||||
			}, ...this.$store.state.widgets]);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeWidget(widget) {
 | 
			
		||||
			this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id != widget.id));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		updateWidget({ id, data }) {
 | 
			
		||||
			this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? {
 | 
			
		||||
				...w,
 | 
			
		||||
				data: data
 | 
			
		||||
			} : w));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		updateWidgets(widgets) {
 | 
			
		||||
			this.$store.set('widgets', widgets);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	emit('mounted', rootEl);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function addWidget(widget) {
 | 
			
		||||
	defaultStore.set('widgets', [{
 | 
			
		||||
		...widget,
 | 
			
		||||
		place: null,
 | 
			
		||||
	}, ...defaultStore.state.widgets]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeWidget(widget) {
 | 
			
		||||
	defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id != widget.id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateWidget({ id, data }) {
 | 
			
		||||
	defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
 | 
			
		||||
		...w,
 | 
			
		||||
		data: data
 | 
			
		||||
	} : w));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateWidgets(widgets) {
 | 
			
		||||
	defaultStore.set('widgets', widgets);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue