From ae3abc2126409da909edc1134a057fa13d819ed8 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 1 Feb 2022 23:48:19 +0900 Subject: [PATCH] fix: Fix Sideview (#8235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #7890 * a- * 3度目の正直 * fix * :v: * update CHANGELOG Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --- CHANGELOG.md | 1 + packages/client/src/components/global/a.vue | 5 +- packages/client/src/ui/classic.side.vue | 162 ++++++------- packages/client/src/ui/universal.vue | 250 +++++++++----------- 4 files changed, 194 insertions(+), 224 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1725c7703b..a0eef5a6f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - 投稿フォームのハッシュタグ保持フィールドが動作しない問題を修正 - Add `img-src` and `media-src` directives to `Content-Security-Policy` for files and media proxy +- サイドビューが動かないのを修正 - ensure that specified users does not get duplicates ## 12.102.1 (2022/01/27) diff --git a/packages/client/src/components/global/a.vue b/packages/client/src/components/global/a.vue index b1b6a0cdaf..52fef50f9b 100644 --- a/packages/client/src/components/global/a.vue +++ b/packages/client/src/components/global/a.vue @@ -23,8 +23,9 @@ const props = withDefaults(defineProps<{ behavior: null, }); -const navHook = inject('navHook', null); -const sideViewHook = inject('sideViewHook', null); +type Navigate = (path: string, record?: boolean) => void; +const navHook = inject<null | Navigate>('navHook', null); +const sideViewHook = inject<null | Navigate>('sideViewHook', null); const active = $computed(() => { if (props.activeClass == null) return false; diff --git a/packages/client/src/ui/classic.side.vue b/packages/client/src/ui/classic.side.vue index f816834141..6c2329194e 100644 --- a/packages/client/src/ui/classic.side.vue +++ b/packages/client/src/ui/classic.side.vue @@ -4,7 +4,7 @@ <header class="header" @contextmenu.prevent.stop="onContextmenu"> <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-chevron-left"></i></button> <button v-else class="_button" style="pointer-events: none;"><!-- マージンのバランスを取るためのダミー --></button> - <span class="title">{{ pageInfo.title }}</span> + <span class="title" v-text="pageInfo?.title" /> <button class="_button" @click="close()"><i class="fas fa-times"></i></button> </header> <MkHeader class="pageHeader" :info="pageInfo"/> @@ -13,99 +13,89 @@ </div> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { provide } from 'vue'; import * as os from '@/os'; import copyToClipboard from '@/scripts/copy-to-clipboard'; -import { resolve } from '@/router'; -import { url } from '@/config'; +import { resolve, router } from '@/router'; +import { url as root } from '@/config'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - provide() { - return { - navHook: (path) => { - this.navigate(path); - } - }; - }, +provide('navHook', navigate); - data() { - return { - path: null, - component: null, - props: {}, - pageInfo: null, - history: [], - }; - }, +let path: string | null = $ref(null); +let component: ReturnType<typeof resolve>['component'] | null = $ref(null); +let props: any | null = $ref(null); +let pageInfo: any | null = $ref(null); +let history: string[] = $ref([]); - computed: { - url(): string { - return url + this.path; - } - }, +let url = $computed(() => `${root}${path}`); - methods: { - changePage(page) { - if (page == null) return; - if (page[symbols.PAGE_INFO]) { - this.pageInfo = page[symbols.PAGE_INFO]; - } - }, - - navigate(path, record = true) { - if (record && this.path) this.history.push(this.path); - this.path = path; - const { component, props } = resolve(path); - this.component = component; - this.props = props; - }, - - back() { - this.navigate(this.history.pop(), false); - }, - - close() { - this.path = null; - this.component = null; - this.props = {}; - }, - - onContextmenu(ev: MouseEvent) { - os.contextMenu([{ - type: 'label', - text: this.path, - }, { - icon: 'fas fa-expand-alt', - text: this.$ts.showInPage, - action: () => { - this.$router.push(this.path); - this.close(); - } - }, { - icon: 'fas fa-window-maximize', - text: this.$ts.openInWindow, - action: () => { - os.pageWindow(this.path); - this.close(); - } - }, null, { - icon: 'fas fa-external-link-alt', - text: this.$ts.openInNewTab, - action: () => { - window.open(this.url, '_blank'); - this.close(); - } - }, { - icon: 'fas fa-link', - text: this.$ts.copyLink, - action: () => { - copyToClipboard(this.url); - } - }], ev); - } +function changePage(page) { + if (page == null) return; + if (page[symbols.PAGE_INFO]) { + pageInfo = page[symbols.PAGE_INFO]; } +} + +function navigate(_path: string, record = true) { + if (record && path) history.push($$(path).value); + path = _path; + const resolved = resolve(path); + component = resolved.component; + props = resolved.props; +} + +function back() { + const prev = history.pop(); + if (prev) navigate(prev, false); +} + +function close() { + path = null; + component = null; + props = {}; +} + +function onContextmenu(ev: MouseEvent) { + os.contextMenu([{ + type: 'label', + text: path || '', + }, { + icon: 'fas fa-expand-alt', + text: i18n.ts.showInPage, + action: () => { + if (path) router.push(path); + close(); + } + }, { + icon: 'fas fa-window-maximize', + text: i18n.ts.openInWindow, + action: () => { + if (path) os.pageWindow(path); + close(); + } + }, null, { + icon: 'fas fa-external-link-alt', + text: i18n.ts.openInNewTab, + action: () => { + window.open(url, '_blank'); + close(); + } + }, { + icon: 'fas fa-link', + text: i18n.ts.copyLink, + action: () => { + copyToClipboard(url); + } + }], ev); +} + +defineExpose({ + navigate, + back, + close, }); </script> diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue index 8fe9dcffaf..b0dfc5aadc 100644 --- a/packages/client/src/ui/universal.vue +++ b/packages/client/src/ui/universal.vue @@ -20,7 +20,7 @@ </main> </div> - <XSideView v-if="isDesktop" ref="side" class="side"/> + <XSideView v-if="isDesktop" ref="sideEl" class="side"/> <div v-if="isDesktop" ref="widgetsEl" class="widgets"> <XWidgets @mounted="attachSticky"/> @@ -31,9 +31,9 @@ <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="$route.name === 'index' ? top() : $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 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 widget _button" @click="widgetsShowing = true"><i class="fas fa-layer-group"></i></button> - <button class="button post _button" @click="post()"><i class="fas fa-pencil-alt"></i></button> + <button class="button post _button" @click="os.post()"><i class="fas fa-pencil-alt"></i></button> </div> <transition :name="$store.state.animation ? 'menuDrawer-back' : ''"> @@ -64,155 +64,133 @@ </div> </template> -<script lang="ts"> -import { defineComponent, defineAsyncComponent, provide, onMounted, computed, ref, watch } from 'vue'; +<script lang="ts" setup> +import { defineAsyncComponent, provide, onMounted, computed, ref, watch } from 'vue'; import { instanceName } from '@/config'; import { StickySidebar } from '@/scripts/sticky-sidebar'; -import XSidebar from '@/ui/_common_/sidebar.vue'; import XDrawerMenu from '@/ui/_common_/sidebar-for-mobile.vue'; import XCommon from './_common_/common.vue'; import XSideView from './classic.side.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { defaultStore } from '@/store'; -import * as EventEmitter from 'eventemitter3'; import { menuDef } from '@/menu'; import { useRoute } from 'vue-router'; import { i18n } from '@/i18n'; +import { $i } from '@/account'; +const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); +const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/sidebar.vue')); const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; -export default defineComponent({ - components: { - XCommon, - XSidebar, - XDrawerMenu, - XWidgets: defineAsyncComponent(() => import('./universal.widgets.vue')), - XSideView, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる - }, - - setup() { - const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); - const isMobile = ref(window.innerWidth <= MOBILE_THRESHOLD); - window.addEventListener('resize', () => { - isMobile.value = window.innerWidth <= MOBILE_THRESHOLD; - }); - - const pageInfo = ref(); - const widgetsEl = ref<HTMLElement>(); - const widgetsShowing = ref(false); - - const sideViewController = new EventEmitter(); - - provide('sideViewHook', isDesktop.value ? (url) => { - sideViewController.emit('navigate', url); - } : null); - - const menuIndicated = computed(() => { - for (const def in menuDef) { - if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから - if (menuDef[def].indicated) return true; - } - return false; - }); - - const drawerMenuShowing = ref(false); - - const route = useRoute(); - watch(route, () => { - drawerMenuShowing.value = false; - }); - - document.documentElement.style.overflowY = 'scroll'; - - if (defaultStore.state.widgets.length === 0) { - defaultStore.set('widgets', [{ - name: 'calendar', - id: 'a', place: 'right', data: {} - }, { - name: 'notifications', - id: 'b', place: 'right', data: {} - }, { - name: 'trends', - id: 'c', place: 'right', data: {} - }]); - } - - onMounted(() => { - if (!isDesktop.value) { - window.addEventListener('resize', () => { - if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; - }, { passive: true }); - } - }); - - const changePage = (page) => { - if (page == null) return; - if (page[symbols.PAGE_INFO]) { - pageInfo.value = page[symbols.PAGE_INFO]; - document.title = `${pageInfo.value.title} | ${instanceName}`; - } - }; - - const onContextmenu = (ev) => { - 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 = route.path; - os.contextMenu([{ - type: 'label', - text: path, - }, { - icon: 'fas fa-columns', - text: i18n.ts.openInSideView, - action: () => { - this.$refs.side.navigate(path); - } - }, { - icon: 'fas fa-window-maximize', - text: i18n.ts.openInWindow, - action: () => { - os.pageWindow(path); - } - }], ev); - }; - - const attachSticky = (el) => { - const sticky = new StickySidebar(widgetsEl.value); - window.addEventListener('scroll', () => { - sticky.calc(window.scrollY); - }, { passive: true }); - }; - - return { - pageInfo, - isDesktop, - isMobile, - widgetsEl, - widgetsShowing, - drawerMenuShowing, - menuIndicated, - wallpaper: localStorage.getItem('wallpaper') != null, - changePage, - top: () => { - window.scroll({ top: 0, behavior: 'smooth' }); - }, - onTransition: () => { - if (window._scroll) window._scroll(); - }, - post: os.post, - onContextmenu, - attachSticky, - }; - }, +const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); +const isMobile = ref(window.innerWidth <= MOBILE_THRESHOLD); +window.addEventListener('resize', () => { + isMobile.value = window.innerWidth <= MOBILE_THRESHOLD; }); + +const pageInfo = ref(); +const widgetsEl = $ref<HTMLElement>(); +const widgetsShowing = ref(false); + +let sideEl = $ref<InstanceType<typeof XSideView>>(); + +provide('sideViewHook', isDesktop.value ? (url) => { + sideEl.navigate(url); +} : null); + +const menuIndicated = computed(() => { + for (const def in menuDef) { + if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから + if (menuDef[def].indicated) return true; + } + return false; +}); + +const drawerMenuShowing = ref(false); + +const route = useRoute(); +watch(route, () => { + drawerMenuShowing.value = false; +}); + +document.documentElement.style.overflowY = 'scroll'; + +if (defaultStore.state.widgets.length === 0) { + defaultStore.set('widgets', [{ + name: 'calendar', + id: 'a', place: 'right', data: {} + }, { + name: 'notifications', + id: 'b', place: 'right', data: {} + }, { + name: 'trends', + id: 'c', place: 'right', data: {} + }]); +} + +onMounted(() => { + if (!isDesktop.value) { + window.addEventListener('resize', () => { + if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; + }, { passive: true }); + } +}); + +const changePage = (page) => { + if (page == null) return; + if (page[symbols.PAGE_INFO]) { + pageInfo.value = page[symbols.PAGE_INFO]; + document.title = `${pageInfo.value.title} | ${instanceName}`; + } +}; + +const onContextmenu = (ev) => { + 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 = route.path; + os.contextMenu([{ + type: 'label', + text: path, + }, { + icon: 'fas fa-columns', + text: i18n.ts.openInSideView, + action: () => { + sideEl.navigate(path); + } + }, { + icon: 'fas fa-window-maximize', + text: i18n.ts.openInWindow, + action: () => { + os.pageWindow(path); + } + }], ev); +}; + +const attachSticky = (el) => { + const sticky = new StickySidebar(widgetsEl); + window.addEventListener('scroll', () => { + sticky.calc(window.scrollY); + }, { passive: true }); +}; + +function top() { + window.scroll({ top: 0, behavior: 'smooth' }); +} + +function onTransition() { + if (window._scroll) window._scroll(); +} + +const wallpaper = localStorage.getItem('wallpaper') != null; </script> <style lang="scss" scoped>