mirror of
https://github.com/h3poteto/whalebird-desktop
synced 2025-01-29 16:49:24 +01:00
[refactor] Public store
This commit is contained in:
parent
3b667ccaee
commit
55629be421
@ -1,235 +0,0 @@
|
|||||||
import { Entity } from 'megalodon'
|
|
||||||
import Public, { PublicState, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Public'
|
|
||||||
|
|
||||||
const account: Entity.Account = {
|
|
||||||
id: '1',
|
|
||||||
username: 'h3poteto',
|
|
||||||
acct: 'h3poteto@pleroma.io',
|
|
||||||
display_name: 'h3poteto',
|
|
||||||
locked: false,
|
|
||||||
created_at: '2019-03-26T21:30:32',
|
|
||||||
followers_count: 10,
|
|
||||||
following_count: 10,
|
|
||||||
statuses_count: 100,
|
|
||||||
note: 'engineer',
|
|
||||||
url: 'https://pleroma.io',
|
|
||||||
avatar: '',
|
|
||||||
avatar_static: '',
|
|
||||||
header: '',
|
|
||||||
header_static: '',
|
|
||||||
emojis: [],
|
|
||||||
moved: null,
|
|
||||||
fields: null,
|
|
||||||
bot: false
|
|
||||||
}
|
|
||||||
const status1: Entity.Status = {
|
|
||||||
id: '1',
|
|
||||||
uri: 'http://example.com',
|
|
||||||
url: 'http://example.com',
|
|
||||||
account: account,
|
|
||||||
in_reply_to_id: null,
|
|
||||||
in_reply_to_account_id: null,
|
|
||||||
reblog: null,
|
|
||||||
content: 'hoge',
|
|
||||||
plain_content: 'hoge',
|
|
||||||
created_at: '2019-03-26T21:40:32',
|
|
||||||
emojis: [],
|
|
||||||
replies_count: 0,
|
|
||||||
reblogs_count: 0,
|
|
||||||
favourites_count: 0,
|
|
||||||
reblogged: null,
|
|
||||||
favourited: null,
|
|
||||||
muted: null,
|
|
||||||
sensitive: false,
|
|
||||||
spoiler_text: '',
|
|
||||||
visibility: 'public',
|
|
||||||
media_attachments: [],
|
|
||||||
mentions: [],
|
|
||||||
tags: [],
|
|
||||||
card: null,
|
|
||||||
poll: null,
|
|
||||||
application: {
|
|
||||||
name: 'Web'
|
|
||||||
} as Entity.Application,
|
|
||||||
language: null,
|
|
||||||
pinned: null,
|
|
||||||
emoji_reactions: [],
|
|
||||||
bookmarked: false,
|
|
||||||
quote: false
|
|
||||||
}
|
|
||||||
const status2: Entity.Status = {
|
|
||||||
id: '2',
|
|
||||||
uri: 'http://example.com',
|
|
||||||
url: 'http://example.com',
|
|
||||||
account: account,
|
|
||||||
in_reply_to_id: null,
|
|
||||||
in_reply_to_account_id: null,
|
|
||||||
reblog: null,
|
|
||||||
content: 'fuga',
|
|
||||||
plain_content: 'fuga',
|
|
||||||
created_at: '2019-03-26T21:40:32',
|
|
||||||
emojis: [],
|
|
||||||
replies_count: 0,
|
|
||||||
reblogs_count: 0,
|
|
||||||
favourites_count: 0,
|
|
||||||
reblogged: null,
|
|
||||||
favourited: null,
|
|
||||||
muted: null,
|
|
||||||
sensitive: false,
|
|
||||||
spoiler_text: '',
|
|
||||||
visibility: 'public',
|
|
||||||
media_attachments: [],
|
|
||||||
mentions: [],
|
|
||||||
tags: [],
|
|
||||||
card: null,
|
|
||||||
poll: null,
|
|
||||||
application: {
|
|
||||||
name: 'Web'
|
|
||||||
} as Entity.Application,
|
|
||||||
language: null,
|
|
||||||
pinned: null,
|
|
||||||
emoji_reactions: [],
|
|
||||||
bookmarked: false,
|
|
||||||
quote: false
|
|
||||||
}
|
|
||||||
|
|
||||||
const rebloggedStatus: Entity.Status = {
|
|
||||||
id: '3',
|
|
||||||
uri: 'http://example.com',
|
|
||||||
url: 'http://example.com',
|
|
||||||
account: account,
|
|
||||||
in_reply_to_id: null,
|
|
||||||
in_reply_to_account_id: null,
|
|
||||||
reblog: status1,
|
|
||||||
content: '',
|
|
||||||
plain_content: null,
|
|
||||||
created_at: '2019-03-31T21:40:32',
|
|
||||||
emojis: [],
|
|
||||||
replies_count: 0,
|
|
||||||
reblogs_count: 0,
|
|
||||||
favourites_count: 0,
|
|
||||||
reblogged: null,
|
|
||||||
favourited: null,
|
|
||||||
muted: null,
|
|
||||||
sensitive: false,
|
|
||||||
spoiler_text: '',
|
|
||||||
visibility: 'public',
|
|
||||||
media_attachments: [],
|
|
||||||
mentions: [],
|
|
||||||
tags: [],
|
|
||||||
card: null,
|
|
||||||
poll: null,
|
|
||||||
application: {
|
|
||||||
name: 'Web'
|
|
||||||
} as Entity.Application,
|
|
||||||
language: null,
|
|
||||||
pinned: null,
|
|
||||||
emoji_reactions: [],
|
|
||||||
bookmarked: false,
|
|
||||||
quote: false
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('TimelineSpace/Contents/Local', () => {
|
|
||||||
describe('mutations', () => {
|
|
||||||
let state: PublicState
|
|
||||||
|
|
||||||
describe('deleteToot', () => {
|
|
||||||
describe('message is not reblogged', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: true,
|
|
||||||
timeline: [status2, status1],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should be deleted', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.DELETE_TOOT](state, status1.id)
|
|
||||||
expect(state.timeline).toEqual([status2])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('message is reblogged', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: true,
|
|
||||||
timeline: [status2, rebloggedStatus],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should be deleted', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.DELETE_TOOT](state, status1.id)
|
|
||||||
expect(state.timeline).toEqual([status2])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('appendTimeline', () => {
|
|
||||||
describe('heading', () => {
|
|
||||||
describe('normal', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: true,
|
|
||||||
timeline: [status2, status1],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should be updated timeline', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.APPEND_TIMELINE](state, rebloggedStatus)
|
|
||||||
expect(state.timeline).toEqual([rebloggedStatus, status2, status1])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('duplicated status', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: true,
|
|
||||||
timeline: [rebloggedStatus, status2, status1],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should not be updated timeline', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.APPEND_TIMELINE](state, rebloggedStatus)
|
|
||||||
expect(state.timeline).toEqual([rebloggedStatus, status2, status1])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('not heading', () => {
|
|
||||||
describe('normal', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: false,
|
|
||||||
timeline: [status2, status1],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should not be updated timeline', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.APPEND_TIMELINE](state, rebloggedStatus)
|
|
||||||
expect(state.timeline).toEqual([status2, status1])
|
|
||||||
expect(state.unreads).toEqual([rebloggedStatus])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('duplicated status', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
state = {
|
|
||||||
lazyLoading: false,
|
|
||||||
heading: false,
|
|
||||||
timeline: [rebloggedStatus, status2, status1],
|
|
||||||
unreads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('should not be updated timeline', () => {
|
|
||||||
Public.mutations![MUTATION_TYPES.APPEND_TIMELINE](state, rebloggedStatus)
|
|
||||||
expect(state.timeline).toEqual([rebloggedStatus, status2, status1])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -137,6 +137,7 @@ export default defineComponent({
|
|||||||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||||
!lazyLoading.value
|
!lazyLoading.value
|
||||||
) {
|
) {
|
||||||
|
lazyLoading.value = true
|
||||||
store
|
store
|
||||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
||||||
lastStatus: timeline.value[timeline.value.length - 1],
|
lastStatus: timeline.value[timeline.value.length - 1],
|
||||||
@ -149,6 +150,9 @@ export default defineComponent({
|
|||||||
type: 'error'
|
type: 'error'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
lazyLoading.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
||||||
heading.value = false
|
heading.value = false
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="public">
|
<div id="public">
|
||||||
<div class="unread">{{ unreads.length > 0 ? unreads.length : '' }}</div>
|
|
||||||
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
|
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
|
||||||
<template v-slot="{ item, index, active }">
|
<template v-slot="{ item, index, active }">
|
||||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||||
<toot
|
<toot
|
||||||
|
v-if="account.account && account.server"
|
||||||
:message="item"
|
:message="item"
|
||||||
:focused="item.uri + item.id === focusedId"
|
:focused="item.uri + item.id === focusedId"
|
||||||
:overlaid="modalOpened"
|
:overlaid="modalOpened"
|
||||||
:filters="filters"
|
:filters="filters"
|
||||||
|
:account="account.account"
|
||||||
|
:server="account.server"
|
||||||
v-on:update="updateToot"
|
v-on:update="updateToot"
|
||||||
v-on:delete="deleteToot"
|
v-on:delete="deleteToot"
|
||||||
@focusRight="focusSidebar"
|
@focusRight="focusSidebar"
|
||||||
@ -27,7 +29,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, defineComponent, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, ref, watch, reactive } from 'vue'
|
||||||
import { logicAnd } from '@vueuse/math'
|
import { logicAnd } from '@vueuse/math'
|
||||||
import { useMagicKeys, whenever } from '@vueuse/core'
|
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
@ -39,10 +41,12 @@ import Toot from '@/components/organisms/Toot.vue'
|
|||||||
import { EventEmitter } from '@/components/event'
|
import { EventEmitter } from '@/components/event'
|
||||||
import useReloadable from '@/components/utils/reloadable'
|
import useReloadable from '@/components/utils/reloadable'
|
||||||
import { MUTATION_TYPES as SIDE_MENU_MUTATION } from '@/store/TimelineSpace/SideMenu'
|
import { MUTATION_TYPES as SIDE_MENU_MUTATION } from '@/store/TimelineSpace/SideMenu'
|
||||||
import { MUTATION_TYPES as TIMELINE_MUTATION, ACTION_TYPES as TIMELINE_ACTION } from '@/store/TimelineSpace'
|
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||||
import { MUTATION_TYPES as HEADER_MUTATION } from '@/store/TimelineSpace/HeaderMenu'
|
import { MUTATION_TYPES as HEADER_MUTATION } from '@/store/TimelineSpace/HeaderMenu'
|
||||||
import { MUTATION_TYPES as CONTENTS_MUTATION } from '@/store/TimelineSpace/Contents'
|
|
||||||
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Public'
|
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Public'
|
||||||
|
import { LocalAccount } from '~/src/types/localAccount'
|
||||||
|
import { LocalServer } from '~/src/types/localServer'
|
||||||
|
import { MyWindow } from '~/src/types/global'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'public',
|
name: 'public',
|
||||||
@ -55,30 +59,33 @@ export default defineComponent({
|
|||||||
const { reloadable } = useReloadable(store, route, i18n)
|
const { reloadable } = useReloadable(store, route, i18n)
|
||||||
const { j, k, Ctrl_r } = useMagicKeys()
|
const { j, k, Ctrl_r } = useMagicKeys()
|
||||||
|
|
||||||
|
const win = (window as any) as MyWindow
|
||||||
|
const id = computed(() => parseInt(route.params.id as string))
|
||||||
|
|
||||||
const focusedId = ref<string | null>(null)
|
const focusedId = ref<string | null>(null)
|
||||||
const scroller = ref<any>(null)
|
const scroller = ref<any>(null)
|
||||||
|
const lazyLoading = ref(false)
|
||||||
|
const heading = ref(true)
|
||||||
|
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||||
|
account: null,
|
||||||
|
server: null
|
||||||
|
})
|
||||||
|
|
||||||
const timeline = computed(() => store.state.TimelineSpace.Contents.Public.timeline)
|
const timeline = computed(() => store.state.TimelineSpace.Contents.Public.timeline[id.value])
|
||||||
const unreads = computed(() => store.state.TimelineSpace.Contents.Public.unreads)
|
|
||||||
const lazyLoading = computed(() => store.state.TimelineSpace.Contents.Public.lazyLoading)
|
|
||||||
const heading = computed(() => store.state.TimelineSpace.Contents.Public.heading)
|
|
||||||
const openSideBar = computed(() => store.state.TimelineSpace.Contents.SideBar.openSideBar)
|
const openSideBar = computed(() => store.state.TimelineSpace.Contents.SideBar.openSideBar)
|
||||||
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
||||||
const unreadNotification = computed(() => store.state.TimelineSpace.timelineSetting.unreadNotification)
|
|
||||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||||
const filters = computed(() => store.getters[`${space}/filters`])
|
const filters = computed(() => store.getters[`${space}/filters`])
|
||||||
const currentFocusedIndex = computed(() => timeline.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
const currentFocusedIndex = computed(() => timeline.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||||
|
account.account = a
|
||||||
|
account.server = s
|
||||||
|
|
||||||
store.commit(`TimelineSpace/SideMenu/${SIDE_MENU_MUTATION.CHANGE_UNREAD_PUBLIC_TIMELINE}`, false)
|
store.commit(`TimelineSpace/SideMenu/${SIDE_MENU_MUTATION.CHANGE_UNREAD_PUBLIC_TIMELINE}`, false)
|
||||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||||
if (!unreadNotification.value.public) {
|
|
||||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
|
||||||
await initialize().finally(() => {
|
|
||||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
onBeforeUpdate(() => {
|
onBeforeUpdate(() => {
|
||||||
if (store.state.TimelineSpace.SideMenu.unreadPublicTimeline && heading.value) {
|
if (store.state.TimelineSpace.SideMenu.unreadPublicTimeline && heading.value) {
|
||||||
@ -86,18 +93,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (!unreadNotification.value.public) {
|
|
||||||
store.dispatch(`TimelineSpace/${TIMELINE_ACTION.STOP_PUBLIC_STREAMING}`)
|
|
||||||
store.dispatch(`TimelineSpace/${TIMELINE_ACTION.UNBIND_PUBLIC_STREAMING}`)
|
|
||||||
}
|
|
||||||
EventEmitter.off('focus-timeline')
|
EventEmitter.off('focus-timeline')
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CHANGE_HEADING}`, true)
|
|
||||||
store.commit(`${space}/${MUTATION_TYPES.ARCHIVE_TIMELINE}`)
|
|
||||||
if (!unreadNotification.value.public) {
|
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CLEAR_TIMELINE}`)
|
|
||||||
}
|
|
||||||
const el = document.getElementById('scroller')
|
const el = document.getElementById('scroller')
|
||||||
if (el !== undefined && el !== null) {
|
if (el !== undefined && el !== null) {
|
||||||
el.removeEventListener('scroll', onScroll)
|
el.removeEventListener('scroll', onScroll)
|
||||||
@ -114,9 +112,9 @@ export default defineComponent({
|
|||||||
|
|
||||||
watch(focusedId, (newVal, _oldVal) => {
|
watch(focusedId, (newVal, _oldVal) => {
|
||||||
if (newVal && heading.value) {
|
if (newVal && heading.value) {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CHANGE_HEADING}`, false)
|
heading.value = false
|
||||||
} else if (newVal === null && !heading.value) {
|
} else if (newVal === null && !heading.value) {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CHANGE_HEADING}`, true)
|
heading.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
whenever(logicAnd(j, shortcutEnabled), () => {
|
whenever(logicAnd(j, shortcutEnabled), () => {
|
||||||
@ -133,35 +131,34 @@ export default defineComponent({
|
|||||||
reload()
|
reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialize = async () => {
|
|
||||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_PUBLIC_TIMELINE}`).catch(_ => {
|
|
||||||
ElMessage({
|
|
||||||
message: i18n.t('message.timeline_fetch_error'),
|
|
||||||
type: 'error'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
await store.dispatch(`TimelineSpace/${TIMELINE_ACTION.BIND_PUBLIC_STREAMING}`)
|
|
||||||
store.dispatch(`TimelineSpace/${TIMELINE_ACTION.START_PUBLIC_STREAMING}`)
|
|
||||||
}
|
|
||||||
const onScroll = (event: Event) => {
|
const onScroll = (event: Event) => {
|
||||||
if (
|
if (
|
||||||
(event.target as HTMLElement)!.clientHeight + (event.target as HTMLElement)!.scrollTop >=
|
(event.target as HTMLElement)!.clientHeight + (event.target as HTMLElement)!.scrollTop >=
|
||||||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||||
!lazyLoading.value
|
!lazyLoading.value
|
||||||
) {
|
) {
|
||||||
store.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, timeline.value[timeline.value.length - 1]).catch(() => {
|
lazyLoading.value = true
|
||||||
ElMessage({
|
store
|
||||||
message: i18n.t('message.timeline_fetch_error'),
|
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
||||||
type: 'error'
|
statuses: timeline.value[timeline.value.length - 1],
|
||||||
|
account: account.account,
|
||||||
|
server: account.server
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: i18n.t('message.timeline_fetch_error'),
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
lazyLoading.value = false
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CHANGE_HEADING}`, false)
|
heading.value = false
|
||||||
} else if ((event.target as HTMLElement)!.scrollTop <= 10 && !heading.value) {
|
} else if ((event.target as HTMLElement)!.scrollTop <= 10 && !heading.value) {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.CHANGE_HEADING}`, true)
|
heading.value = true
|
||||||
store.commit(`${space}/${MUTATION_TYPES.MERGE_UNREADS}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
@ -173,10 +170,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const updateToot = (message: Entity.Status) => {
|
const updateToot = (message: Entity.Status) => {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, message)
|
if (account.account) {
|
||||||
|
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, { status: message, accountId: account.account.id })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const deleteToot = (message: Entity.Status) => {
|
const deleteToot = (message: Entity.Status) => {
|
||||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, message.id)
|
if (account.account) {
|
||||||
|
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: message.id, accountId: account.account.id })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const upper = () => {
|
const upper = () => {
|
||||||
scroller.value.scrollToItem(0)
|
scroller.value.scrollToItem(0)
|
||||||
@ -216,7 +217,7 @@ export default defineComponent({
|
|||||||
openSideBar,
|
openSideBar,
|
||||||
heading,
|
heading,
|
||||||
upper,
|
upper,
|
||||||
unreads
|
account
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -122,6 +122,7 @@ const actions: ActionTree<GlobalHeaderState, RootState> = {
|
|||||||
await dispatch('TimelineSpace/Contents/Home/fetchTimeline', { account, server }, { root: true })
|
await dispatch('TimelineSpace/Contents/Home/fetchTimeline', { account, server }, { root: true })
|
||||||
await dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', { account, server }, { root: true })
|
await dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', { account, server }, { root: true })
|
||||||
await dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', { account, server }, { root: true })
|
await dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', { account, server }, { root: true })
|
||||||
|
await dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline', { account, server }, { root: true })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[ACTION_TYPES.BIND_STREAMINGS]: async ({ commit }, req: Array<[LocalAccount, LocalServer]>) => {
|
[ACTION_TYPES.BIND_STREAMINGS]: async ({ commit }, req: Array<[LocalAccount, LocalServer]>) => {
|
||||||
@ -147,6 +148,14 @@ const actions: ActionTree<GlobalHeaderState, RootState> = {
|
|||||||
win.ipcRenderer.on(`delete-local-streamings-${account.id}`, (_, id: string) => {
|
win.ipcRenderer.on(`delete-local-streamings-${account.id}`, (_, id: string) => {
|
||||||
commit('TimelineSpace/Contents/Local/deleteToot', { statusId: id, accountId: account.id }, { root: true })
|
commit('TimelineSpace/Contents/Local/deleteToot', { statusId: id, accountId: account.id }, { root: true })
|
||||||
})
|
})
|
||||||
|
win.ipcRenderer.removeAllListeners(`update-public-streamings-${account.id}`)
|
||||||
|
win.ipcRenderer.on(`update-public-streamings-${account.id}`, (_, update: Entity.Status) => {
|
||||||
|
commit('TimelineSpace/Contents/Public/appendTimeline', { status: update, accountId: account.id }, { root: true })
|
||||||
|
})
|
||||||
|
win.ipcRenderer.removeAllListeners(`delete-public-streamings-${account.id}`)
|
||||||
|
win.ipcRenderer.on(`delete-public-streamings-${account.id}`, (_, id: string) => {
|
||||||
|
commit('TimelineSpace/Contents/Public/deleteToot', { statusId: id, accountId: account.id }, { root: true })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,6 @@ export const ACTION_TYPES = {
|
|||||||
FETCH_CONTENTS_TIMELINES: 'fetchContentsTimelines',
|
FETCH_CONTENTS_TIMELINES: 'fetchContentsTimelines',
|
||||||
CLEAR_CONTENTS_TIMELINES: 'clearContentsTimelines',
|
CLEAR_CONTENTS_TIMELINES: 'clearContentsTimelines',
|
||||||
BIND_STREAMINGS: 'bindStreamings',
|
BIND_STREAMINGS: 'bindStreamings',
|
||||||
BIND_PUBLIC_STREAMING: 'bindPublicStreaming',
|
|
||||||
BIND_DIRECT_MESSAGES_STREAMING: 'bindDirectMessagesStreaming',
|
BIND_DIRECT_MESSAGES_STREAMING: 'bindDirectMessagesStreaming',
|
||||||
UPDATE_TOOT_FOR_ALL_TIMELINES: 'updateTootForAllTimelines',
|
UPDATE_TOOT_FOR_ALL_TIMELINES: 'updateTootForAllTimelines',
|
||||||
LOAD_SETTING: 'loadSetting'
|
LOAD_SETTING: 'loadSetting'
|
||||||
@ -198,31 +197,16 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
|||||||
},
|
},
|
||||||
[ACTION_TYPES.FETCH_CONTENTS_TIMELINES]: async ({ dispatch }) => {
|
[ACTION_TYPES.FETCH_CONTENTS_TIMELINES]: async ({ dispatch }) => {
|
||||||
await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', {}, { root: true })
|
await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', {}, { root: true })
|
||||||
await dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline', {}, { root: true })
|
|
||||||
},
|
},
|
||||||
[ACTION_TYPES.CLEAR_CONTENTS_TIMELINES]: ({ commit }) => {
|
[ACTION_TYPES.CLEAR_CONTENTS_TIMELINES]: ({ commit }) => {
|
||||||
commit('TimelineSpace/Contents/DirectMessages/clearTimeline', {}, { root: true })
|
commit('TimelineSpace/Contents/DirectMessages/clearTimeline', {}, { root: true })
|
||||||
commit('TimelineSpace/Contents/Public/clearTimeline', {}, { root: true })
|
|
||||||
},
|
},
|
||||||
[ACTION_TYPES.BIND_STREAMINGS]: ({ dispatch }) => {
|
[ACTION_TYPES.BIND_STREAMINGS]: ({ dispatch }) => {
|
||||||
dispatch('bindDirectMessagesStreaming')
|
dispatch('bindDirectMessagesStreaming')
|
||||||
dispatch('bindPublicStreaming')
|
|
||||||
},
|
},
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
// Each streaming methods
|
// Each streaming methods
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
[ACTION_TYPES.BIND_PUBLIC_STREAMING]: ({ commit, rootState, state }) => {
|
|
||||||
win.ipcRenderer.on(`update-public-streamings-${state.account!.id}`, (_, update: Entity.Status) => {
|
|
||||||
commit('TimelineSpace/Contents/Public/appendTimeline', update, { root: true })
|
|
||||||
if (rootState.TimelineSpace.Contents.Public.heading && Math.random() > 0.8) {
|
|
||||||
commit('TimelineSpace/Contents/Public/archiveTimeline', {}, { root: true })
|
|
||||||
}
|
|
||||||
commit('TimelineSpace/SideMenu/changeUnreadPublicTimeline', true, { root: true })
|
|
||||||
})
|
|
||||||
win.ipcRenderer.on(`delete-public-streamings-${state.account!.id}`, (_, id: string) => {
|
|
||||||
commit('TimelineSpace/Contents/Public/deleteToot', id, { root: true })
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[ACTION_TYPES.BIND_DIRECT_MESSAGES_STREAMING]: ({ commit, rootState, state }) => {
|
[ACTION_TYPES.BIND_DIRECT_MESSAGES_STREAMING]: ({ commit, rootState, state }) => {
|
||||||
win.ipcRenderer.on(`update-direct-streamings-${state.account!.id}`, (_, update: Entity.Status) => {
|
win.ipcRenderer.on(`update-direct-streamings-${state.account!.id}`, (_, update: Entity.Status) => {
|
||||||
commit('TimelineSpace/Contents/DirectMessages/appendTimeline', update, { root: true })
|
commit('TimelineSpace/Contents/DirectMessages/appendTimeline', update, { root: true })
|
||||||
|
@ -82,6 +82,7 @@ const actions: ActionTree<LocalState, RootState> = {
|
|||||||
commit(MUTATION_TYPES.REPLACE_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
commit(MUTATION_TYPES.REPLACE_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
||||||
return res.data
|
return res.data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,70 +1,52 @@
|
|||||||
import generator, { Entity, FilterContext } from 'megalodon'
|
import generator, { Entity, FilterContext } from 'megalodon'
|
||||||
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'
|
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'
|
||||||
import { RootState } from '@/store'
|
import { RootState } from '@/store'
|
||||||
|
import { LocalAccount } from '~/src/types/localAccount'
|
||||||
|
import { LocalServer } from '~/src/types/localServer'
|
||||||
|
|
||||||
export type PublicState = {
|
export type PublicState = {
|
||||||
timeline: Array<Entity.Status>
|
timeline: { [key: number]: Array<Entity.Status> }
|
||||||
lazyLoading: boolean
|
|
||||||
heading: boolean
|
|
||||||
unreads: Array<Entity.Status>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = (): PublicState => ({
|
const state = (): PublicState => ({
|
||||||
timeline: [],
|
timeline: {}
|
||||||
lazyLoading: false,
|
|
||||||
heading: true,
|
|
||||||
unreads: []
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const MUTATION_TYPES = {
|
export const MUTATION_TYPES = {
|
||||||
CHANGE_HEADING: 'changeHeading',
|
|
||||||
APPEND_TIMELINE: 'appendTimeline',
|
APPEND_TIMELINE: 'appendTimeline',
|
||||||
UPDATE_TIMELINE: 'updateTimeline',
|
REPLACE_TIMELINE: 'replaceTimeline',
|
||||||
INSERT_TIMELINE: 'insertTimeline',
|
INSERT_TIMELINE: 'insertTimeline',
|
||||||
ARCHIVE_TIMELINE: 'archiveTimeline',
|
|
||||||
CLEAR_TIMELINE: 'clearTimeline',
|
|
||||||
UPDATE_TOOT: 'updateToot',
|
UPDATE_TOOT: 'updateToot',
|
||||||
DELETE_TOOT: 'deleteToot',
|
DELETE_TOOT: 'deleteToot'
|
||||||
CHANGE_LAZY_LOADING: 'changeLazyLoading',
|
|
||||||
MERGE_UNREADS: 'mergeUnreads'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutations: MutationTree<PublicState> = {
|
const mutations: MutationTree<PublicState> = {
|
||||||
[MUTATION_TYPES.CHANGE_HEADING]: (state, value: boolean) => {
|
[MUTATION_TYPES.APPEND_TIMELINE]: (state, obj: { status: Entity.Status; accountId: number }) => {
|
||||||
state.heading = value
|
if (state.timeline[obj.accountId]) {
|
||||||
},
|
state.timeline[obj.accountId] = [obj.status, ...state.timeline[obj.accountId]]
|
||||||
[MUTATION_TYPES.APPEND_TIMELINE]: (state, update: Entity.Status) => {
|
} else {
|
||||||
// Reject duplicated status in timeline
|
state.timeline[obj.accountId] = [obj.status]
|
||||||
if (!state.timeline.find(item => item.id === update.id) && !state.unreads.find(item => item.id === update.id)) {
|
|
||||||
if (state.heading) {
|
|
||||||
state.timeline = [update].concat(state.timeline)
|
|
||||||
} else {
|
|
||||||
state.unreads = [update].concat(state.unreads)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array<Entity.Status>) => {
|
[MUTATION_TYPES.REPLACE_TIMELINE]: (state, obj: { statuses: Array<Entity.Status>; accountId: number }) => {
|
||||||
state.timeline = messages
|
state.timeline[obj.accountId] = obj.statuses
|
||||||
},
|
},
|
||||||
[MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array<Entity.Status>) => {
|
[MUTATION_TYPES.INSERT_TIMELINE]: (state, obj: { statuses: Array<Entity.Status>; accountId: number }) => {
|
||||||
state.timeline = state.timeline.concat(messages)
|
if (state.timeline[obj.accountId]) {
|
||||||
|
state.timeline[obj.accountId] = [...state.timeline[obj.accountId], ...obj.statuses]
|
||||||
|
} else {
|
||||||
|
state.timeline[obj.accountId] = obj.statuses
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[MUTATION_TYPES.ARCHIVE_TIMELINE]: state => {
|
[MUTATION_TYPES.UPDATE_TOOT]: (state, obj: { status: Entity.Status; accountId: number }) => {
|
||||||
state.timeline = state.timeline.slice(0, 20)
|
state.timeline[obj.accountId] = state.timeline[obj.accountId].map(toot => {
|
||||||
},
|
if (toot.id === obj.status.id) {
|
||||||
[MUTATION_TYPES.CLEAR_TIMELINE]: state => {
|
return obj.status
|
||||||
state.timeline = []
|
} else if (toot.reblog && toot.reblog.id === obj.status.id) {
|
||||||
state.unreads = []
|
|
||||||
},
|
|
||||||
[MUTATION_TYPES.UPDATE_TOOT]: (state, message: Entity.Status) => {
|
|
||||||
state.timeline = state.timeline.map(toot => {
|
|
||||||
if (toot.id === message.id) {
|
|
||||||
return message
|
|
||||||
} else if (toot.reblog && toot.reblog.id === message.id) {
|
|
||||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||||
// So, a message which is received now is original toot.
|
// So, a message which is received now is original toot.
|
||||||
const reblog = {
|
const reblog = {
|
||||||
reblog: message
|
reblog: obj.status
|
||||||
}
|
}
|
||||||
return Object.assign(toot, reblog)
|
return Object.assign(toot, reblog)
|
||||||
} else {
|
} else {
|
||||||
@ -72,21 +54,14 @@ const mutations: MutationTree<PublicState> = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[MUTATION_TYPES.DELETE_TOOT]: (state, id: string) => {
|
[MUTATION_TYPES.DELETE_TOOT]: (state, obj: { statusId: string; accountId: number }) => {
|
||||||
state.timeline = state.timeline.filter(toot => {
|
state.timeline[obj.accountId] = state.timeline[obj.accountId].filter(toot => {
|
||||||
if (toot.reblog !== null && toot.reblog.id === id) {
|
if (toot.reblog !== null && toot.reblog.id === obj.statusId) {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
return toot.id !== id
|
return toot.id !== obj.statusId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
|
||||||
[MUTATION_TYPES.CHANGE_LAZY_LOADING]: (state, value: boolean) => {
|
|
||||||
state.lazyLoading = value
|
|
||||||
},
|
|
||||||
[MUTATION_TYPES.MERGE_UNREADS]: state => {
|
|
||||||
state.timeline = state.unreads.slice(0, 80).concat(state.timeline)
|
|
||||||
state.unreads = []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,46 +71,29 @@ export const ACTION_TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actions: ActionTree<PublicState, RootState> = {
|
const actions: ActionTree<PublicState, RootState> = {
|
||||||
[ACTION_TYPES.FETCH_PUBLIC_TIMELINE]: async ({ dispatch, commit, rootState }): Promise<Array<Entity.Status>> => {
|
[ACTION_TYPES.FETCH_PUBLIC_TIMELINE]: async (
|
||||||
const client = generator(
|
{ commit, rootState },
|
||||||
rootState.TimelineSpace.server!.sns,
|
req: { account: LocalAccount; server: LocalServer }
|
||||||
rootState.TimelineSpace.server!.baseURL,
|
): Promise<Array<Entity.Status>> => {
|
||||||
rootState.TimelineSpace.account!.accessToken,
|
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||||
rootState.App.userAgent
|
|
||||||
)
|
|
||||||
try {
|
try {
|
||||||
const res = await client.getPublicTimeline({ limit: 20 })
|
const res = await client.getPublicTimeline({ limit: 20 })
|
||||||
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
|
commit(MUTATION_TYPES.REPLACE_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
||||||
return res.data
|
return res.data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Disable public timeline
|
console.error(err)
|
||||||
dispatch('TimelineSpace/SideMenu/disablePublic', {}, { root: true })
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[ACTION_TYPES.LAZY_FETCH_TIMELINE]: async (
|
[ACTION_TYPES.LAZY_FETCH_TIMELINE]: async (
|
||||||
{ state, commit, rootState },
|
{ commit, rootState },
|
||||||
lastStatus: Entity.Status
|
req: { lastStatus: Entity.Status; account: LocalAccount; server: LocalServer }
|
||||||
): Promise<Array<Entity.Status> | null> => {
|
): Promise<Array<Entity.Status> | null> => {
|
||||||
if (state.lazyLoading) {
|
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||||
return Promise.resolve(null)
|
return client.getPublicTimeline({ max_id: req.lastStatus.id, limit: 20 }).then(res => {
|
||||||
}
|
commit(MUTATION_TYPES.INSERT_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
||||||
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
|
return res.data
|
||||||
const client = generator(
|
})
|
||||||
rootState.TimelineSpace.server!.sns,
|
|
||||||
rootState.TimelineSpace.server!.baseURL,
|
|
||||||
rootState.TimelineSpace.account!.accessToken,
|
|
||||||
rootState.App.userAgent
|
|
||||||
)
|
|
||||||
return client
|
|
||||||
.getPublicTimeline({ max_id: lastStatus.id, limit: 20 })
|
|
||||||
.then(res => {
|
|
||||||
commit(MUTATION_TYPES.INSERT_TIMELINE, res.data)
|
|
||||||
return res.data
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, false)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user