Clean up organisms store

This commit is contained in:
AkiraFukushima 2023-01-22 22:56:11 +09:00
parent c9d43714e9
commit b36543502b
No known key found for this signature in database
GPG Key ID: B6E51BAC4DE1A957
5 changed files with 83 additions and 246 deletions

View File

@ -230,37 +230,32 @@
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed, toRefs, watch, nextTick } from 'vue'
import { defineComponent, PropType, ref, computed, toRefs, watch, nextTick, onMounted } from 'vue'
import { logicAnd } from '@vueuse/math'
import { useMagicKeys, whenever } from '@vueuse/core'
import 'emoji-mart-vue-fast/css/emoji-mart.css'
import data from 'emoji-mart-vue-fast/data/all.json'
import moment from 'moment'
import { Entity } from 'megalodon'
import generator, { Entity, MegalodonInterface } from 'megalodon'
import { useRoute, useRouter } from 'vue-router'
import { useI18next } from 'vue3-i18next'
import { ElMessage } from 'element-plus'
import { useStore } from '@/store'
import { Picker, EmojiIndex } from 'emoji-mart-vue-fast/src'
import { findAccount, findLink, findTag } from '~/src/renderer/utils/tootParser'
import { findAccount, findLink, findTag, ParsedAccount } from '~/src/renderer/utils/tootParser'
import emojify from '~/src/renderer/utils/emojify'
import FailoverImg from '~/src/renderer/components/atoms/FailoverImg.vue'
import Poll from '~/src/renderer/components/molecules/Toot/Poll.vue'
import LinkPreview from '~/src/renderer/components/molecules/Toot/LinkPreview.vue'
import Quote from '@/components/molecules/Toot/Quote.vue'
// import { setInterval, clearInterval } from 'timers'
import QuoteSupported from '@/utils/quoteSupported'
import Filtered from '@/utils/filter'
import { usernameWithStyle, accountNameWithStyle } from '@/utils/username'
import { parseDatetime } from '@/utils/datetime'
import { MUTATION_TYPES as SIDEBAR_MUTATION, ACTION_TYPES as SIDEBAR_ACTION } from '@/store/TimelineSpace/Contents/SideBar'
import { ACTION_TYPES as PROFILE_ACTION } from '@/store/TimelineSpace/Contents/SideBar/AccountProfile'
import { ACTION_TYPES as DETAIL_ACTION } from '@/store/TimelineSpace/Contents/SideBar/TootDetail'
import { ACTION_TYPES as REPORT_ACTION } from '@/store/TimelineSpace/Modals/Report'
import { MUTATION_TYPES as COMPOSE_MUTATION } from '@/store/TimelineSpace/Compose'
import { ACTION_TYPES as MUTE_ACTION } from '@/store/TimelineSpace/Modals/MuteConfirm'
import { ACTION_TYPES as VIEWER_ACTION } from '@/store/TimelineSpace/Modals/ImageViewer'
import { ACTION_TYPES } from '@/store/organisms/Toot'
import { LocalAccount } from '~/src/types/localAccount'
import { LocalServer } from '~/src/types/localServer'
@ -311,7 +306,6 @@ export default defineComponent({
},
emits: ['selectToot', 'focusRight', 'focusLeft', 'update', 'delete', 'sizeChanged'],
setup(props, ctx) {
const space = 'organisms/Toot'
const store = useStore()
const route = useRoute()
const router = useRouter()
@ -325,6 +319,8 @@ export default defineComponent({
const hideAllAttachments = ref(store.state.App.hideAllAttachments)
const emojiPickerOpened = ref(false)
const emojiIndex = defaultEmojiIndex
const userAgent = computed(() => store.state.App.userAgent)
const client = ref<MegalodonInterface | null>(null)
const displayNameStyle = computed(() => store.state.App.displayNameStyle)
const timeFormat = computed(() => store.state.App.timeFormat)
@ -395,6 +391,10 @@ export default defineComponent({
return QuoteSupported(server.value.sns, server.value.domain)
})
onMounted(() => {
client.value = generator(server.value.sns, server.value.baseURL, account.value.accessToken, userAgent.value)
})
whenever(logicAnd(l, shortcutEnabled), () => {
ctx.emit('focusRight')
})
@ -451,25 +451,46 @@ export default defineComponent({
}
const parsedAccount = findAccount(e.target as HTMLElement, 'toot')
if (parsedAccount !== null) {
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, true)
store
.dispatch(`TimelineSpace/Contents/SideBar/AccountProfile/${PROFILE_ACTION.SEARCH_ACCOUNT}`, {
parsedAccount: parsedAccount,
status: originalMessage.value
})
searchAccount(parsedAccount, originalMessage.value)
.then(account => {
store.dispatch(`TimelineSpace/Contents/SideBar/${SIDEBAR_ACTION.OPEN_ACCOUNT_COMPONENT}`)
store.dispatch(`TimelineSpace/Contents/SideBar/AccountProfile/${PROFILE_ACTION.CHANGE_ACCOUNT}`, account)
if (account === null) throw new Error('account not found')
router.push({ query: { detail: 'true', account_id: account.id } })
})
.catch(err => {
console.error(err)
openLink(e)
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, false)
})
return parsedAccount.acct
}
return openLink(e)
}
const searchAccount = async (account: ParsedAccount, status: Entity.Status): Promise<Entity.Account | null> => {
if (!client.value) {
return null
}
if (status.in_reply_to_account_id) {
const res = await client.value.getAccount(status.in_reply_to_account_id)
if (res.status === 200) {
const user = accountMatch([res.data], account, server.value.domain)
if (user) return user
}
}
if (status.in_reply_to_id) {
const res = await client.value.getStatusContext(status.id)
if (res.status === 200) {
const accounts: Array<Entity.Account> = res.data.ancestors.map(s => s.account).concat(res.data.descendants.map(s => s.account))
const user = accountMatch(accounts, account, server.value.domain)
if (user) return user
}
}
const res = await client.value.searchAccount(account.url, { resolve: true })
if (res.data.length === 0) return null
const user = accountMatch(res.data, account, server.value.domain)
if (user) return user
return null
}
const openLink = (e: MouseEvent) => {
const link = findLink(e.target as HTMLElement, 'toot')
if (link !== null) {
@ -499,12 +520,12 @@ export default defineComponent({
store.dispatch(`TimelineSpace/Modals/MuteConfirm/${MUTE_ACTION.CHANGE_MODAL}`, true)
}
const block = () => {
store.dispatch(`${space}/${ACTION_TYPES.BLOCK}`, originalMessage.value.account)
client.value?.blockAccount(originalMessage.value.account.id)
}
const changeReblog = (message: Entity.Status) => {
if (message.reblogged) {
store
.dispatch(`${space}/${ACTION_TYPES.UNREBLOG}`, message)
client.value
?.unreblogStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -516,8 +537,8 @@ export default defineComponent({
})
})
} else {
store
.dispatch(`${space}/${ACTION_TYPES.REBLOG}`, message)
client.value
?.reblogStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -532,8 +553,8 @@ export default defineComponent({
}
const changeFavourite = (message: Entity.Status) => {
if (message.favourited) {
store
.dispatch(`${space}/${ACTION_TYPES.REMOVE_FAVOURITE}`, message)
client.value
?.unfavouriteStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -545,8 +566,8 @@ export default defineComponent({
})
})
} else {
store
.dispatch(`${space}/${ACTION_TYPES.ADD_FAVOURITE}`, message)
client.value
?.favouriteStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -561,8 +582,8 @@ export default defineComponent({
}
const changeBookmark = (message: Entity.Status) => {
if (message.bookmarked) {
store
.dispatch(`${space}/${ACTION_TYPES.REMOVE_BOOKMARK}`, message)
client.value
?.unbookmarkStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -574,8 +595,8 @@ export default defineComponent({
})
})
} else {
store
.dispatch(`${space}/${ACTION_TYPES.ADD_BOOKMARK}`, message)
client.value
?.bookmarkStatus(message.id)
.then(data => {
ctx.emit('update', data)
})
@ -599,13 +620,11 @@ export default defineComponent({
})
}
const openUser = (account: Entity.Account) => {
store.dispatch(`TimelineSpace/Contents/SideBar/${SIDEBAR_ACTION.OPEN_ACCOUNT_COMPONENT}`)
store.dispatch(`TimelineSpace/Contents/SideBar/AccountProfile/${PROFILE_ACTION.CHANGE_ACCOUNT}`, account)
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, true)
router.push({ query: { detail: 'true', account_id: account.id } })
}
const deleteToot = (message: Entity.Status) => {
store
.dispatch(`${space}/${ACTION_TYPES.DELETE_TOOT}`, message)
client.value
?.deleteStatus(message.id)
.then(message => {
ctx.emit('delete', message)
})
@ -620,48 +639,40 @@ export default defineComponent({
return emojify(content, emojis)
}
const vote = async choices => {
if (!poll.value) {
if (!poll.value || !client.value) {
return
}
const res = await store.dispatch(`${space}/${ACTION_TYPES.VOTE}`, {
id: poll.value.id,
choices: choices
})
const res = await client.value.votePoll(poll.value.id, choices)
const status = Object.assign({}, originalMessage.value, {
poll: res
})
ctx.emit('update', status)
}
const refresh = async (id: string) => {
const res = await store.dispatch(`${space}/${ACTION_TYPES.REFRESH}`, id)
if (!client.value) return
const res = await client.value.getPoll(id)
const status = Object.assign({}, originalMessage.value, {
poll: res
})
ctx.emit('update', status)
}
const selectEmoji = async (emoji: any) => {
const status = await store.dispatch(`${space}/${ACTION_TYPES.SEND_REACTION}`, {
status_id: originalMessage.value.id,
native: emoji.native
})
if (!client.value) return
const status = await client.value.createEmojiReaction(originalMessage.value.id, emoji.native)
ctx.emit('update', status)
}
const addReaction = async (native: any) => {
const status = await store.dispatch(`${space}/${ACTION_TYPES.SEND_REACTION}`, {
status_id: originalMessage.value.id,
native: native
})
if (!client.value) return
const status = await client.value.createEmojiReaction(originalMessage.value.id, native)
ctx.emit('update', status)
}
const removeReaction = async (native: any) => {
const status = await store.dispatch(`${space}/${ACTION_TYPES.DELETE_REACTION}`, {
status_id: originalMessage.value.id,
native: native
})
if (!client.value) return
const status = await client.value.deleteEmojiReaction(originalMessage.value.id, native)
ctx.emit('update', status)
}
const openQuote = () => {
store.dispatch(`TimelineSpace/Modals/NewToot/${NEW_ACTION.OPEN_QUOTE}`, originalMessage.value)
// TODO
}
const toggleSpoiler = () => {
showContent.value = !showContent.value
@ -732,6 +743,18 @@ export default defineComponent({
}
}
})
const accountMatch = (findAccounts: Array<Entity.Account>, parsedAccount: ParsedAccount, domain: string): Entity.Account | false => {
const account = findAccounts.find(a => `@${a.acct}` === parsedAccount.acct)
if (account) return account
const pleromaUser = findAccounts.find(a => a.acct === parsedAccount.acct)
if (pleromaUser) return pleromaUser
const localUser = findAccounts.find(a => `@${a.username}@${domain}` === parsedAccount.acct)
if (localUser) return localUser
const user = findAccounts.find(a => a.url === parsedAccount.url)
if (!user) return false
return user
}
</script>
<style lang="scss" scoped>

View File

@ -8,7 +8,6 @@ import Login, { LoginState } from './Login'
import TimelineSpace, { TimelineSpaceModuleState } from './TimelineSpace'
import Preferences, { PreferencesModuleState } from './Preferences'
import Settings, { SettingsModuleState } from './Settings'
import organisms, { OrganismsModuleState } from './organisms'
import { MyWindow } from '~/src/types/global'
const win = (window as any) as MyWindow
@ -20,7 +19,6 @@ export interface RootState {
TimelineSpace: TimelineSpaceModuleState
Preferences: PreferencesModuleState
Settings: SettingsModuleState
molecules: OrganismsModuleState
route: RouteLocationNormalized
}
@ -39,7 +37,6 @@ export default createStore({
Login,
TimelineSpace,
Preferences,
Settings,
organisms
Settings
}
})

View File

@ -1,12 +0,0 @@
import Toot, { TootState } from './organisms/Toot'
export type OrganismsModuleState = {
Toot: TootState
}
export default {
namespaced: true,
modules: {
Toot
}
}

View File

@ -1,171 +0,0 @@
import generator, { Entity } from 'megalodon'
import { Module, ActionTree } from 'vuex'
import { RootState } from '@/store'
import { MyWindow } from '~/src/types/global'
const win = (window as any) as MyWindow
type VoteParam = {
id: string
choices: Array<number>
}
type ReactionParam = {
status_id: string
native: string
}
export type TootState = {}
const state = (): TootState => ({})
export const ACTION_TYPES = {
REBLOG: 'reblog',
UNREBLOG: 'unreblog',
ADD_FAVOURITE: 'addFavourite',
REMOVE_FAVOURITE: 'addFavourite',
ADD_BOOKMARK: 'addBookmark',
REMOVE_BOOKMARK: 'removeBookmark',
DELETE_TOOT: 'deleteToot',
BLOCK: 'block',
VOTE: 'vote',
REFRESH: 'refresh',
SEND_REACTION: 'sendReaction',
DELETE_REACTION: 'deleteReaction'
}
const actions: ActionTree<TootState, RootState> = {
[ACTION_TYPES.REBLOG]: async ({ rootState }, message: Entity.Status) => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.reblogStatus(message.id)
// API returns new status when reblog.
// Reblog target status is in the data.reblog.
// So I send data.reblog as status for update local timeline.
win.ipcRenderer.send('fav-rt-action-sound')
return res.data.reblog
},
[ACTION_TYPES.UNREBLOG]: async ({ rootState }, message: Entity.Status) => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.unreblogStatus(message.id)
return res.data
},
[ACTION_TYPES.ADD_FAVOURITE]: async ({ rootState }, message: Entity.Status): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.favouriteStatus(message.id)
win.ipcRenderer.send('fav-rt-action-sound')
return res.data
},
removeFavourite: async ({ rootState }, message: Entity.Status): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.unfavouriteStatus(message.id)
return res.data
},
addBookmark: async ({ rootState }, message: Entity.Status): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.bookmarkStatus(message.id)
win.ipcRenderer.send('fav-rt-action-sound')
return res.data
},
removeBookmark: async ({ rootState }, message: Entity.Status): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.unbookmarkStatus(message.id)
return res.data
},
deleteToot: async ({ rootState }, message: Entity.Status) => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
await client.deleteStatus(message.id)
return message
},
block: async ({ rootState }, account: Entity.Account) => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
return client.blockAccount(account.id)
},
vote: async ({ rootState }, params: VoteParam): Promise<Entity.Poll> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.votePoll(params.id, params.choices)
return res.data
},
refresh: async ({ rootState }, id: string): Promise<Entity.Poll> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.getPoll(id)
return res.data
},
sendReaction: async ({ rootState }, params: ReactionParam): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.createEmojiReaction(params.status_id, params.native)
return res.data
},
deleteReaction: async ({ rootState }, params: ReactionParam): Promise<Entity.Status> => {
const client = generator(
rootState.TimelineSpace.server!.sns,
rootState.TimelineSpace.server!.baseURL,
rootState.TimelineSpace.account!.accessToken,
rootState.App.userAgent
)
const res = await client.deleteEmojiReaction(params.status_id, params.native)
return res.data
}
}
const Toot: Module<TootState, RootState> = {
namespaced: true,
state: state,
actions: actions
}
export default Toot

View File

@ -1,4 +1,4 @@
type Account = {
export type ParsedAccount = {
username: string
acct: string
url: string
@ -50,7 +50,7 @@ function parseTag(tagURL: string): string | null {
return res[3]
}
export function findAccount(target: HTMLElement, parentClass = 'toot'): Account | null {
export function findAccount(target: HTMLElement, parentClass = 'toot'): ParsedAccount | null {
const targetClass = target.getAttribute('class')
const link = target as HTMLLinkElement
if (targetClass && targetClass.includes('u-url')) {
@ -79,7 +79,7 @@ export function findAccount(target: HTMLElement, parentClass = 'toot'): Account
return findAccount(parent, parentClass)
}
export function parseMastodonAccount(accountURL: string): Account | null {
export function parseMastodonAccount(accountURL: string): ParsedAccount | null {
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/(@[a-zA-Z0-9-_.]+)$/)
if (!res) {
return null
@ -93,7 +93,7 @@ export function parseMastodonAccount(accountURL: string): Account | null {
}
}
export function parsePleromaAccount(accountURL: string): Account | null {
export function parsePleromaAccount(accountURL: string): ParsedAccount | null {
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/users\/([a-zA-Z0-9-_.]+)$/)
if (!res) {
return null