Merge pull request #4058 from h3poteto/refactor/timeline-store
[refactor] Remove timeline stores
This commit is contained in:
commit
a0a82e3465
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div id="bookmarks">
|
||||
<div></div>
|
||||
<DynamicScroller :items="bookmarks" :min-item-size="60" id="scroller" class="scroller" ref="scroller">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<DynamicScroller :items="bookmarks" :min-item-size="60" id="scroller" class="scroller" ref="scroller" v-else>
|
||||
<template #default="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<toot
|
||||
|
@ -30,10 +30,9 @@ import { useStore } from '@/store'
|
|||
import { useI18next } from 'vue3-i18next'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Entity } from 'megalodon'
|
||||
import useReloadable from '@/components/utils/reloadable'
|
||||
import parse from 'parse-link-header'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Bookmarks'
|
||||
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||
import { MUTATION_TYPES as HEADER_MUTATION } from '@/store/TimelineSpace/HeaderMenu'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
|
@ -44,15 +43,14 @@ export default defineComponent({
|
|||
name: 'bookmarks',
|
||||
components: { Toot },
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/Bookmarks'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
const { reloadable } = useReloadable(store, route, i18n)
|
||||
|
||||
const focusedId = ref<string | null>(null)
|
||||
const heading = ref<boolean>(true)
|
||||
const scroller = ref<any>()
|
||||
const loading = ref(false)
|
||||
const lazyLoading = ref(false)
|
||||
const { j, k, Ctrl_r } = useMagicKeys()
|
||||
|
||||
|
@ -63,12 +61,16 @@ export default defineComponent({
|
|||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const bookmarks = computed(() => store.state.TimelineSpace.Contents.Bookmarks.bookmarks)
|
||||
const bookmarks = ref<Array<Entity.Status>>([])
|
||||
const nextMaxId = ref<string | null>(null)
|
||||
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
const currentFocusedIndex = computed(() => bookmarks.value.findIndex(toot => focusedId.value === toot.uri))
|
||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
|
@ -76,18 +78,27 @@ export default defineComponent({
|
|||
account.server = s
|
||||
|
||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||
store.commit(`TimelineSpace/Contents/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.FETCH_BOOKMARKS}`, account)
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.bookmark_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await client.value.getBookmarks({ limit: 20 })
|
||||
bookmarks.value = res.data
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.bookmark_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
watch(startReload, (newVal, oldVal) => {
|
||||
if (!oldVal && newVal) {
|
||||
|
@ -115,12 +126,23 @@ export default defineComponent({
|
|||
if (
|
||||
(event.target as HTMLElement)!.clientHeight + (event.target as HTMLElement)!.scrollTop >=
|
||||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||
!lazyLoading.value
|
||||
!lazyLoading.value &&
|
||||
nextMaxId.value
|
||||
) {
|
||||
lazyLoading.value = true
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_BOOKMARKS}`, account)
|
||||
.catch(() => {
|
||||
client.value
|
||||
?.getBookmarks({ limit: 20, max_id: nextMaxId.value })
|
||||
.then(res => {
|
||||
bookmarks.value = [...bookmarks.value, ...res.data]
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.bookmark_fetch_error'),
|
||||
type: 'error'
|
||||
|
@ -130,7 +152,7 @@ export default defineComponent({
|
|||
lazyLoading.value = false
|
||||
})
|
||||
}
|
||||
// for upper
|
||||
|
||||
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
||||
heading.value = false
|
||||
} else if ((event.target as HTMLElement)!.scrollTop <= 10 && !heading.value) {
|
||||
|
@ -139,27 +161,40 @@ export default defineComponent({
|
|||
}
|
||||
const reload = async () => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
if (!client.value) return
|
||||
try {
|
||||
await reloadable()
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_BOOKMARKS}`, account).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.bookmark_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
const res = await client.value.getBookmarks({ limit: 20 })
|
||||
bookmarks.value = res.data
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
}
|
||||
}
|
||||
const updateToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, message)
|
||||
bookmarks.value = bookmarks.value.map(status => {
|
||||
if (status.id === message.id) {
|
||||
return message
|
||||
} else if (status.reblog && status.reblog.id === message.id) {
|
||||
return Object.assign(status, {
|
||||
reblog: message
|
||||
})
|
||||
}
|
||||
return status
|
||||
})
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, message)
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
const deleteToot = (id: string) => {
|
||||
bookmarks.value = bookmarks.value.filter(status => {
|
||||
if (status.reblog !== null && status.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return status.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
|
@ -188,8 +223,9 @@ export default defineComponent({
|
|||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
account,
|
||||
loading,
|
||||
backgroundColor
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
:filters="[]"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -132,15 +132,11 @@ export default defineComponent({
|
|||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, { status: message, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
const deleteToot = (id: string) => {
|
||||
if (account.account) {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: message.id, accountId: account.account.id })
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: id, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
|
@ -168,7 +164,6 @@ export default defineComponent({
|
|||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div id="favourites">
|
||||
<div></div>
|
||||
<DynamicScroller :items="favourites" :min-item-size="60" id="scroller" class="scroller" ref="scroller">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<DynamicScroller :items="favourites" :min-item-size="60" id="scroller" class="scroller" ref="scroller" v-else>
|
||||
<template #default="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<toot
|
||||
|
@ -12,9 +12,9 @@
|
|||
:filters="[]"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -30,10 +30,9 @@ import { useMagicKeys, whenever } from '@vueuse/core'
|
|||
import { useStore } from '@/store'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { Entity } from 'megalodon'
|
||||
import parse from 'parse-link-header'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Favourites'
|
||||
import { MUTATION_TYPES as CONTENTS_MUTATION } from '@/store/TimelineSpace/Contents'
|
||||
import { MUTATION_TYPES as HEADER_MUTATION } from '@/store/TimelineSpace/HeaderMenu'
|
||||
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
|
@ -45,12 +44,10 @@ export default defineComponent({
|
|||
name: 'favourites',
|
||||
components: { Toot },
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/Favourites'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
|
||||
const heading = ref<boolean>(false)
|
||||
const focusedId = ref<string | null>(null)
|
||||
const scroller = ref<any>()
|
||||
const { j, k, Ctrl_r } = useMagicKeys()
|
||||
|
@ -58,41 +55,53 @@ export default defineComponent({
|
|||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => parseInt(route.params.id as string))
|
||||
|
||||
const loading = ref(false)
|
||||
const lazyLoading = ref(false)
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
||||
const favourites = computed(() => store.state.TimelineSpace.Contents.Favourites.favourites)
|
||||
const favourites = ref<Array<Entity.Status>>([])
|
||||
const nextMaxId = ref<string | null>(null)
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
const currentFocusedIndex = computed(() => favourites.value.findIndex(status => focusedId.value === status.uri))
|
||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
|
||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.FETCH_FAVOURITES}`, account)
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.favourite_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await client.value.getFavourites({ limit: 20 })
|
||||
favourites.value = res.data
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.favourite_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_FAVOURITES}`, [])
|
||||
const el = document.getElementById('scroller')
|
||||
if (el !== undefined && el !== null) {
|
||||
el.removeEventListener('scroll', onScroll)
|
||||
|
@ -125,12 +134,23 @@ export default defineComponent({
|
|||
if (
|
||||
(event.target as HTMLElement)!.clientHeight + (event.target as HTMLElement)!.scrollTop >=
|
||||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||
!lazyLoading.value
|
||||
!lazyLoading.value &&
|
||||
nextMaxId.value
|
||||
) {
|
||||
lazyLoading.value = true
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_FAVOURITES}`, account)
|
||||
.catch(() => {
|
||||
client.value
|
||||
?.getFavourites({ limit: 20, max_id: nextMaxId.value })
|
||||
.then(res => {
|
||||
favourites.value = [...favourites.value, ...res.data]
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.favourite_fetch_error'),
|
||||
type: 'error'
|
||||
|
@ -140,36 +160,44 @@ export default defineComponent({
|
|||
lazyLoading.value = false
|
||||
})
|
||||
}
|
||||
// for upper
|
||||
if ((event.target as HTMLElement)!.scrollTop > 10 && heading.value) {
|
||||
heading.value = false
|
||||
} else if ((event.target as HTMLElement)!.scrollTop <= 10 && !heading.value) {
|
||||
heading.value = true
|
||||
}
|
||||
}
|
||||
const updateToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, message)
|
||||
favourites.value = favourites.value.map(status => {
|
||||
if (status.id === message.id) {
|
||||
return message
|
||||
} else if (status.reblog && status.reblog.id === message.id) {
|
||||
return Object.assign(status, {
|
||||
reblog: message
|
||||
})
|
||||
}
|
||||
return status
|
||||
})
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, message)
|
||||
const deleteToot = (id: string) => {
|
||||
favourites.value = favourites.value.filter(status => {
|
||||
if (status.reblog !== null && status.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return status.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
const reload = async () => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
if (!client.value) return
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_FAVOURITES}`, account).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.favourite_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
const res = await client.value.getFavourites({ limit: 20 })
|
||||
favourites.value = res.data
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
nextMaxId.value = link.next.max_id
|
||||
} else {
|
||||
nextMaxId.value = null
|
||||
}
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
}
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = favourites.value[0].uri
|
||||
|
@ -189,15 +217,15 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
favourites,
|
||||
backgroundColor,
|
||||
scroller,
|
||||
focusedId,
|
||||
modalOpened,
|
||||
updateToot,
|
||||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, onMounted, reactive } from 'vue'
|
||||
import { defineComponent, computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useStore } from '@/store'
|
||||
import User from '@/components/molecules/User.vue'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace/Contents/FollowRequests'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
@ -23,20 +22,21 @@ export default defineComponent({
|
|||
name: 'follow-requests',
|
||||
components: { User },
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/FollowRequests'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => parseInt(route.params.id as string))
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const requests = computed(() => store.state.TimelineSpace.Contents.FollowRequests.requests)
|
||||
const requests = ref<Array<Entity.Account>>([])
|
||||
|
||||
onMounted(async () => {
|
||||
await initialize()
|
||||
|
@ -47,28 +47,41 @@ export default defineComponent({
|
|||
account.account = a
|
||||
account.server = s
|
||||
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_REQUESTS}`, account).catch(_ => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
const res = await client.value.getFollowRequests()
|
||||
requests.value = res.data
|
||||
}
|
||||
const accept = (user: Entity.Account) => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.ACCEPT_REQUEST}`, { user, account: account.account, server: account.server }).catch(_ => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.follow_request_accept_error'),
|
||||
type: 'error'
|
||||
client.value
|
||||
?.acceptFollowRequest(user.id)
|
||||
.then(() => {
|
||||
client.value?.getFollowRequests().then(res => {
|
||||
requests.value = res.data
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.follow_request_accept_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const reject = (user: Entity.Account) => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.REJECT_REQUEST}`, { user, account: account.account, server: account.server }).catch(_ => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.follow_request_reject_error'),
|
||||
type: 'error'
|
||||
client.value
|
||||
?.rejectFollowRequest(user.id)
|
||||
.then(() => {
|
||||
client.value?.getFollowRequests().then(res => {
|
||||
requests.value = res.data
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.follow_request_reject_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div id="list">
|
||||
<table class="tag-list">
|
||||
<tbody>
|
||||
<tr v-for="tag in tags" v-bind:key="tag._id" @click.stop.prevent="openTimeline(tag.tagName)">
|
||||
<tr v-for="tag in tags" :key="tag._id" @click.stop.prevent="openTimeline(tag.tagName)">
|
||||
<td>
|
||||
{{ tag.tagName }}
|
||||
</td>
|
||||
|
@ -18,31 +18,31 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, onMounted } from 'vue'
|
||||
import { defineComponent, computed, onMounted, ref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useStore } from '@/store'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace/Contents/Hashtag/List'
|
||||
import { LocalTag } from '~/src/types/localTag'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'list',
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/Hashtag/List'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const tags = computed(() => store.state.TimelineSpace.Contents.Hashtag.List.tags)
|
||||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => parseInt(route.params.id as string))
|
||||
const tags = ref<Array<LocalTag>>([])
|
||||
|
||||
onMounted(() => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.LIST_TAGS}`)
|
||||
onMounted(async () => {
|
||||
tags.value = await win.ipcRenderer.invoke('list-hashtags', id.value)
|
||||
})
|
||||
const openTimeline = (tagName: string) => {
|
||||
router.push({
|
||||
path: `/${route.params.id}/hashtag/${tagName}`
|
||||
})
|
||||
}
|
||||
const deleteTag = (tag: LocalTag) => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.REMOVE_TAG}`, tag)
|
||||
const deleteTag = async (tag: LocalTag) => {
|
||||
await win.ipcRenderer.invoke('remove-hashtag', tag)
|
||||
tags.value = await win.ipcRenderer.invoke('list-hashtags', id.value)
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div name="tag" class="tag-timeline">
|
||||
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<DynamicScroller :items="statuses" :min-item-size="86" id="scroller" class="scroller" ref="scroller" v-else>
|
||||
<template #default="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<toot
|
||||
|
@ -11,9 +12,9 @@
|
|||
:filters="[]"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -27,16 +28,12 @@ import { computed, defineComponent, onBeforeUnmount, onMounted, ref, toRefs, wat
|
|||
import { logicAnd } from '@vueuse/math'
|
||||
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useStore } from '@/store'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import useReloadable from '@/components/utils/reloadable'
|
||||
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||
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/Hashtag/Tag'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
@ -46,11 +43,9 @@ export default defineComponent({
|
|||
components: { Toot },
|
||||
props: ['tag'],
|
||||
setup(props) {
|
||||
const space = 'TimelineSpace/Contents/Hashtag/Tag'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
const { reloadable } = useReloadable(store, route, i18n)
|
||||
const { j, k, Ctrl_r } = useMagicKeys()
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
|
@ -60,34 +55,39 @@ export default defineComponent({
|
|||
const focusedId = ref<string | null>(null)
|
||||
const scroller = ref<any>(null)
|
||||
const lazyLoading = ref(false)
|
||||
const loading = ref(false)
|
||||
const heading = ref(true)
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const timeline = computed(() => store.state.TimelineSpace.Contents.Hashtag.Tag.timeline)
|
||||
const statuses = ref<Array<Entity.Status>>([])
|
||||
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
const currentFocusedIndex = computed(() => timeline.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const currentFocusedIndex = computed(() => statuses.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
loading.value = true
|
||||
load(tag.value).finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
loading.value = false
|
||||
})
|
||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||
})
|
||||
watch(tag, (newTag, _oldTag) => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
loading.value = true
|
||||
reset()
|
||||
load(newTag).finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
watch(startReload, (newVal, oldVal) => {
|
||||
|
@ -106,7 +106,7 @@ export default defineComponent({
|
|||
})
|
||||
whenever(logicAnd(j, shortcutEnabled), () => {
|
||||
if (focusedId.value === null) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else {
|
||||
focusNext()
|
||||
}
|
||||
|
@ -119,24 +119,32 @@ export default defineComponent({
|
|||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.STOP_STREAMING}`)
|
||||
reset()
|
||||
})
|
||||
|
||||
const load = async (tag: string) => {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH}`, { tag: tag, account: account.account, server: account.server }).catch(() => {
|
||||
if (!client.value) return
|
||||
try {
|
||||
const res = await client.value.getTagTimeline(tag, { limit: 20 })
|
||||
statuses.value = res.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
if (!account.account) return
|
||||
win.ipcRenderer.on(`update-tag-streamings-${account.account.id}`, (_, update: Entity.Status) => {
|
||||
statuses.value = [update, ...statuses.value]
|
||||
})
|
||||
store.dispatch(`${space}/${ACTION_TYPES.START_STREAMING}`, { tag: tag, account: account.account }).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.start_streaming_error'),
|
||||
type: 'error'
|
||||
})
|
||||
win.ipcRenderer.on(`delete-tag-streamings-${account.account.id}`, (_, id: string) => {
|
||||
deleteToot(id)
|
||||
})
|
||||
win.ipcRenderer.send('start-tag-streaming', {
|
||||
tag: encodeURIComponent(tag),
|
||||
accountId: account.account.id
|
||||
})
|
||||
return true
|
||||
}
|
||||
const reset = () => {
|
||||
heading.value = true
|
||||
|
@ -152,13 +160,12 @@ export default defineComponent({
|
|||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||
!lazyLoading.value
|
||||
) {
|
||||
const lastStatus = statuses.value[statuses.value.length - 1]
|
||||
lazyLoading.value = true
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
||||
tag: tag.value,
|
||||
status: timeline.value[timeline.value.length - 1],
|
||||
account: account.account,
|
||||
server: account.server
|
||||
client.value
|
||||
?.getTagTimeline(tag.value, { max_id: lastStatus.id, limit: 20 })
|
||||
.then(res => {
|
||||
statuses.value = [...statuses.value, ...res.data]
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
|
@ -177,49 +184,47 @@ export default defineComponent({
|
|||
heading.value = true
|
||||
}
|
||||
}
|
||||
const updateToot = (toot: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, toot)
|
||||
const updateToot = (message: Entity.Status) => {
|
||||
statuses.value = statuses.value.map(status => {
|
||||
if (status.id === message.id) {
|
||||
return message
|
||||
} else if (status.reblog && status.reblog.id === message.id) {
|
||||
return Object.assign(status, {
|
||||
reblog: message
|
||||
})
|
||||
}
|
||||
return status
|
||||
})
|
||||
}
|
||||
const deleteToot = (toot: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, toot.id)
|
||||
const deleteToot = (id: string) => {
|
||||
statuses.value = statuses.value.filter(status => {
|
||||
if (status.reblog !== null && status.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return status.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
const reload = async () => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
loading.value = true
|
||||
try {
|
||||
await reloadable()
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.STOP_STREAMING}`)
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH}`, tag.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
store.dispatch(`${space}/${ACTION_TYPES.START_STREAMING}`, tag.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.start_streaming_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
await load(tag.value)
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
} else if (currentFocusedIndex.value < timeline.value.length) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value + 1].uri + timeline.value[currentFocusedIndex.value + 1].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else if (currentFocusedIndex.value < statuses.value.length) {
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value + 1].uri + statuses.value[currentFocusedIndex.value + 1].id
|
||||
}
|
||||
}
|
||||
const focusPrev = () => {
|
||||
if (currentFocusedIndex.value === 0) {
|
||||
focusedId.value = null
|
||||
} else if (currentFocusedIndex.value > 0) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value - 1].uri + timeline.value[currentFocusedIndex.value - 1].id
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value - 1].uri + statuses.value[currentFocusedIndex.value - 1].id
|
||||
}
|
||||
}
|
||||
const focusToot = (message: Entity.Status) => {
|
||||
|
@ -227,7 +232,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
return {
|
||||
timeline,
|
||||
statuses,
|
||||
scroller,
|
||||
focusedId,
|
||||
modalOpened,
|
||||
|
@ -235,8 +240,9 @@ export default defineComponent({
|
|||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
account,
|
||||
loading,
|
||||
backgroundColor
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
:filters="filters"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -180,9 +180,9 @@ export default defineComponent({
|
|||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, { status: message, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
const deleteToot = (id: string) => {
|
||||
if (account.account) {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: message.id, accountId: account.account.id })
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: id, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const fetchTimelineSince = (since_id: string) => {
|
||||
|
|
|
@ -12,18 +12,20 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, toRefs } from 'vue'
|
||||
import { computed, defineComponent, onMounted, toRefs, ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useStore } from '@/store'
|
||||
import User from '@/components/molecules/User.vue'
|
||||
import { MUTATION_TYPES as CONTENTS_MUTATION } from '@/store/TimelineSpace/Contents'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace/Contents/Lists/Edit'
|
||||
import {
|
||||
MUTATION_TYPES as ADD_LIST_MEMBER_MUTATION,
|
||||
ACTION_TYPES as ADD_LIST_MEMBER_ACTION
|
||||
} from '@/store/TimelineSpace/Modals/AddListMember'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'edit-list',
|
||||
|
@ -31,43 +33,55 @@ export default defineComponent({
|
|||
components: { User },
|
||||
setup(props) {
|
||||
const { list_id } = toRefs(props)
|
||||
const space = 'TimelineSpace/Contents/Lists/Edit'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
|
||||
const members = computed(() => store.state.TimelineSpace.Contents.Lists.Edit.members)
|
||||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => parseInt(route.params.id as string))
|
||||
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const members = ref<Array<Entity.Account>>([])
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
|
||||
onMounted(async () => {
|
||||
await init()
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
await load()
|
||||
})
|
||||
const init = async () => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
|
||||
const load = async () => {
|
||||
if (!client.value) return
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_MEMBERS}`, list_id.value)
|
||||
const res = await client.value.getAccountsInList(list_id.value, { limit: 0 })
|
||||
members.value = res.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.members_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
}
|
||||
}
|
||||
|
||||
const removeAccount = async (account: Entity.Account) => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
if (!client.value) return
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.REMOVE_ACCOUNT}`, {
|
||||
account: account,
|
||||
listId: list_id.value
|
||||
})
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_MEMBERS}`, list_id.value)
|
||||
await client.value.deleteAccountsFromList(list_id.value, [account.id])
|
||||
await load()
|
||||
} catch (err) {
|
||||
ElMessage({
|
||||
message: i18n.t('message.remove_user_error'),
|
||||
type: 'error'
|
||||
})
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
}
|
||||
}
|
||||
const addAccount = () => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div id="lists">
|
||||
<div class="new-list" v-loading="creating" :element-loading-background="loadingBackground">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<div class="new-list" v-else>
|
||||
<el-form :inline="true">
|
||||
<input v-model="title" :placeholder="$t('lists.index.new_list')" class="list-title" />
|
||||
<el-button link class="create" @click="createList">
|
||||
|
@ -25,61 +26,78 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, ref } from 'vue'
|
||||
import { computed, defineComponent, onMounted, ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from '@/store'
|
||||
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace/Contents/Lists/Index'
|
||||
import { ACTION_TYPES as SIDE_MENU_ACTION } from '@/store/TimelineSpace/SideMenu'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'lists',
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/Lists/Index'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const i18n = useI18next()
|
||||
|
||||
const title = ref<string>('')
|
||||
const creating = ref<boolean>(false)
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const lists = computed(() => store.state.TimelineSpace.Contents.Lists.Index.lists)
|
||||
const loadingBackground = computed(() => store.state.App.theme.wrapper_mask_color)
|
||||
const lists = ref<Array<Entity.List>>([])
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => route.params.id)
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
fetch().finally(() => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
})
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
loading.value = true
|
||||
await load().finally(() => (loading.value = false))
|
||||
})
|
||||
|
||||
const fetch = async () => {
|
||||
return store.dispatch(`${space}/${ACTION_TYPES.FETCH_LISTS}`).catch(() => {
|
||||
const load = async () => {
|
||||
if (!client.value) return
|
||||
try {
|
||||
const res = await client.value.getLists()
|
||||
lists.value = res.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.lists_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const createList = async () => {
|
||||
creating.value = true
|
||||
if (!client.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.CREATE_LIST}`, title.value)
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_LISTS}`)
|
||||
await client.value.createList(title.value)
|
||||
await load()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.list_create_error'),
|
||||
type: 'error'
|
||||
})
|
||||
} finally {
|
||||
creating.value = false
|
||||
loading.value = false
|
||||
}
|
||||
await store.dispatch(`TimelineSpace/SideMenu/${SIDE_MENU_ACTION.FETCH_LISTS}`)
|
||||
}
|
||||
const edit = (list: Entity.List) => {
|
||||
return router.push(`/${id.value}/lists/${list.id}/edit`)
|
||||
|
@ -89,19 +107,19 @@ export default defineComponent({
|
|||
confirmButtonText: i18n.t('lists.index.delete.confirm.ok'),
|
||||
cancelButtonText: i18n.t('lists.index.delete.confirm.cancel'),
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
if (!client.value) return
|
||||
await client.value.deleteList(list.id)
|
||||
await load()
|
||||
})
|
||||
.then(() => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.DELETE_LIST}`, list)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
creating,
|
||||
loading,
|
||||
title,
|
||||
lists,
|
||||
loadingBackground,
|
||||
backgroundColor,
|
||||
createList,
|
||||
edit,
|
||||
del
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div name="list" class="list-timeline">
|
||||
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<DynamicScroller :items="statuses" :min-item-size="86" id="scroller" class="scroller" ref="scroller" v-else>
|
||||
<template #default="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<toot
|
||||
|
@ -23,18 +24,15 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, ref, computed, onMounted, watch, onBeforeUnmount, onUnmounted, reactive } from 'vue'
|
||||
import { defineComponent, toRefs, ref, computed, onMounted, watch, onUnmounted, reactive } from 'vue'
|
||||
import { logicAnd } from '@vueuse/math'
|
||||
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useStore } from '@/store'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { MUTATION_TYPES as CONTENTS_MUTATION } from '@/store/TimelineSpace/Contents'
|
||||
import { MUTATION_TYPES as HEADER_MUTATION } from '@/store/TimelineSpace/HeaderMenu'
|
||||
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Lists/Show'
|
||||
import { MUTATION_TYPES as TIMELINE_MUTATION } from '@/store/TimelineSpace'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
@ -45,7 +43,6 @@ export default defineComponent({
|
|||
props: ['list_id'],
|
||||
components: { Toot },
|
||||
setup(props) {
|
||||
const space = 'TimelineSpace/Contents/Lists/Show'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
|
@ -58,17 +55,21 @@ export default defineComponent({
|
|||
const focusedId = ref<string | null>(null)
|
||||
const scroller = ref<any>(null)
|
||||
const lazyLoading = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
const heading = ref<boolean>(true)
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const timeline = computed(() => store.state.TimelineSpace.Contents.Lists.Show.timeline)
|
||||
const statuses = ref<Array<Entity.Status>>([])
|
||||
const startReload = computed(() => store.state.TimelineSpace.HeaderMenu.reload)
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
const currentFocusedIndex = computed(() => timeline.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const currentFocusedIndex = computed(() => statuses.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
|
@ -76,15 +77,18 @@ export default defineComponent({
|
|||
account.server = s
|
||||
|
||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
load().finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
})
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
loading.value = true
|
||||
try {
|
||||
await load(list_id.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
watch(list_id, () => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, true)
|
||||
load().finally(() => {
|
||||
store.commit(`TimelineSpace/Contents/${CONTENTS_MUTATION.CHANGE_LOADING}`, false)
|
||||
watch(list_id, id => {
|
||||
loading.value = true
|
||||
load(id).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
watch(startReload, (newVal, oldVal) => {
|
||||
|
@ -103,7 +107,7 @@ export default defineComponent({
|
|||
})
|
||||
whenever(logicAnd(j, shortcutEnabled), () => {
|
||||
if (focusedId.value === null) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else {
|
||||
focusNext()
|
||||
}
|
||||
|
@ -115,9 +119,6 @@ export default defineComponent({
|
|||
reload()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.STOP_STREAMING}`)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
heading.value = true
|
||||
const el = document.getElementById('scroller')
|
||||
|
@ -127,42 +128,42 @@ export default defineComponent({
|
|||
}
|
||||
})
|
||||
|
||||
const load = async () => {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.STOP_STREAMING}`)
|
||||
const load = async (id: string) => {
|
||||
if (!client.value) return
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_TIMELINE}`, {
|
||||
listID: list_id.value,
|
||||
account: account.account,
|
||||
server: account.server
|
||||
})
|
||||
const res = await client.value.getListTimeline(id, { limit: 20 })
|
||||
statuses.value = res.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
store.dispatch(`${space}/${ACTION_TYPES.START_STREAMING}`, { listID: list_id.value, account: account.account }).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.start_streaming_error'),
|
||||
type: 'error'
|
||||
})
|
||||
if (!account.account) return
|
||||
win.ipcRenderer.on(`update-list-streamings-${account.account.id}`, (_, update: Entity.Status) => {
|
||||
statuses.value = [update, ...statuses.value]
|
||||
})
|
||||
win.ipcRenderer.on(`delete-list-streamings-${account.account.id}`, (_, id: string) => {
|
||||
deleteToot(id)
|
||||
})
|
||||
|
||||
win.ipcRenderer.send('start-list-streaming', {
|
||||
listId: id,
|
||||
accountId: account.account.id
|
||||
})
|
||||
return 'started'
|
||||
}
|
||||
|
||||
const onScroll = (event: Event) => {
|
||||
if (
|
||||
(event.target as HTMLElement)!.clientHeight + (event.target as HTMLElement)!.scrollTop >=
|
||||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||
!lazyLoading
|
||||
!lazyLoading.value
|
||||
) {
|
||||
const lastStatus = statuses.value[statuses.value.length - 1]
|
||||
lazyLoading.value = true
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
||||
list_id: list_id.value,
|
||||
status: timeline.value[timeline.value.length - 1],
|
||||
account: account.account,
|
||||
server: account.server
|
||||
})
|
||||
client.value
|
||||
?.getListTimeline(list_id.value, { max_id: lastStatus.id, limit: 20 })
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
|
@ -181,47 +182,46 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
const reload = async () => {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, true)
|
||||
loading.value = true
|
||||
try {
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.STOP_STREAMING}`)
|
||||
await store.dispatch(`${space}/${ACTION_TYPES.FETCH_TIMELINE}`, list_id.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
store.dispatch(`${space}/${ACTION_TYPES.START_STREAMING}`, list_id.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.start_streaming_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
await load(list_id.value)
|
||||
} finally {
|
||||
store.commit(`TimelineSpace/${TIMELINE_MUTATION.CHANGE_LOADING}`, false)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const updateToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, message)
|
||||
statuses.value = statuses.value.map(status => {
|
||||
if (status.id === message.id) {
|
||||
return message
|
||||
} else if (status.reblog && status.reblog.id === message.id) {
|
||||
return Object.assign(status, {
|
||||
reblog: message
|
||||
})
|
||||
}
|
||||
return status
|
||||
})
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, message.id)
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
const deleteToot = (id: string) => {
|
||||
statuses.value = statuses.value.filter(status => {
|
||||
if (status.reblog !== null && status.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return status.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
} else if (currentFocusedIndex.value < timeline.value.length) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value + 1].uri + timeline.value[currentFocusedIndex.value + 1].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else if (currentFocusedIndex.value < statuses.value.length) {
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value + 1].uri + statuses.value[currentFocusedIndex.value + 1].id
|
||||
}
|
||||
}
|
||||
const focusPrev = () => {
|
||||
if (currentFocusedIndex.value === 0) {
|
||||
focusedId.value = null
|
||||
} else if (currentFocusedIndex.value > 0) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value - 1].uri + timeline.value[currentFocusedIndex.value - 1].id
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value - 1].uri + statuses.value[currentFocusedIndex.value - 1].id
|
||||
}
|
||||
}
|
||||
const focusToot = (message: Entity.Status) => {
|
||||
|
@ -230,15 +230,16 @@ export default defineComponent({
|
|||
|
||||
return {
|
||||
scroller,
|
||||
timeline,
|
||||
statuses,
|
||||
focusedId,
|
||||
modalOpened,
|
||||
updateToot,
|
||||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
account,
|
||||
loading,
|
||||
backgroundColor
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
:filters="[]"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -145,10 +145,6 @@ export default defineComponent({
|
|||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: message.id, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
|
@ -175,8 +171,6 @@ export default defineComponent({
|
|||
updateToot,
|
||||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
:filters="filters"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
@selectNotification="focusNotification(item)"
|
||||
@update="updateToot"
|
||||
@select-notification="focusNotification(item)"
|
||||
>
|
||||
</notification>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -180,10 +180,7 @@ export default defineComponent({
|
|||
}, 500)
|
||||
})
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
}
|
||||
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = notifications.value[0].id
|
||||
|
@ -213,9 +210,8 @@ export default defineComponent({
|
|||
focusNext,
|
||||
focusPrev,
|
||||
focusNotification,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
account,
|
||||
scroller
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div id="public">
|
||||
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
|
||||
<div style="width: 100%; height: 120px" v-loading="loading" :element-loading-background="backgroundColor" v-if="loading" />
|
||||
<DynamicScroller :items="statuses" :min-item-size="86" id="scroller" class="scroller" ref="scroller" v-else>
|
||||
<template #default="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<toot
|
||||
|
@ -11,9 +12,9 @@
|
|||
:filters="filters"
|
||||
:account="account.account"
|
||||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@selectToot="focusToot(item)"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
</DynamicScrollerItem>
|
||||
|
@ -27,13 +28,12 @@ import { computed, defineComponent, onBeforeUpdate, onMounted, onUnmounted, ref,
|
|||
import { logicAnd } from '@vueuse/math'
|
||||
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Entity } from 'megalodon'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useStore } from '@/store'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { MUTATION_TYPES as SIDE_MENU_MUTATION } from '@/store/TimelineSpace/SideMenu'
|
||||
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'
|
||||
|
@ -53,23 +53,45 @@ export default defineComponent({
|
|||
|
||||
const focusedId = ref<string | null>(null)
|
||||
const scroller = ref<any>(null)
|
||||
const loading = ref<boolean>(false)
|
||||
const lazyLoading = ref(false)
|
||||
const heading = ref(true)
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
|
||||
const timeline = computed(() => store.state.TimelineSpace.Contents.Public.timeline[id.value])
|
||||
const statuses = ref<Array<Entity.Status>>([])
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
const filters = computed(() => store.getters[`${space}/filters`])
|
||||
const currentFocusedIndex = computed(() => timeline.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const currentFocusedIndex = computed(() => statuses.value.findIndex(toot => focusedId.value === toot.uri + toot.id))
|
||||
const shortcutEnabled = computed(() => !modalOpened.value)
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const backgroundColor = computed(() => store.state.App.theme.background_color)
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await client.value.getPublicTimeline({ limit: 20 })
|
||||
statuses.value = res.data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
win.ipcRenderer.removeAllListeners(`update-public-streamings-${a.id}`)
|
||||
win.ipcRenderer.on(`update-public-streamings-${a.id}`, (_, update: Entity.Status) => {
|
||||
statuses.value = [update, ...statuses.value]
|
||||
})
|
||||
win.ipcRenderer.removeAllListeners(`delete-public-streamings-${a.id}`)
|
||||
win.ipcRenderer.on(`delete-public-streamings-${a.id}`, (_, id: string) => {
|
||||
deleteToot(id)
|
||||
})
|
||||
|
||||
store.commit(`TimelineSpace/SideMenu/${SIDE_MENU_MUTATION.CHANGE_UNREAD_PUBLIC_TIMELINE}`, false)
|
||||
document.getElementById('scroller')?.addEventListener('scroll', onScroll)
|
||||
|
@ -96,7 +118,7 @@ export default defineComponent({
|
|||
})
|
||||
whenever(logicAnd(j, shortcutEnabled), () => {
|
||||
if (focusedId.value === null) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else {
|
||||
focusNext()
|
||||
}
|
||||
|
@ -111,14 +133,15 @@ export default defineComponent({
|
|||
document.getElementById('scroller')!.scrollHeight - 10 &&
|
||||
!lazyLoading.value
|
||||
) {
|
||||
const lastStatus = statuses.value[statuses.value.length - 1]
|
||||
lazyLoading.value = true
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.LAZY_FETCH_TIMELINE}`, {
|
||||
statuses: timeline.value[timeline.value.length - 1],
|
||||
account: account.account,
|
||||
server: account.server
|
||||
client.value
|
||||
?.getPublicTimeline({ max_id: lastStatus.id, limit: 20 })
|
||||
.then(res => {
|
||||
statuses.value = [...statuses.value, ...res.data]
|
||||
})
|
||||
.catch(() => {
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.timeline_fetch_error'),
|
||||
type: 'error'
|
||||
|
@ -137,31 +160,38 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
const updateToot = (message: Entity.Status) => {
|
||||
if (account.account) {
|
||||
store.commit(`${space}/${MUTATION_TYPES.UPDATE_TOOT}`, { status: message, accountId: account.account.id })
|
||||
}
|
||||
statuses.value = statuses.value.map(status => {
|
||||
if (status.id === message.id) {
|
||||
return message
|
||||
} else if (status.reblog && status.reblog.id === message.id) {
|
||||
return Object.assign(status, {
|
||||
reblog: message
|
||||
})
|
||||
}
|
||||
return status
|
||||
})
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
if (account.account) {
|
||||
store.commit(`${space}/${MUTATION_TYPES.DELETE_TOOT}`, { statusId: message.id, accountId: account.account.id })
|
||||
}
|
||||
}
|
||||
const upper = () => {
|
||||
scroller.value.scrollToItem(0)
|
||||
focusedId.value = null
|
||||
const deleteToot = (id: string) => {
|
||||
statuses.value = statuses.value.filter(status => {
|
||||
if (status.reblog !== null && status.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return status.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
const focusNext = () => {
|
||||
if (currentFocusedIndex.value === -1) {
|
||||
focusedId.value = timeline.value[0].uri + timeline.value[0].id
|
||||
} else if (currentFocusedIndex.value < timeline.value.length) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value + 1].uri + timeline.value[currentFocusedIndex.value + 1].id
|
||||
focusedId.value = statuses.value[0].uri + statuses.value[0].id
|
||||
} else if (currentFocusedIndex.value < statuses.value.length) {
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value + 1].uri + statuses.value[currentFocusedIndex.value + 1].id
|
||||
}
|
||||
}
|
||||
const focusPrev = () => {
|
||||
if (currentFocusedIndex.value === 0) {
|
||||
focusedId.value = null
|
||||
} else if (currentFocusedIndex.value > 0) {
|
||||
focusedId.value = timeline.value[currentFocusedIndex.value - 1].uri + timeline.value[currentFocusedIndex.value - 1].id
|
||||
focusedId.value = statuses.value[currentFocusedIndex.value - 1].uri + statuses.value[currentFocusedIndex.value - 1].id
|
||||
}
|
||||
}
|
||||
const focusToot = (message: Entity.Status) => {
|
||||
|
@ -169,7 +199,8 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
return {
|
||||
timeline,
|
||||
loading,
|
||||
statuses,
|
||||
scroller,
|
||||
focusedId,
|
||||
modalOpened,
|
||||
|
@ -177,9 +208,8 @@ export default defineComponent({
|
|||
updateToot,
|
||||
deleteToot,
|
||||
focusToot,
|
||||
heading,
|
||||
upper,
|
||||
account
|
||||
account,
|
||||
backgroundColor
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -10,35 +10,45 @@
|
|||
</el-form>
|
||||
</div>
|
||||
<div class="search-result">
|
||||
<search-account v-if="target === 'account'"></search-account>
|
||||
<search-tag v-else-if="target === 'tag'"></search-tag>
|
||||
<search-toots v-else-if="target === 'toot'"></search-toots>
|
||||
<search-account v-if="target === 'account'" :results="accounts"></search-account>
|
||||
<search-tag v-else-if="target === 'tag'" :results="tags"></search-tag>
|
||||
<search-toots v-else-if="target === 'toot'" :results="statuses"></search-toots>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { defineComponent, ref, onMounted, computed, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import generator, { MegalodonInterface, Entity } from 'megalodon'
|
||||
import { useStore } from '@/store'
|
||||
import SearchAccount from './Search/Account.vue'
|
||||
import SearchTag from './Search/Tag.vue'
|
||||
import SearchToots from './Search/Toots.vue'
|
||||
import { ACTION_TYPES as TAG_ACTION } from '@/store/TimelineSpace/Contents/Search/Tag'
|
||||
import { ACTION_TYPES as ACCOUNT_ACTION } from '@/store/TimelineSpace/Contents/Search/Account'
|
||||
import { ACTION_TYPES as TOOTS_ACTION } from '@/store/TimelineSpace/Contents/Search/Toots'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'search',
|
||||
components: { SearchAccount, SearchTag, SearchToots },
|
||||
setup() {
|
||||
const space = 'TimelineSpace/Contents/Search'
|
||||
const store = useStore()
|
||||
const i18n = useI18next()
|
||||
const route = useRoute()
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
const id = computed(() => parseInt(route.params.id as string))
|
||||
|
||||
const target = ref<string>('account')
|
||||
const query = ref<string>('')
|
||||
const account = reactive<{ account: LocalAccount | null; server: LocalServer | null }>({
|
||||
account: null,
|
||||
server: null
|
||||
})
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
const searchTargets = [
|
||||
{
|
||||
target: 'account',
|
||||
|
@ -53,32 +63,69 @@ export default defineComponent({
|
|||
label: i18n.t('search.toot')
|
||||
}
|
||||
]
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const accounts = ref<Array<Entity.Account>>([])
|
||||
const tags = ref<Array<Entity.Tag>>([])
|
||||
const statuses = ref<Array<Entity.Status>>([])
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
account.account = a
|
||||
account.server = s
|
||||
|
||||
client.value = generator(s.sns, s.baseURL, a.accessToken, userAgent.value)
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
accounts.value = []
|
||||
tags.value = []
|
||||
statuses.value = []
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
clear()
|
||||
switch (target.value) {
|
||||
case 'account':
|
||||
store.dispatch(`${space}/Account/${ACCOUNT_ACTION.SEARCH}`, query.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
client.value
|
||||
?.searchAccount(query.value, { resolve: true })
|
||||
.then(res => {
|
||||
accounts.value = res.data
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
break
|
||||
case 'tag':
|
||||
store.dispatch(`${space}/Tag/${TAG_ACTION.SEARCH}`, `#${query.value}`).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
client.value
|
||||
?.search(`#${query.value}`, 'hashtags', { resolve: true })
|
||||
.then(res => {
|
||||
tags.value = res.data.hashtags
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
break
|
||||
case 'toot':
|
||||
store.dispatch(`${space}/Toots/${TOOTS_ACTION.SEARCH}`, query.value).catch(() => {
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
client.value
|
||||
?.search(query.value, 'statuses', { resolve: true })
|
||||
.then(res => {
|
||||
statuses.value = res.data.statuses
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
ElMessage({
|
||||
message: i18n.t('message.search_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
@ -89,7 +136,10 @@ export default defineComponent({
|
|||
target,
|
||||
searchTargets,
|
||||
query,
|
||||
search
|
||||
search,
|
||||
accounts,
|
||||
tags,
|
||||
statuses
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,26 +11,18 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted } from 'vue'
|
||||
import { useStore } from '@/store'
|
||||
import { MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Search/Account'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { Entity } from 'megalodon'
|
||||
import User from '@/components/molecules/User.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'search-account',
|
||||
components: { User },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const results = computed(() => store.state.TimelineSpace.Contents.Search.Account.results)
|
||||
|
||||
onUnmounted(() => {
|
||||
store.commit(`TimelineSpace/Contents/Search/Account/${MUTATION_TYPES.UPDATE_RESULTS}`, [])
|
||||
})
|
||||
|
||||
return {
|
||||
results
|
||||
props: {
|
||||
results: {
|
||||
type: Object as PropType<Array<Entity.Account>>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
unmounted() {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -11,24 +11,17 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onUnmounted, computed } from 'vue'
|
||||
import { useStore } from '@/store'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { Entity } from 'megalodon'
|
||||
import Tag from '@/components/molecules/Tag.vue'
|
||||
import { MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Search/Tag'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'search-tag',
|
||||
components: { Tag },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const results = computed(() => store.state.TimelineSpace.Contents.Search.Tag.results)
|
||||
|
||||
onUnmounted(() => {
|
||||
store.commit(`TimelineSpace/Contents/Search/Tag/${MUTATION_TYPES.UPDATE_RESULTS}`, [])
|
||||
})
|
||||
|
||||
return {
|
||||
results
|
||||
props: {
|
||||
results: {
|
||||
type: Object as PropType<Array<Entity.Account>>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,24 +11,17 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted } from 'vue'
|
||||
import { useStore } from '@/store'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { Entity } from 'megalodon'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Search/Toots'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'search-account',
|
||||
components: { Toot },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const results = computed(() => store.state.TimelineSpace.Contents.Search.Toots.results)
|
||||
|
||||
onUnmounted(() => {
|
||||
this.$store.commit(`TimelineSpace/Contents/Search/Toots/${MUTATION_TYPES.UPDATE_RESULTS}`, [])
|
||||
})
|
||||
|
||||
return {
|
||||
results
|
||||
props: {
|
||||
results: {
|
||||
type: Object as PropType<Array<Entity.Account>>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -41,7 +41,6 @@ import { useI18next } from 'vue3-i18next'
|
|||
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import { useStore } from '@/store'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace/Modals/AddListMember'
|
||||
import { ACTION_TYPES as LIST_ACTION_TYPES } from '@/store/TimelineSpace/Contents/Lists/Edit'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'add-list-member',
|
||||
|
@ -55,7 +54,6 @@ export default defineComponent({
|
|||
|
||||
const loadingBackground = computed(() => store.state.App.theme.wrapper_mask_color)
|
||||
const accounts = computed(() => store.state.TimelineSpace.Modals.AddListMember.accounts)
|
||||
const listId = computed(() => store.state.TimelineSpace.Modals.AddListMember.targetListId)
|
||||
const addListMemberModal = computed({
|
||||
get: () => store.state.TimelineSpace.Modals.AddListMember.modalOpen,
|
||||
set: (value: boolean) => store.dispatch(`${space}/${ACTION_TYPES.CHANGE_MODAL}`, value)
|
||||
|
@ -80,7 +78,6 @@ export default defineComponent({
|
|||
.dispatch(`${space}/${ACTION_TYPES.ADD}`, account)
|
||||
.then(() => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_MODAL}`, false)
|
||||
store.dispatch(`TimelineSpace/Contents/Lists/Edit/${LIST_ACTION_TYPES.FETCH_MEMBERS}`, listId.value)
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
|
|
|
@ -633,8 +633,8 @@ export default defineComponent({
|
|||
const deleteToot = (message: Entity.Status) => {
|
||||
client.value
|
||||
?.deleteStatus(message.id)
|
||||
.then(message => {
|
||||
ctx.emit('delete', message)
|
||||
.then(() => {
|
||||
ctx.emit('delete', message.id)
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { Store } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import { i18n } from 'i18next'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ACTION_TYPES } from '@/store/TimelineSpace'
|
||||
|
||||
export default function useReloadable(store: Store<RootState>, route: RouteLocationNormalizedLoaded, i18next: i18n) {
|
||||
async function reloadable() {
|
||||
const account = await store.dispatch(`TimelineSpace/${ACTION_TYPES.LOCAL_ACCOUNT}`, route.params.id).catch(err => {
|
||||
ElMessage({
|
||||
message: i18next.t<string>('message.account_load_error'),
|
||||
type: 'error'
|
||||
})
|
||||
throw err
|
||||
})
|
||||
return account
|
||||
}
|
||||
|
||||
return {
|
||||
reloadable
|
||||
}
|
||||
}
|
|
@ -122,7 +122,6 @@ const actions: ActionTree<GlobalHeaderState, RootState> = {
|
|||
await dispatch('TimelineSpace/Contents/Home/fetchTimeline', { 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/Public/fetchPublicTimeline', { account, server }, { root: true })
|
||||
await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', { account, server }, { root: true })
|
||||
})
|
||||
},
|
||||
|
@ -149,14 +148,6 @@ const actions: ActionTree<GlobalHeaderState, RootState> = {
|
|||
win.ipcRenderer.on(`delete-local-streamings-${account.id}`, (_, id: string) => {
|
||||
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 })
|
||||
})
|
||||
win.ipcRenderer.removeAllListeners(`update-direct-streamings-${account.id}`)
|
||||
win.ipcRenderer.on(`update-direct-streamings-${account.id}`, (_, update: Entity.Status) => {
|
||||
commit('TimelineSpace/Contents/DirectMessages/appendTimeline', { status: update, accountId: account.id }, { root: true })
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import Home, { HomeState } from './Contents/Home'
|
||||
import Notifications, { NotificationsState } from './Contents/Notifications'
|
||||
import Favourites, { FavouritesState } from './Contents/Favourites'
|
||||
import Bookmarks, { BookmarksState } from './Contents/Bookmarks'
|
||||
import Local, { LocalState } from './Contents/Local'
|
||||
import Public, { PublicState } from './Contents/Public'
|
||||
import Search, { SearchModuleState } from './Contents/Search'
|
||||
import Lists, { ListsModuleState } from './Contents/Lists'
|
||||
import Hashtag, { HashtagModuleState } from './Contents/Hashtag'
|
||||
import DirectMessages, { DirectMessagesState } from './Contents/DirectMessages'
|
||||
import FollowRequests, { FollowRequestsState } from './Contents/FollowRequests'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
|
@ -20,14 +13,7 @@ type ContentsModule = {
|
|||
Home: HomeState
|
||||
Notifications: NotificationsState
|
||||
DirectMessages: DirectMessagesState
|
||||
Favourites: FavouritesState
|
||||
Bookmarks: BookmarksState
|
||||
Local: LocalState
|
||||
Public: PublicState
|
||||
Search: SearchModuleState
|
||||
Hashtag: HashtagModuleState
|
||||
FollowRequests: FollowRequestsState
|
||||
Lists: ListsModuleState
|
||||
}
|
||||
|
||||
export type ContentsModuleState = ContentsModule & ContentsState
|
||||
|
@ -62,15 +48,8 @@ const Contents: Module<ContentsState, RootState> = {
|
|||
modules: {
|
||||
Home,
|
||||
Notifications,
|
||||
Favourites,
|
||||
Bookmarks,
|
||||
Local,
|
||||
DirectMessages,
|
||||
Public,
|
||||
Search,
|
||||
Lists,
|
||||
Hashtag,
|
||||
FollowRequests
|
||||
DirectMessages
|
||||
},
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import parse from 'parse-link-header'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { LocalAccount } from '~src/types/localAccount'
|
||||
import { LocalServer } from '~src/types/localServer'
|
||||
|
||||
export type BookmarksState = {
|
||||
bookmarks: Array<Entity.Status>
|
||||
maxId: string | null
|
||||
}
|
||||
|
||||
const state = (): BookmarksState => ({
|
||||
bookmarks: [],
|
||||
maxId: null
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_BOOKMARKS: 'updateBookmarks',
|
||||
INSERT_BOOKMARKS: 'insertBookmarks',
|
||||
UPDATE_TOOT: 'updateToot',
|
||||
DELETE_TOOT: 'deleteToot',
|
||||
CHANGE_MAX_ID: 'changeMaxId'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<BookmarksState> = {
|
||||
[MUTATION_TYPES.UPDATE_BOOKMARKS]: (state, bookmarks: Array<Entity.Status>) => {
|
||||
state.bookmarks = bookmarks
|
||||
},
|
||||
[MUTATION_TYPES.INSERT_BOOKMARKS]: (state, bookmarks: Array<Entity.Status>) => {
|
||||
state.bookmarks = state.bookmarks.concat(bookmarks)
|
||||
},
|
||||
[MUTATION_TYPES.UPDATE_TOOT]: (state, message: Entity.Status) => {
|
||||
state.bookmarks = state.bookmarks.map(toot => {
|
||||
if (toot.id === message.id) {
|
||||
return message
|
||||
} else if (toot.reblog !== null && toot.reblog.id === message.id) {
|
||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||
// So, a message which is received now is original toot.
|
||||
const reblog = {
|
||||
reblog: message
|
||||
}
|
||||
return Object.assign(toot, reblog)
|
||||
} else {
|
||||
return toot
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.DELETE_TOOT]: (state, message: Entity.Status) => {
|
||||
state.bookmarks = state.bookmarks.filter(toot => {
|
||||
if (toot.reblog !== null && toot.reblog.id === message.id) {
|
||||
return false
|
||||
} else {
|
||||
return toot.id !== message.id
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.CHANGE_MAX_ID]: (state, id: string | null) => {
|
||||
state.maxId = id
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_BOOKMARKS: 'fetchBookmarks',
|
||||
LAZY_FETCH_BOOKMARKS: 'lazyFetchBookmarks'
|
||||
}
|
||||
|
||||
const actions: ActionTree<BookmarksState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_BOOKMARKS]: async (
|
||||
{ commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getBookmarks({ limit: 20 })
|
||||
commit(MUTATION_TYPES.UPDATE_BOOKMARKS, res.data)
|
||||
// Parse link header
|
||||
try {
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, link.next.max_id)
|
||||
} else {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
}
|
||||
} catch (err) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
console.error(err)
|
||||
}
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.LAZY_FETCH_BOOKMARKS]: async (
|
||||
{ state, commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status> | null> => {
|
||||
if (!state.maxId) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getFavourites({ max_id: state.maxId, limit: 20 })
|
||||
commit(MUTATION_TYPES.INSERT_BOOKMARKS, res.data)
|
||||
// Parse link header
|
||||
try {
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, link.next.max_id)
|
||||
} else {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
}
|
||||
} catch (err) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
console.error(err)
|
||||
}
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
const Bookmark: Module<BookmarksState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default Bookmark
|
|
@ -1,123 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import parse from 'parse-link-header'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
|
||||
export type FavouritesState = {
|
||||
favourites: Array<Entity.Status>
|
||||
maxId: string | null
|
||||
}
|
||||
|
||||
const state = (): FavouritesState => ({
|
||||
favourites: [],
|
||||
maxId: null
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_FAVOURITES: 'updateFavourites',
|
||||
INSERT_FAVOURITES: 'insertFavourites',
|
||||
UPDATE_TOOT: 'updateToot',
|
||||
DELETE_TOOT: 'deleteToot',
|
||||
CHANGE_MAX_ID: 'changeMaxId'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<FavouritesState> = {
|
||||
[MUTATION_TYPES.UPDATE_FAVOURITES]: (state, favourites: Array<Entity.Status>) => {
|
||||
state.favourites = favourites
|
||||
},
|
||||
[MUTATION_TYPES.INSERT_FAVOURITES]: (state, favourites: Array<Entity.Status>) => {
|
||||
state.favourites = state.favourites.concat(favourites)
|
||||
},
|
||||
[MUTATION_TYPES.UPDATE_TOOT]: (state, message: Entity.Status) => {
|
||||
state.favourites = state.favourites.map(toot => {
|
||||
if (toot.id === message.id) {
|
||||
return message
|
||||
} else if (toot.reblog !== null && toot.reblog.id === message.id) {
|
||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||
// So, a message which is received now is original toot.
|
||||
const reblog = {
|
||||
reblog: message
|
||||
}
|
||||
return Object.assign(toot, reblog)
|
||||
} else {
|
||||
return toot
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.DELETE_TOOT]: (state, message: Entity.Status) => {
|
||||
state.favourites = state.favourites.filter(toot => {
|
||||
if (toot.reblog !== null && toot.reblog.id === message.id) {
|
||||
return false
|
||||
} else {
|
||||
return toot.id !== message.id
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.CHANGE_MAX_ID]: (state, id: string | null) => {
|
||||
state.maxId = id
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_FAVOURITES: 'fetchFavourites',
|
||||
LAZY_FETCH_FAVOURITES: 'lazyFetchFavourites'
|
||||
}
|
||||
|
||||
const actions: ActionTree<FavouritesState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_FAVOURITES]: async (
|
||||
{ commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getFavourites({ limit: 20 })
|
||||
commit(MUTATION_TYPES.UPDATE_FAVOURITES, res.data)
|
||||
// Parse link header
|
||||
try {
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, link.next.max_id)
|
||||
} else {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
}
|
||||
} catch (err) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
console.error(err)
|
||||
}
|
||||
return res.data
|
||||
},
|
||||
lazyFetchFavourites: async (
|
||||
{ state, commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status> | null> => {
|
||||
if (!state.maxId) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getFavourites({ max_id: state.maxId, limit: 20 })
|
||||
commit(MUTATION_TYPES.INSERT_FAVOURITES, res.data)
|
||||
// Parse link header
|
||||
try {
|
||||
const link = parse(res.headers.link)
|
||||
if (link !== null && link.next) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, link.next.max_id)
|
||||
} else {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
}
|
||||
} catch (err) {
|
||||
commit(MUTATION_TYPES.CHANGE_MAX_ID, null)
|
||||
console.error(err)
|
||||
}
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
const Favourites: Module<FavouritesState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default Favourites
|
|
@ -1,68 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
|
||||
export type FollowRequestsState = {
|
||||
requests: Array<Entity.Account>
|
||||
}
|
||||
|
||||
const state = (): FollowRequestsState => ({
|
||||
requests: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_REQUESTS: 'updateRequests'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<FollowRequestsState> = {
|
||||
[MUTATION_TYPES.UPDATE_REQUESTS]: (state, accounts: Array<Entity.Account>) => {
|
||||
state.requests = accounts
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_REQUESTS: 'fetchRequests',
|
||||
ACCEPT_REQUEST: 'acceptRequest',
|
||||
REJECT_REQUEST: 'rejectRequest'
|
||||
}
|
||||
|
||||
const actions: ActionTree<FollowRequestsState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_REQUESTS]: async (
|
||||
{ commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Account>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getFollowRequests()
|
||||
commit(MUTATION_TYPES.UPDATE_REQUESTS, res.data)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.ACCEPT_REQUEST]: async (
|
||||
{ dispatch, rootState },
|
||||
req: { user: Entity.Account; account: LocalAccount; server: LocalServer }
|
||||
) => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.acceptFollowRequest(req.user.id)
|
||||
await dispatch(ACTION_TYPES.FETCH_REQUESTS, { account: req.account, server: req.server })
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.REJECT_REQUEST]: async (
|
||||
{ dispatch, rootState },
|
||||
req: { user: Entity.Account; account: LocalAccount; server: LocalServer }
|
||||
) => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.rejectFollowRequest(req.user.id)
|
||||
await dispatch(ACTION_TYPES.FETCH_REQUESTS, { action: req.account, server: req.server })
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
const FollowRequests: Module<FollowRequestsState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default FollowRequests
|
|
@ -1,26 +0,0 @@
|
|||
import List, { ListState } from './Hashtag/List'
|
||||
import Tag, { TagState } from './Hashtag/Tag'
|
||||
import { Module } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type HashtagState = {}
|
||||
|
||||
type HashtagModule = {
|
||||
List: ListState
|
||||
Tag: TagState
|
||||
}
|
||||
|
||||
export type HashtagModuleState = HashtagModule & HashtagState
|
||||
|
||||
const state = (): HashtagState => ({})
|
||||
|
||||
const Hashtag: Module<HashtagState, RootState> = {
|
||||
namespaced: true,
|
||||
modules: {
|
||||
List,
|
||||
Tag
|
||||
},
|
||||
state: state
|
||||
}
|
||||
|
||||
export default Hashtag
|
|
@ -1,53 +0,0 @@
|
|||
import { LocalTag } from '~/src/types/localTag'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
import { toRaw } from 'vue'
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
|
||||
export type ListState = {
|
||||
tags: Array<LocalTag>
|
||||
}
|
||||
|
||||
const state = (): ListState => ({
|
||||
tags: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_TAGS: 'updateTags'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<ListState> = {
|
||||
[MUTATION_TYPES.UPDATE_TAGS]: (state, tags: Array<LocalTag>) => {
|
||||
state.tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
LIST_TAGS: 'listTags',
|
||||
REMOVE_TAG: 'removeTag'
|
||||
}
|
||||
|
||||
const actions: ActionTree<ListState, RootState> = {
|
||||
[ACTION_TYPES.LIST_TAGS]: async ({ rootState, commit }) => {
|
||||
const tags: Array<LocalTag> = await win.ipcRenderer.invoke('list-hashtags', rootState.TimelineSpace.account!.id)
|
||||
commit(MUTATION_TYPES.UPDATE_TAGS, tags)
|
||||
return tags
|
||||
},
|
||||
[ACTION_TYPES.REMOVE_TAG]: async ({ dispatch }, tag: LocalTag) => {
|
||||
await win.ipcRenderer.invoke('remove-hashtag', toRaw(tag))
|
||||
dispatch(ACTION_TYPES.LIST_TAGS)
|
||||
dispatch('TimelineSpace/SideMenu/listTags', {}, { root: true })
|
||||
return 'deleted'
|
||||
}
|
||||
}
|
||||
|
||||
const List: Module<ListState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default List
|
|
@ -1,129 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
import { LocalAccount } from '~src/types/localAccount'
|
||||
import { LocalServer } from '~src/types/localServer'
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
|
||||
export type TagState = {
|
||||
timeline: Array<Entity.Status>
|
||||
}
|
||||
|
||||
const state = (): TagState => ({
|
||||
timeline: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
APPEND_TIMELINE: 'appendTimeline',
|
||||
REPLACE_TIMELINE: 'replaceTimeline',
|
||||
INSERT_TIMELINE: 'insertTimeline',
|
||||
UPDATE_TOOT: 'updateToot',
|
||||
DELETE_TOOT: 'deleteToot'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<TagState> = {
|
||||
[MUTATION_TYPES.APPEND_TIMELINE]: (state, update: Entity.Status) => {
|
||||
state.timeline = [update].concat(state.timeline)
|
||||
},
|
||||
[MUTATION_TYPES.REPLACE_TIMELINE]: (state, timeline: Array<Entity.Status>) => {
|
||||
state.timeline = timeline
|
||||
},
|
||||
[MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array<Entity.Status>) => {
|
||||
state.timeline = state.timeline.concat(messages)
|
||||
},
|
||||
[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 !== null && toot.reblog.id === message.id) {
|
||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||
// So, a message which is received now is original toot.
|
||||
const reblog = {
|
||||
reblog: message
|
||||
}
|
||||
return Object.assign(toot, reblog)
|
||||
} else {
|
||||
return toot
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.DELETE_TOOT]: (state, id: string) => {
|
||||
state.timeline = state.timeline.filter(toot => {
|
||||
if (toot.reblog !== null && toot.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return toot.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH: 'fetch',
|
||||
START_STREAMING: 'startStreaming',
|
||||
STOP_STREAMING: 'stopStreaming',
|
||||
LAZY_FETCH_TIMELINE: 'lazyFetchTimeline'
|
||||
}
|
||||
|
||||
const actions: ActionTree<TagState, RootState> = {
|
||||
[ACTION_TYPES.FETCH]: async (
|
||||
{ commit, rootState },
|
||||
req: { tag: string; account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getTagTimeline(encodeURIComponent(req.tag), { limit: 20 })
|
||||
commit(MUTATION_TYPES.REPLACE_TIMELINE, res.data)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.START_STREAMING]: ({ commit }, req: { tag: string; account: LocalAccount }) => {
|
||||
win.ipcRenderer.on(`update-tag-streamings-${req.account.id}`, (_, update: Entity.Status) => {
|
||||
commit(MUTATION_TYPES.APPEND_TIMELINE, update)
|
||||
})
|
||||
win.ipcRenderer.on(`delete-tag-streamings-${req.account.id}`, (_, id: string) => {
|
||||
commit(MUTATION_TYPES.DELETE_TOOT, id)
|
||||
})
|
||||
// @ts-ignore
|
||||
return new Promise((resolve, reject) => {
|
||||
// eslint-disable-line no-unused-vars
|
||||
win.ipcRenderer.send('start-tag-streaming', {
|
||||
tag: encodeURIComponent(req.tag),
|
||||
accountId: req.account.id
|
||||
})
|
||||
})
|
||||
},
|
||||
[ACTION_TYPES.STOP_STREAMING]: ({ rootState }) => {
|
||||
return new Promise(resolve => {
|
||||
if (rootState.TimelineSpace.account) {
|
||||
win.ipcRenderer.removeAllListeners(`update-tag-streamings-${rootState.TimelineSpace.account.id}`)
|
||||
win.ipcRenderer.removeAllListeners(`update-tag-streamings-${rootState.TimelineSpace.account.id}`)
|
||||
}
|
||||
resolve(null)
|
||||
})
|
||||
},
|
||||
[ACTION_TYPES.LAZY_FETCH_TIMELINE]: async (
|
||||
{ commit, rootState },
|
||||
req: {
|
||||
tag: string
|
||||
status: Entity.Status
|
||||
account: LocalAccount
|
||||
server: LocalServer
|
||||
}
|
||||
): Promise<Array<Entity.Status> | null> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
return client.getTagTimeline(req.tag, { max_id: req.status.id, limit: 20 }).then(res => {
|
||||
commit(MUTATION_TYPES.INSERT_TIMELINE, res.data)
|
||||
return res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const Tag: Module<TagState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default Tag
|
|
@ -1,27 +0,0 @@
|
|||
import Index, { IndexState } from './Lists/Index'
|
||||
import Show, { ShowState } from './Lists/Show'
|
||||
import Edit, { EditState } from './Lists/Edit'
|
||||
import { Module } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type ListsState = {}
|
||||
|
||||
type ListModule = {
|
||||
Index: IndexState
|
||||
Show: ShowState
|
||||
Edit: EditState
|
||||
}
|
||||
|
||||
export type ListsModuleState = ListModule & ListsState
|
||||
|
||||
const state = (): ListsState => ({})
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
modules: {
|
||||
Index,
|
||||
Show,
|
||||
Edit
|
||||
}
|
||||
} as Module<ListsState, RootState>
|
|
@ -1,57 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { RemoveAccountFromList } from '@/types/removeAccountFromList'
|
||||
|
||||
export type EditState = {
|
||||
members: Array<Entity.Account>
|
||||
}
|
||||
|
||||
const state = (): EditState => ({
|
||||
members: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
CHANGE_MEMBERS: 'changeMembers'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<EditState> = {
|
||||
[MUTATION_TYPES.CHANGE_MEMBERS]: (state, members: Array<Entity.Account>) => {
|
||||
state.members = members
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_MEMBERS: 'fetchMembers',
|
||||
REMOVE_ACCOUNT: 'removeAccount'
|
||||
}
|
||||
|
||||
const actions: ActionTree<EditState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_MEMBERS]: async ({ commit, rootState }, listId: string): Promise<Array<Entity.Account>> => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
const res = await client.getAccountsInList(listId, { limit: 0 })
|
||||
commit(MUTATION_TYPES.CHANGE_MEMBERS, res.data)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.REMOVE_ACCOUNT]: async ({ rootState }, remove: RemoveAccountFromList): Promise<{}> => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
return client.deleteAccountsFromList(remove.listId, [remove.account.id])
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
} as Module<EditState, RootState>
|
|
@ -1,69 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type IndexState = {
|
||||
lists: Array<Entity.List>
|
||||
}
|
||||
|
||||
const state = (): IndexState => ({
|
||||
lists: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
CHANGE_LISTS: 'changeLists'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<IndexState> = {
|
||||
[MUTATION_TYPES.CHANGE_LISTS]: (state, lists: Array<Entity.List>) => {
|
||||
state.lists = lists
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_LISTS: 'fetchLists',
|
||||
CREATE_LIST: 'createList',
|
||||
DELETE_LIST: 'deleteList'
|
||||
}
|
||||
|
||||
const actions: ActionTree<IndexState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_LISTS]: async ({ commit, rootState }): Promise<Array<Entity.List>> => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
const res = await client.getLists()
|
||||
commit(MUTATION_TYPES.CHANGE_LISTS, res.data)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.CREATE_LIST]: async ({ rootState }, title: string): Promise<Entity.List> => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
const res = await client.createList(title)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.DELETE_LIST]: async ({ dispatch, rootState }, list: Entity.List) => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
const res = await client.deleteList(list.id)
|
||||
dispatch(ACTION_TYPES.FETCH_LISTS)
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
} as Module<IndexState, RootState>
|
|
@ -1,122 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
|
||||
export type ShowState = {
|
||||
timeline: Array<Entity.Status>
|
||||
}
|
||||
|
||||
const state = (): ShowState => ({
|
||||
timeline: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
APPEND_TIMELINE: 'appendTimeline',
|
||||
REPLACE_TIMELINE: 'replaceTimeline',
|
||||
INSERT_TIMELINE: 'insertTimeline',
|
||||
UPDATE_TOOT: 'updateToot',
|
||||
DELETE_TOOT: 'deleteToot'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<ShowState> = {
|
||||
[MUTATION_TYPES.APPEND_TIMELINE]: (state, update: Entity.Status) => {
|
||||
state.timeline = [update].concat(state.timeline)
|
||||
},
|
||||
[MUTATION_TYPES.REPLACE_TIMELINE]: (state, timeline: Array<Entity.Status>) => {
|
||||
state.timeline = timeline
|
||||
},
|
||||
[MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array<Entity.Status>) => {
|
||||
state.timeline = state.timeline.concat(messages)
|
||||
},
|
||||
[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 !== null && toot.reblog.id === message.id) {
|
||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||
// So, a message which is received now is original toot.
|
||||
const reblog = {
|
||||
reblog: message
|
||||
}
|
||||
return Object.assign(toot, reblog)
|
||||
} else {
|
||||
return toot
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.DELETE_TOOT]: (state, id: string) => {
|
||||
state.timeline = state.timeline.filter(toot => {
|
||||
if (toot.reblog !== null && toot.reblog.id === id) {
|
||||
return false
|
||||
} else {
|
||||
return toot.id !== id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_TIMELINE: 'fetchTimeline',
|
||||
START_STREAMING: 'startStreaming',
|
||||
STOP_STREAMING: 'stopStreaming',
|
||||
LAZY_FETCH_TIMELINE: 'lazyFetchTimeline'
|
||||
}
|
||||
|
||||
const actions: ActionTree<ShowState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_TIMELINE]: async (
|
||||
{ commit, rootState },
|
||||
req: { listID: string; account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
const res = await client.getListTimeline(req.listID, { limit: 20 })
|
||||
commit(MUTATION_TYPES.REPLACE_TIMELINE, res.data)
|
||||
return res.data
|
||||
},
|
||||
[ACTION_TYPES.START_STREAMING]: ({ commit }, req: { listID: string; account: LocalAccount }) => {
|
||||
win.ipcRenderer.on(`update-list-streamings-${req.account.id}`, (_, update: Entity.Status) => {
|
||||
commit(MUTATION_TYPES.APPEND_TIMELINE, update)
|
||||
})
|
||||
win.ipcRenderer.on(`delete-list-streamings-${req.account.id}`, (_, id: string) => {
|
||||
commit(MUTATION_TYPES.DELETE_TOOT, id)
|
||||
})
|
||||
// @ts-ignore
|
||||
return new Promise((resolve, reject) => {
|
||||
// eslint-disable-line no-unused-vars
|
||||
win.ipcRenderer.send('start-list-streaming', {
|
||||
listId: req.listID,
|
||||
accountId: req.account.id
|
||||
})
|
||||
})
|
||||
},
|
||||
[ACTION_TYPES.STOP_STREAMING]: ({ rootState }) => {
|
||||
return new Promise(resolve => {
|
||||
if (rootState.TimelineSpace.account) {
|
||||
win.ipcRenderer.removeAllListeners(`update-list-streamings-${rootState.TimelineSpace.account.id}`)
|
||||
win.ipcRenderer.removeAllListeners(`delete-list-streamings-${rootState.TimelineSpace.account.id}`)
|
||||
}
|
||||
resolve(null)
|
||||
})
|
||||
},
|
||||
[ACTION_TYPES.LAZY_FETCH_TIMELINE]: async (
|
||||
{ commit, rootState },
|
||||
req: { list_id: string; status: Entity.Status; account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status> | null> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
return client.getListTimeline(req.list_id, { max_id: req.status.id, limit: 20 }).then(res => {
|
||||
commit(MUTATION_TYPES.INSERT_TIMELINE, res.data)
|
||||
return res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
} as Module<ShowState, RootState>
|
|
@ -1,114 +0,0 @@
|
|||
import generator, { Entity, FilterContext } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
|
||||
export type PublicState = {
|
||||
timeline: { [key: number]: Array<Entity.Status> }
|
||||
}
|
||||
|
||||
const state = (): PublicState => ({
|
||||
timeline: {}
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
APPEND_TIMELINE: 'appendTimeline',
|
||||
REPLACE_TIMELINE: 'replaceTimeline',
|
||||
INSERT_TIMELINE: 'insertTimeline',
|
||||
UPDATE_TOOT: 'updateToot',
|
||||
DELETE_TOOT: 'deleteToot'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<PublicState> = {
|
||||
[MUTATION_TYPES.APPEND_TIMELINE]: (state, obj: { status: Entity.Status; accountId: number }) => {
|
||||
if (state.timeline[obj.accountId]) {
|
||||
state.timeline[obj.accountId] = [obj.status, ...state.timeline[obj.accountId]]
|
||||
} else {
|
||||
state.timeline[obj.accountId] = [obj.status]
|
||||
}
|
||||
},
|
||||
[MUTATION_TYPES.REPLACE_TIMELINE]: (state, obj: { statuses: Array<Entity.Status>; accountId: number }) => {
|
||||
state.timeline[obj.accountId] = obj.statuses
|
||||
},
|
||||
[MUTATION_TYPES.INSERT_TIMELINE]: (state, obj: { statuses: Array<Entity.Status>; accountId: number }) => {
|
||||
if (state.timeline[obj.accountId]) {
|
||||
state.timeline[obj.accountId] = [...state.timeline[obj.accountId], ...obj.statuses]
|
||||
} else {
|
||||
state.timeline[obj.accountId] = obj.statuses
|
||||
}
|
||||
},
|
||||
[MUTATION_TYPES.UPDATE_TOOT]: (state, obj: { status: Entity.Status; accountId: number }) => {
|
||||
state.timeline[obj.accountId] = state.timeline[obj.accountId].map(toot => {
|
||||
if (toot.id === obj.status.id) {
|
||||
return obj.status
|
||||
} else if (toot.reblog && toot.reblog.id === obj.status.id) {
|
||||
// When user reblog/favourite a reblogged toot, target message is a original toot.
|
||||
// So, a message which is received now is original toot.
|
||||
const reblog = {
|
||||
reblog: obj.status
|
||||
}
|
||||
return Object.assign(toot, reblog)
|
||||
} else {
|
||||
return toot
|
||||
}
|
||||
})
|
||||
},
|
||||
[MUTATION_TYPES.DELETE_TOOT]: (state, obj: { statusId: string; accountId: number }) => {
|
||||
state.timeline[obj.accountId] = state.timeline[obj.accountId].filter(toot => {
|
||||
if (toot.reblog !== null && toot.reblog.id === obj.statusId) {
|
||||
return false
|
||||
} else {
|
||||
return toot.id !== obj.statusId
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
FETCH_PUBLIC_TIMELINE: 'fetchPublicTimeline',
|
||||
LAZY_FETCH_TIMELINE: 'lazyFetchTimeline'
|
||||
}
|
||||
|
||||
const actions: ActionTree<PublicState, RootState> = {
|
||||
[ACTION_TYPES.FETCH_PUBLIC_TIMELINE]: async (
|
||||
{ commit, rootState },
|
||||
req: { account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status>> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
try {
|
||||
const res = await client.getPublicTimeline({ limit: 20 })
|
||||
commit(MUTATION_TYPES.REPLACE_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
||||
return res.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return []
|
||||
}
|
||||
},
|
||||
[ACTION_TYPES.LAZY_FETCH_TIMELINE]: async (
|
||||
{ commit, rootState },
|
||||
req: { lastStatus: Entity.Status; account: LocalAccount; server: LocalServer }
|
||||
): Promise<Array<Entity.Status> | null> => {
|
||||
const client = generator(req.server.sns, req.server.baseURL, req.account.accessToken, rootState.App.userAgent)
|
||||
return client.getPublicTimeline({ max_id: req.lastStatus.id, limit: 20 }).then(res => {
|
||||
commit(MUTATION_TYPES.INSERT_TIMELINE, { statuses: res.data, accountId: req.account.id })
|
||||
return res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getters: GetterTree<PublicState, RootState> = {
|
||||
filters: (_state, _getters, rootState) => {
|
||||
return rootState.TimelineSpace.filters.filter(f => f.context.includes(FilterContext.Public) && !f.irreversible)
|
||||
}
|
||||
}
|
||||
|
||||
const Public: Module<PublicState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions,
|
||||
getters: getters
|
||||
}
|
||||
|
||||
export default Public
|
|
@ -1,25 +0,0 @@
|
|||
import Account, { AccountState } from './Search/Account'
|
||||
import Tag, { TagState } from './Search/Tag'
|
||||
import Toots, { TootsState } from './Search/Toots'
|
||||
import { Module } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type SearchState = {}
|
||||
|
||||
type SearchModule = {
|
||||
Account: AccountState
|
||||
Tag: TagState
|
||||
Toots: TootsState
|
||||
}
|
||||
|
||||
export type SearchModuleState = SearchModule & SearchState
|
||||
|
||||
const state = (): SearchState => ({})
|
||||
|
||||
const Search: Module<SearchState, RootState> = {
|
||||
namespaced: true,
|
||||
modules: { Account, Tag, Toots },
|
||||
state: state
|
||||
}
|
||||
|
||||
export default Search
|
|
@ -1,53 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type AccountState = {
|
||||
results: Array<Entity.Account>
|
||||
}
|
||||
|
||||
const state = (): AccountState => ({
|
||||
results: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_RESULTS: 'updateResults'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<AccountState> = {
|
||||
[MUTATION_TYPES.UPDATE_RESULTS]: (state, results: Array<Entity.Account>) => {
|
||||
state.results = results
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
SEARCH: 'search'
|
||||
}
|
||||
|
||||
const actions: ActionTree<AccountState, RootState> = {
|
||||
[ACTION_TYPES.SEARCH]: async ({ commit, rootState }, query: string): Promise<Array<Entity.Account>> => {
|
||||
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
return client
|
||||
.searchAccount(query, { resolve: true })
|
||||
.then(res => {
|
||||
commit(MUTATION_TYPES.UPDATE_RESULTS, res.data)
|
||||
return res.data
|
||||
})
|
||||
.finally(() => {
|
||||
commit('TimelineSpace/Contents/changeLoading', false, { root: true })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
} as Module<AccountState, RootState>
|
|
@ -1,53 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type TagState = {
|
||||
results: Array<Entity.Tag>
|
||||
}
|
||||
|
||||
const state = (): TagState => ({
|
||||
results: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_RESULTS: 'updateResults'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<TagState> = {
|
||||
[MUTATION_TYPES.UPDATE_RESULTS]: (state, results: Array<Entity.Tag>) => {
|
||||
state.results = results
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
SEARCH: 'search'
|
||||
}
|
||||
|
||||
const actions: ActionTree<TagState, RootState> = {
|
||||
[ACTION_TYPES.SEARCH]: async ({ commit, rootState }, query: string): Promise<Array<Entity.Tag>> => {
|
||||
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
return client
|
||||
.search(query, 'hashtags', { resolve: true })
|
||||
.then(res => {
|
||||
commit(MUTATION_TYPES.UPDATE_RESULTS, res.data.hashtags)
|
||||
return res.data.hashtags
|
||||
})
|
||||
.finally(() => {
|
||||
commit('TimelineSpace/Contents/changeLoading', false, { root: true })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
} as Module<TagState, RootState>
|
|
@ -1,55 +0,0 @@
|
|||
import generator, { Entity } from 'megalodon'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
|
||||
export type TootsState = {
|
||||
results: Array<Entity.Status>
|
||||
}
|
||||
|
||||
const state = (): TootsState => ({
|
||||
results: []
|
||||
})
|
||||
|
||||
export const MUTATION_TYPES = {
|
||||
UPDATE_RESULTS: 'updateResults'
|
||||
}
|
||||
|
||||
const mutations: MutationTree<TootsState> = {
|
||||
[MUTATION_TYPES.UPDATE_RESULTS]: (state, results: Array<Entity.Status>) => {
|
||||
state.results = results
|
||||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
SEARCH: 'search'
|
||||
}
|
||||
|
||||
const actions: ActionTree<TootsState, RootState> = {
|
||||
[ACTION_TYPES.SEARCH]: async ({ commit, rootState }, query: string): Promise<Array<Entity.Status>> => {
|
||||
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.server!.sns,
|
||||
rootState.TimelineSpace.server!.baseURL,
|
||||
rootState.TimelineSpace.account!.accessToken,
|
||||
rootState.App.userAgent
|
||||
)
|
||||
return client
|
||||
.search(query, 'statuses', { resolve: true })
|
||||
.then(res => {
|
||||
commit(MUTATION_TYPES.UPDATE_RESULTS, res.data.statuses)
|
||||
return res.data.statuses
|
||||
})
|
||||
.finally(() => {
|
||||
commit('TimelineSpace/Contents/changeLoading', false, { root: true })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const Toots: Module<TootsState, RootState> = {
|
||||
namespaced: true,
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
actions: actions
|
||||
}
|
||||
|
||||
export default Toots
|
Loading…
Reference in New Issue