refs #3301 Rewrite organisms/Toot with composition API
This commit is contained in:
parent
189a35017f
commit
03e600559d
|
@ -32,7 +32,8 @@ I released Whalebird version 2.4.1. In version 2.4.0, Whalebird supports streami
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('tag')
|
||||
it('should find', () => {
|
||||
const res = findTag(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findTag(target!)
|
||||
expect(res).toEqual('whalebird')
|
||||
})
|
||||
})
|
||||
|
@ -48,7 +49,8 @@ I released Whalebird version 2.4.1. In version 2.4.0, Whalebird supports streami
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('tag')
|
||||
it('should find', () => {
|
||||
const res = findTag(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findTag(target!)
|
||||
expect(res).toEqual('whalebird')
|
||||
})
|
||||
})
|
||||
|
@ -65,7 +67,8 @@ describe('findAccount', () => {
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('user')
|
||||
it('should find', () => {
|
||||
const res = findAccount(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findAccount(target!)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res!.username).toEqual('@h3_poteto')
|
||||
expect(res!.acct).toEqual('@h3_poteto@social.mikutter.hachune.net')
|
||||
|
@ -81,7 +84,8 @@ describe('findAccount', () => {
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('user')
|
||||
it('should find', () => {
|
||||
const res = findAccount(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findAccount(target!)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res!.username).toEqual('@h3poteto')
|
||||
expect(res!.acct).toEqual('@h3poteto@pleroma.io')
|
||||
|
@ -97,7 +101,8 @@ describe('findAccount', () => {
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('status')
|
||||
it('should not find', () => {
|
||||
const res = findAccount(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findAccount(target!)
|
||||
expect(res).toBeNull()
|
||||
})
|
||||
})
|
||||
|
@ -111,7 +116,8 @@ describe('findAccount', () => {
|
|||
</html>`).window.document
|
||||
const target = doc.getElementById('status')
|
||||
it('should not find', () => {
|
||||
const res = findAccount(target)
|
||||
expect(target).not.toBeNull()
|
||||
const res = findAccount(target!)
|
||||
expect(res).toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -219,27 +219,38 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed, toRefs } from 'vue'
|
||||
import 'emoji-mart-vue-fast/css/emoji-mart.css'
|
||||
import data from 'emoji-mart-vue-fast/data/all.json'
|
||||
import moment from 'moment'
|
||||
import { mapState } from 'vuex'
|
||||
import { Entity } 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 DisplayStyle from '~/src/constants/displayStyle'
|
||||
import TimeFormat from '~/src/constants/timeFormat'
|
||||
import emojify from '~/src/renderer/utils/emojify'
|
||||
import FailoverImg from '~/src/renderer/components/atoms/FailoverImg'
|
||||
import Poll from '~/src/renderer/components/molecules/Toot/Poll'
|
||||
import LinkPreview from '~/src/renderer/components/molecules/Toot/LinkPreview'
|
||||
import Quote from '@/components/molecules/Toot/Quote'
|
||||
import { setInterval, clearInterval } from 'timers'
|
||||
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 { 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 NEW_ACTION } from '@/store/TimelineSpace/Modals/NewToot'
|
||||
import { ACTION_TYPES as DETAIL_ACTION } from '@/store/TimelineSpace/Contents/SideBar/TootDetail'
|
||||
import { ACTION_TYPES as REPORT_ACTION } from '@/store/TimelineSpace/Modals/Report'
|
||||
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'
|
||||
|
||||
const emojiIndex = new EmojiIndex(data)
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: 'toot',
|
||||
components: {
|
||||
FailoverImg,
|
||||
|
@ -248,22 +259,13 @@ export default {
|
|||
LinkPreview,
|
||||
Quote
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showContent: this.$store.state.App.ignoreCW,
|
||||
showAttachments: this.$store.state.App.ignoreNSFW,
|
||||
hideAllAttachments: this.$store.state.App.hideAllAttachments,
|
||||
now: Date.now(),
|
||||
emojiIndex: emojiIndex
|
||||
}
|
||||
},
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
type: Object as PropType<Entity.Status>,
|
||||
default: {}
|
||||
},
|
||||
filters: {
|
||||
type: Array,
|
||||
type: Array as PropType<Array<Entity.Filter>>,
|
||||
default: []
|
||||
},
|
||||
focused: {
|
||||
|
@ -283,116 +285,94 @@ export default {
|
|||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('App', {
|
||||
displayNameStyle: state => state.displayNameStyle,
|
||||
timeFormat: state => state.timeFormat,
|
||||
language: state => state.language
|
||||
}),
|
||||
...mapState('TimelineSpace', {
|
||||
sns: state => state.sns,
|
||||
account: state => state.account
|
||||
}),
|
||||
...mapState('TimelineSpace/SideMenu', {
|
||||
bookmarkSupported: state => state.enabledTimelines.bookmark
|
||||
}),
|
||||
shortcutEnabled: function () {
|
||||
return this.focused && !this.overlaid
|
||||
},
|
||||
timestamp: function () {
|
||||
return this.parseDatetime(this.originalMessage.created_at, this.now)
|
||||
},
|
||||
readableTimestamp: function () {
|
||||
moment.locale(this.language)
|
||||
return moment(this.originalMessage.created_at).format('LLLL')
|
||||
},
|
||||
originalMessage: function () {
|
||||
if (this.message.reblog && !this.message.quote) {
|
||||
return this.message.reblog
|
||||
setup(props, ctx) {
|
||||
const space = 'organisms/Toot'
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const i18n = useI18next()
|
||||
const { focused, overlaid, message, filters } = toRefs(props)
|
||||
|
||||
const showContent = ref(store.state.App.ignoreCW)
|
||||
const showAttachments = ref(store.state.App.ignoreNSFW)
|
||||
const hideAllAttachments = ref(store.state.App.hideAllAttachments)
|
||||
const now = ref(Date.now())
|
||||
const emojiIndex = new EmojiIndex(data)
|
||||
|
||||
const displayNameStyle = computed(() => store.state.App.displayNameStyle)
|
||||
const timeFormat = computed(() => store.state.App.timeFormat)
|
||||
const language = computed(() => store.state.App.language)
|
||||
const sns = computed(() => store.state.TimelineSpace.sns)
|
||||
const account = computed(() => store.state.TimelineSpace.account)
|
||||
const bookmarkSupported = computed(() => store.state.TimelineSpace.SideMenu.enabledTimelines.bookmark)
|
||||
const shortcutEnabled = computed(() => focused.value && !overlaid.value)
|
||||
const originalMessage = computed(() => {
|
||||
if (message.value.reblog && message.value.quote) {
|
||||
return message.value.reblog
|
||||
} else {
|
||||
return this.message
|
||||
return message.value
|
||||
}
|
||||
},
|
||||
mediaAttachments: function () {
|
||||
return this.originalMessage.media_attachments
|
||||
},
|
||||
reblogsCount: function () {
|
||||
if (this.originalMessage.reblogs_count > 0) {
|
||||
return this.originalMessage.reblogs_count
|
||||
})
|
||||
const timestamp = computed(() => parseDatetime(originalMessage.value.created_at, now.value))
|
||||
const readableTimestamp = computed(() => {
|
||||
moment.locale(language.value)
|
||||
return moment(originalMessage.value.created_at).format('LLLL')
|
||||
})
|
||||
const mediaAttachments = computed(() => {
|
||||
return originalMessage.value.media_attachments
|
||||
})
|
||||
const reblogsCount = computed(() => {
|
||||
if (originalMessage.value.reblogs_count > 0) {
|
||||
return originalMessage.value.reblogs_count
|
||||
}
|
||||
return null
|
||||
},
|
||||
favouritesCount: function () {
|
||||
if (this.originalMessage.favourites_count > 0) {
|
||||
return this.originalMessage.favourites_count
|
||||
})
|
||||
const favouritesCount = computed(() => {
|
||||
if (originalMessage.value.favourites_count > 0) {
|
||||
return originalMessage.value.favourites_count
|
||||
}
|
||||
return null
|
||||
},
|
||||
isMyMessage: function () {
|
||||
return this.$store.state.TimelineSpace.account.accountId === this.originalMessage.account.id
|
||||
},
|
||||
application: function () {
|
||||
const msg = this.originalMessage
|
||||
})
|
||||
const isMyMessage = computed(() => {
|
||||
return store.state.TimelineSpace.account.accountId === originalMessage.value.account.id
|
||||
})
|
||||
const application = computed(() => {
|
||||
const msg = originalMessage.value
|
||||
if (msg.application !== undefined && msg.application !== null) {
|
||||
return msg.application.name
|
||||
}
|
||||
return null
|
||||
},
|
||||
spoilered: function () {
|
||||
return this.originalMessage.spoiler_text.length > 0
|
||||
},
|
||||
isShowContent: function () {
|
||||
return !this.spoilered || this.showContent
|
||||
},
|
||||
poll: function () {
|
||||
return this.originalMessage.poll
|
||||
},
|
||||
sensitive: function () {
|
||||
return (this.hideAllAttachments || this.originalMessage.sensitive) && this.mediaAttachments.length > 0
|
||||
},
|
||||
isShowAttachments: function () {
|
||||
return !this.sensitive || this.showAttachments
|
||||
},
|
||||
filtered: function () {
|
||||
return Filtered(this.originalMessage.content, this.filters)
|
||||
},
|
||||
locked: function () {
|
||||
return this.message.visibility === 'private'
|
||||
},
|
||||
directed: function () {
|
||||
return this.message.visibility === 'direct'
|
||||
},
|
||||
quoteSupported: function () {
|
||||
return QuoteSupported(this.sns, this.account.domain)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.focused) {
|
||||
this.$refs.status.focus()
|
||||
}
|
||||
this.updateInterval = setInterval(() => {
|
||||
this.$data.now = Date.now()
|
||||
}, 60000)
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearInterval(this.updateInterval)
|
||||
},
|
||||
watch: {
|
||||
focused: function (newState, oldState) {
|
||||
if (newState) {
|
||||
this.$nextTick(function () {
|
||||
this.$refs.status.focus()
|
||||
})
|
||||
} else if (oldState && !newState) {
|
||||
this.$nextTick(function () {
|
||||
this.$refs.status.blur()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
username(account) {
|
||||
switch (this.displayNameStyle) {
|
||||
})
|
||||
const spoilered = computed(() => {
|
||||
return originalMessage.value.spoiler_text.length > 0
|
||||
})
|
||||
const isShowContent = computed(() => {
|
||||
return !spoilered.value || showContent.value
|
||||
})
|
||||
const poll = computed(() => {
|
||||
return originalMessage.value.poll
|
||||
})
|
||||
const sensitive = computed(() => {
|
||||
return (hideAllAttachments.value || originalMessage.value.sensitive) && mediaAttachments.value.length > 0
|
||||
})
|
||||
const isShowAttachments = computed(() => {
|
||||
return !sensitive.value || showAttachments.value
|
||||
})
|
||||
const filtered = computed(() => {
|
||||
return Filtered(originalMessage.value.content, filters.value)
|
||||
})
|
||||
const locked = computed(() => {
|
||||
return message.value.visibility === 'private'
|
||||
})
|
||||
const directed = computed(() => {
|
||||
return message.value.visibility === 'direct'
|
||||
})
|
||||
const quoteSupported = computed(() => {
|
||||
return QuoteSupported(sns.value, account.value.domain)
|
||||
})
|
||||
|
||||
const username = (account: Entity.Account) => {
|
||||
switch (displayNameStyle.value) {
|
||||
case DisplayStyle.DisplayNameAndUsername.value:
|
||||
if (account.display_name !== '') {
|
||||
return emojify(account.display_name, account.emojis)
|
||||
|
@ -405,299 +385,312 @@ export default {
|
|||
} else {
|
||||
return account.acct
|
||||
}
|
||||
case DisplayStyle.Username.value:
|
||||
default:
|
||||
return account.acct
|
||||
}
|
||||
},
|
||||
accountName(account) {
|
||||
switch (this.displayNameStyle) {
|
||||
}
|
||||
const accountName = (account: Entity.Account) => {
|
||||
switch (displayNameStyle.value) {
|
||||
case DisplayStyle.DisplayNameAndUsername.value:
|
||||
return `@${account.acct}`
|
||||
case DisplayStyle.DisplayName.value:
|
||||
case DisplayStyle.Username.value:
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
parseDatetime(datetime, epoch) {
|
||||
switch (this.timeFormat) {
|
||||
case TimeFormat.Absolute.value:
|
||||
return moment(datetime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
const parseDatetime = (datetime: string, epoch: number) => {
|
||||
switch (timeFormat.value) {
|
||||
case TimeFormat.Relative.value:
|
||||
moment.locale(this.language)
|
||||
moment.locale(language.value)
|
||||
return moment(datetime).from(epoch)
|
||||
default:
|
||||
return moment(datetime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
},
|
||||
tootClick(e) {
|
||||
const parsedTag = findTag(e.target, 'toot')
|
||||
}
|
||||
const tootClick = (e: MouseEvent) => {
|
||||
const parsedTag = findTag(e.target as HTMLElement, 'toot')
|
||||
if (parsedTag !== null) {
|
||||
const tag = `/${this.$route.params.id}/hashtag/${parsedTag}`
|
||||
this.$router.push({ path: tag })
|
||||
const tag = `/${route.params.id}/hashtag/${parsedTag}`
|
||||
router.push({ path: tag })
|
||||
return tag
|
||||
}
|
||||
const parsedAccount = findAccount(e.target, 'toot')
|
||||
const parsedAccount = findAccount(e.target as HTMLElement, 'toot')
|
||||
if (parsedAccount !== null) {
|
||||
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true)
|
||||
this.$store
|
||||
.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/searchAccount', {
|
||||
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, true)
|
||||
store
|
||||
.dispatch(`TimelineSpace/Contents/SideBar/AccountProfile/${PROFILE_ACTION.SEARCH_ACCOUNT}`, {
|
||||
parsedAccount: parsedAccount,
|
||||
status: this.originalMessage
|
||||
status: originalMessage.value
|
||||
})
|
||||
.then(account => {
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/openAccountComponent')
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/changeAccount', account)
|
||||
store.dispatch(`TimelineSpace/Contents/SideBar/${SIDEBAR_ACTION.OPEN_ACCOUNT_COMPONENT}`)
|
||||
store.dispatch(`TimelineSpace/Contents/SideBar/AccountProfile/${PROFILE_ACTION.CHANGE_ACCOUNT}`, account)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.openLink(e)
|
||||
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', false)
|
||||
openLink(e)
|
||||
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, false)
|
||||
})
|
||||
return parsedAccount.acct
|
||||
}
|
||||
this.openLink(e)
|
||||
},
|
||||
openLink(e) {
|
||||
const link = findLink(e.target, 'toot')
|
||||
openLink(e)
|
||||
return
|
||||
}
|
||||
const openLink = (e: MouseEvent) => {
|
||||
const link = findLink(e.target as HTMLElement, 'toot')
|
||||
if (link !== null) {
|
||||
return window.shell.openExternal(link)
|
||||
return (window as any).shell.openExternal(link)
|
||||
}
|
||||
},
|
||||
openReply() {
|
||||
this.$store.dispatch('TimelineSpace/Modals/NewToot/openReply', this.originalMessage)
|
||||
},
|
||||
openDetail(message) {
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/openTootComponent')
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/TootDetail/changeToot', message)
|
||||
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true)
|
||||
},
|
||||
openBrowser(message) {
|
||||
window.shell.openExternal(message.url)
|
||||
},
|
||||
copyLink(message) {
|
||||
window.clipboard.writeText(message.url, 'toot-link')
|
||||
},
|
||||
reportUser() {
|
||||
this.$store.dispatch('TimelineSpace/Modals/Report/openReport', this.originalMessage)
|
||||
},
|
||||
confirmMute() {
|
||||
this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/changeAccount', this.originalMessage.account)
|
||||
this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/changeModal', true)
|
||||
},
|
||||
block() {
|
||||
this.$store.dispatch('organisms/Toot/block', this.originalMessage.account)
|
||||
},
|
||||
changeReblog(message) {
|
||||
}
|
||||
const openReply = () => {
|
||||
store.dispatch(`TimelineSpace/Modals/NewToot/${NEW_ACTION.OPEN_REPLY}`, originalMessage.value)
|
||||
}
|
||||
const openDetail = (message: Entity.Status) => {
|
||||
store.dispatch(`TimelineSpace/Contents/SideBar/${SIDEBAR_ACTION.OPEN_TOOT_COMPONENT}`)
|
||||
store.dispatch(`TimelineSpace/Contents/SideBar/TootDetail/${DETAIL_ACTION.CHANGE_TOOT}`, message)
|
||||
store.commit(`TimelineSpace/Contents/SideBar/${SIDEBAR_MUTATION.CHANGE_OPEN_SIDEBAR}`, true)
|
||||
}
|
||||
const openBrowser = (message: Entity.Status) => {
|
||||
;(window as any).shell.openExternal(message.url)
|
||||
}
|
||||
const copyLink = (message: Entity.Status) => {
|
||||
;(window as any).clipboard.writeText(message.url, 'toot-link')
|
||||
}
|
||||
const reportUser = () => {
|
||||
store.dispatch(`TimelineSpace/Modals/Report/${REPORT_ACTION.OPEN_REPORT}`, originalMessage.value)
|
||||
}
|
||||
const confirmMute = () => {
|
||||
store.dispatch(`TimelineSpace/Modals/MuteConfirm/${MUTE_ACTION.CHANGE_ACCOUNT}`, originalMessage.value.account)
|
||||
store.dispatch(`TimelineSpace/Modals/MuteConfirm/${MUTE_ACTION.CHANGE_MODAL}`, true)
|
||||
}
|
||||
const block = () => {
|
||||
store.dispatch(`${space}/${ACTION_TYPES.BLOCK}`, originalMessage.value.account)
|
||||
}
|
||||
const changeReblog = (message: Entity.Status) => {
|
||||
if (message.reblogged) {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/unreblog', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.UNREBLOG}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.unreblog_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.unreblog_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/reblog', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.REBLOG}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.reblog_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.reblog_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
changeFavourite(message) {
|
||||
}
|
||||
const changeFavourite = (message: Entity.Status) => {
|
||||
if (message.favourited) {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/removeFavourite', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.REMOVE_FAVOURITE}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.unfavourite_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.unfavourite_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/addFavourite', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.ADD_FAVOURITE}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.favourite_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.favourite_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
changeBookmark(message) {
|
||||
}
|
||||
const changeBookmark = (message: Entity.Status) => {
|
||||
if (message.bookmarked) {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/removeBookmark', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.REMOVE_BOOKMARK}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.unbookmark_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.unbookmark_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/addBookmark', message)
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.ADD_BOOKMARK}`, message)
|
||||
.then(data => {
|
||||
this.$emit('update', data)
|
||||
ctx.emit('update', data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
this.$message({
|
||||
message: this.$t('message.bookmark_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.bookmark_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
openImage(url, rawMediaList) {
|
||||
}
|
||||
const openImage = (url: string, rawMediaList: Array<Entity.Attachment>) => {
|
||||
const mediaList = rawMediaList.map(media => {
|
||||
return media.url
|
||||
})
|
||||
const currentIndex = mediaList.indexOf(url)
|
||||
this.$store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
|
||||
store.dispatch(`TimelineSpace/Modals/ImageViewer/${VIEWER_ACTION.OPEN_MODAL}`, {
|
||||
currentIndex: currentIndex,
|
||||
mediaList: rawMediaList
|
||||
})
|
||||
},
|
||||
openUser(account) {
|
||||
console.log(account)
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/openAccountComponent')
|
||||
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/changeAccount', account)
|
||||
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true)
|
||||
},
|
||||
deleteToot(message) {
|
||||
this.$store
|
||||
.dispatch('organisms/Toot/deleteToot', message)
|
||||
}
|
||||
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)
|
||||
}
|
||||
const deleteToot = (message: Entity.Status) => {
|
||||
store
|
||||
.dispatch(`${space}/${ACTION_TYPES.DELETE_TOOT}`, message)
|
||||
.then(message => {
|
||||
this.$emit('delete', message)
|
||||
ctx.emit('delete', message)
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message({
|
||||
message: this.$t('message.delete_error'),
|
||||
ElMessage({
|
||||
message: i18n.t('message.delete_error'),
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
},
|
||||
emojiText(content, emojis) {
|
||||
}
|
||||
const emojiText = (content: string, emojis: Array<Entity.Emoji>) => {
|
||||
return emojify(content, emojis)
|
||||
},
|
||||
handleTootControl(event) {
|
||||
switch (event.srcKey) {
|
||||
case 'next':
|
||||
this.$emit('focusNext')
|
||||
break
|
||||
case 'prev':
|
||||
this.$emit('focusPrev')
|
||||
break
|
||||
case 'right':
|
||||
this.$emit('focusRight')
|
||||
break
|
||||
case 'left':
|
||||
this.$emit('focusLeft')
|
||||
break
|
||||
case 'reply':
|
||||
this.openReply()
|
||||
break
|
||||
case 'boost':
|
||||
this.changeReblog(this.originalMessage)
|
||||
break
|
||||
case 'fav':
|
||||
this.changeFavourite(this.originalMessage)
|
||||
break
|
||||
case 'open':
|
||||
this.openDetail(this.message)
|
||||
break
|
||||
case 'profile':
|
||||
this.openUser(this.originalMessage.account)
|
||||
break
|
||||
case 'image': {
|
||||
const images = this.mediaAttachments
|
||||
if (images.length === 0) {
|
||||
return 0
|
||||
}
|
||||
this.openImage(images[0].url, images)
|
||||
break
|
||||
}
|
||||
case 'cw':
|
||||
this.toggleSpoiler()
|
||||
this.toggleCW()
|
||||
break
|
||||
}
|
||||
const vote = async choices => {
|
||||
if (!poll.value) {
|
||||
return
|
||||
}
|
||||
},
|
||||
async vote(choices) {
|
||||
const res = await this.$store.dispatch('organisms/Toot/vote', {
|
||||
id: this.poll.id,
|
||||
const res = await store.dispatch(`${space}/${ACTION_TYPES.VOTE}`, {
|
||||
id: poll.value.id,
|
||||
choices: choices
|
||||
})
|
||||
const status = Object.assign({}, this.originalMessage, {
|
||||
const status = Object.assign({}, originalMessage.value, {
|
||||
poll: res
|
||||
})
|
||||
this.$emit('update', status)
|
||||
},
|
||||
async refresh(id) {
|
||||
const res = await this.$store.dispatch('organisms/Toot/refresh', id)
|
||||
const status = Object.assign({}, this.originalMessage, {
|
||||
ctx.emit('update', status)
|
||||
}
|
||||
const refresh = async (id: string) => {
|
||||
const res = await store.dispatch(`${space}/${ACTION_TYPES.REFRESH}`, id)
|
||||
const status = Object.assign({}, originalMessage.value, {
|
||||
poll: res
|
||||
})
|
||||
this.$emit('update', status)
|
||||
},
|
||||
async selectEmoji(emoji) {
|
||||
const status = await this.$store.dispatch('organisms/Toot/sendReaction', {
|
||||
status_id: this.originalMessage.id,
|
||||
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
|
||||
})
|
||||
this.$emit('update', status)
|
||||
},
|
||||
async addReaction(native) {
|
||||
const status = await this.$store.dispatch('organisms/Toot/sendReaction', {
|
||||
status_id: this.originalMessage.id,
|
||||
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
|
||||
})
|
||||
this.$emit('update', status)
|
||||
},
|
||||
async removeReaction(native) {
|
||||
const status = await this.$store.dispatch('organisms/Toot/deleteReaction', {
|
||||
status_id: this.originalMessage.id,
|
||||
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
|
||||
})
|
||||
this.$emit('update', status)
|
||||
},
|
||||
openQuote() {
|
||||
this.$store.dispatch('TimelineSpace/Modals/NewToot/openQuote', this.originalMessage)
|
||||
},
|
||||
toggleSpoiler() {
|
||||
this.showContent = !this.showContent
|
||||
this.$emit('sizeChanged', true)
|
||||
},
|
||||
toggleCW() {
|
||||
this.showAttachments = !this.showAttachments
|
||||
this.$emit('sizeChanged', true)
|
||||
ctx.emit('update', status)
|
||||
}
|
||||
const openQuote = () => {
|
||||
store.dispatch(`TimelineSpace/Modals/NewToot/${NEW_ACTION.OPEN_QUOTE}`, originalMessage.value)
|
||||
}
|
||||
const toggleSpoiler = () => {
|
||||
showContent.value = !showContent.value
|
||||
ctx.emit('sizeChanged', true)
|
||||
}
|
||||
const toggleCW = () => {
|
||||
showAttachments.value = !showAttachments.value
|
||||
ctx.emit('sizeChanged', true)
|
||||
}
|
||||
|
||||
return {
|
||||
emojiIndex,
|
||||
displayNameStyle,
|
||||
timeFormat,
|
||||
language,
|
||||
sns,
|
||||
account,
|
||||
bookmarkSupported,
|
||||
shortcutEnabled,
|
||||
originalMessage,
|
||||
timestamp,
|
||||
readableTimestamp,
|
||||
mediaAttachments,
|
||||
reblogsCount,
|
||||
favouritesCount,
|
||||
isMyMessage,
|
||||
application,
|
||||
spoilered,
|
||||
isShowContent,
|
||||
poll,
|
||||
sensitive,
|
||||
isShowAttachments,
|
||||
filtered,
|
||||
locked,
|
||||
directed,
|
||||
quoteSupported,
|
||||
username,
|
||||
accountName,
|
||||
parseDatetime,
|
||||
tootClick,
|
||||
openReply,
|
||||
openDetail,
|
||||
openBrowser,
|
||||
copyLink,
|
||||
reportUser,
|
||||
confirmMute,
|
||||
block,
|
||||
changeReblog,
|
||||
changeFavourite,
|
||||
changeBookmark,
|
||||
openImage,
|
||||
openUser,
|
||||
deleteToot,
|
||||
emojiText,
|
||||
vote,
|
||||
refresh,
|
||||
selectEmoji,
|
||||
addReaction,
|
||||
removeReaction,
|
||||
openQuote,
|
||||
toggleSpoiler,
|
||||
toggleCW
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -113,13 +113,19 @@ const mutations: MutationTree<TootDetailState> = {
|
|||
}
|
||||
}
|
||||
|
||||
export const ACTION_TYPES = {
|
||||
CHANGE_TOOT: 'changeToot',
|
||||
FETCH_TOOT: 'fetchToot',
|
||||
RELOAD: 'reload'
|
||||
}
|
||||
|
||||
const actions: ActionTree<TootDetailState, RootState> = {
|
||||
changeToot: ({ commit }, message: Entity.Status) => {
|
||||
[ACTION_TYPES.CHANGE_TOOT]: ({ commit }, message: Entity.Status) => {
|
||||
commit(MUTATION_TYPES.UPDATE_ANCESTORS, [])
|
||||
commit(MUTATION_TYPES.UPDATE_DESCENDANTS, [])
|
||||
commit(MUTATION_TYPES.CHANGE_TOOT, message)
|
||||
},
|
||||
fetchToot: async ({ commit, rootState }, message: Entity.Status) => {
|
||||
[ACTION_TYPES.FETCH_TOOT]: async ({ commit, rootState }, message: Entity.Status) => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.sns,
|
||||
rootState.TimelineSpace.account.baseURL,
|
||||
|
@ -131,7 +137,7 @@ const actions: ActionTree<TootDetailState, RootState> = {
|
|||
commit(MUTATION_TYPES.UPDATE_DESCENDANTS, res.data.descendants)
|
||||
return res.data
|
||||
},
|
||||
reload: async ({ state, dispatch }) => {
|
||||
[ACTION_TYPES.RELOAD]: async ({ state, dispatch }) => {
|
||||
await dispatch('fetchToot', state.message)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Module, ActionTree } from 'vuex'
|
|||
import { RootState } from '@/store'
|
||||
import { MyWindow } from '~/src/types/global'
|
||||
|
||||
const win = (window as any) as MyWindow
|
||||
const win = window as any as MyWindow
|
||||
|
||||
type VoteParam = {
|
||||
id: string
|
||||
|
@ -19,8 +19,23 @@ 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> = {
|
||||
reblog: async ({ rootState, dispatch }, message: Entity.Status) => {
|
||||
[ACTION_TYPES.REBLOG]: async ({ rootState, dispatch }, message: Entity.Status) => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.sns,
|
||||
rootState.TimelineSpace.account.baseURL,
|
||||
|
@ -35,7 +50,7 @@ const actions: ActionTree<TootState, RootState> = {
|
|||
dispatch('TimelineSpace/updateTootForAllTimelines', res.data.reblog, { root: true })
|
||||
return res.data.reblog
|
||||
},
|
||||
unreblog: async ({ rootState, dispatch }, message: Entity.Status) => {
|
||||
[ACTION_TYPES.UNREBLOG]: async ({ rootState, dispatch }, message: Entity.Status) => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.sns,
|
||||
rootState.TimelineSpace.account.baseURL,
|
||||
|
@ -46,7 +61,7 @@ const actions: ActionTree<TootState, RootState> = {
|
|||
dispatch('TimelineSpace/updateTootForAllTimelines', res.data, { root: true })
|
||||
return res.data
|
||||
},
|
||||
addFavourite: async ({ rootState, dispatch }, message: Entity.Status): Promise<Entity.Status> => {
|
||||
[ACTION_TYPES.ADD_FAVOURITE]: async ({ rootState, dispatch }, message: Entity.Status): Promise<Entity.Status> => {
|
||||
const client = generator(
|
||||
rootState.TimelineSpace.sns,
|
||||
rootState.TimelineSpace.account.baseURL,
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
type Account = {
|
||||
username: string
|
||||
acct: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export declare function findLink(target: HTMLElement | null, parentClass?: string): string | null
|
||||
|
||||
export declare function findTag(target: HTMLElement | null, parentClass?: string): string | null
|
||||
|
||||
export declare function parseTag(tagURL: string): string
|
||||
|
||||
export declare function findAccount(target: HTMLElement | null, parentClass?: string): Account | null
|
||||
|
||||
export declare function parseMastodonAccount(accountURL: string): Account
|
||||
|
||||
export declare function parsePleromaAccount(accountURL: string): Account
|
|
@ -1,83 +0,0 @@
|
|||
export function findLink(target, parentClass = 'toot') {
|
||||
if (target.localName === 'a') {
|
||||
return target.href
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
if (target.parentNode.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findLink(target.parentNode, parentClass)
|
||||
}
|
||||
|
||||
export function findTag(target, parentClass = 'toot') {
|
||||
if (target.getAttribute('class') && target.getAttribute('class').includes('hashtag')) {
|
||||
return parseTag(target.href)
|
||||
}
|
||||
// In Pleroma, link does not have class.
|
||||
// So I have to check URL.
|
||||
if (target.href && target.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/(tag|tags)\/.+/)) {
|
||||
return parseTag(target.href)
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
if (target.parentNode.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findTag(target.parentNode, parentClass)
|
||||
}
|
||||
|
||||
export function parseTag(tagURL) {
|
||||
const res = tagURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/(tag|tags)\/(.+)/)
|
||||
return res[3]
|
||||
}
|
||||
|
||||
export function findAccount(target, parentClass = 'toot') {
|
||||
if (target.getAttribute('class') && target.getAttribute('class').includes('u-url')) {
|
||||
if (target.href && target.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/users\/[a-zA-Z0-9-_.]+$/)) {
|
||||
return parsePleromaAccount(target.href)
|
||||
} else {
|
||||
return parseMastodonAccount(target.href)
|
||||
}
|
||||
}
|
||||
// In Pleroma, link does not have class.
|
||||
// So we have to check URL.
|
||||
if (target.href && target.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/@[a-zA-Z0-9-_.]+$/)) {
|
||||
return parseMastodonAccount(target.href)
|
||||
}
|
||||
// Toot URL of Pleroma does not contain @.
|
||||
if (target.href && target.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/users\/[a-zA-Z0-9-_.]+$/)) {
|
||||
return parsePleromaAccount(target.href)
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
if (target.parentNode.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findAccount(target.parentNode, parentClass)
|
||||
}
|
||||
|
||||
export function parseMastodonAccount(accountURL) {
|
||||
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/(@[a-zA-Z0-9-_.]+)$/)
|
||||
const domainName = res[1]
|
||||
const accountName = res[2]
|
||||
return {
|
||||
username: accountName,
|
||||
acct: `${accountName}@${domainName}`,
|
||||
url: accountURL
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePleromaAccount(accountURL) {
|
||||
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/users\/([a-zA-Z0-9-_.]+)$/)
|
||||
const domainName = res[1]
|
||||
const accountName = res[2]
|
||||
return {
|
||||
username: `@${accountName}`,
|
||||
acct: `@${accountName}@${domainName}`,
|
||||
url: accountURL
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
type Account = {
|
||||
username: string
|
||||
acct: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export function findLink(target: HTMLElement | null, parentClass = 'toot'): string | null {
|
||||
if (!target) {
|
||||
return null
|
||||
}
|
||||
if (target.localName === 'a') {
|
||||
return (target as HTMLLinkElement).href
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
const parent = target.parentNode as HTMLElement
|
||||
if (parent.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findLink(parent, parentClass)
|
||||
}
|
||||
|
||||
export function findTag(target: HTMLElement, parentClass = 'toot'): string | null {
|
||||
const targetClass = target.getAttribute('class')
|
||||
if (targetClass && targetClass.includes('hashtag')) {
|
||||
return parseTag((target as HTMLLinkElement).href)
|
||||
}
|
||||
// In Pleroma, link does not have class.
|
||||
// So I have to check URL.
|
||||
const link = target as HTMLLinkElement
|
||||
if (link.href && link.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/(tag|tags)\/.+/)) {
|
||||
return parseTag(link.href)
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
const parent = target.parentNode as HTMLElement
|
||||
if (parent.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findTag(parent, parentClass)
|
||||
}
|
||||
|
||||
function parseTag(tagURL: string): string | null {
|
||||
const res = tagURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/(tag|tags)\/(.+)/)
|
||||
if (!res) {
|
||||
return null
|
||||
}
|
||||
return res[3]
|
||||
}
|
||||
|
||||
export function findAccount(target: HTMLElement, parentClass = 'toot'): Account | null {
|
||||
const targetClass = target.getAttribute('class')
|
||||
const link = target as HTMLLinkElement
|
||||
if (targetClass && targetClass.includes('u-url')) {
|
||||
if (link.href && link.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/users\/[a-zA-Z0-9-_.]+$/)) {
|
||||
return parsePleromaAccount(link.href)
|
||||
} else {
|
||||
return parseMastodonAccount(link.href)
|
||||
}
|
||||
}
|
||||
// In Pleroma, link does not have class.
|
||||
// So we have to check URL.
|
||||
if (link.href && link.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/@[a-zA-Z0-9-_.]+$/)) {
|
||||
return parseMastodonAccount(link.href)
|
||||
}
|
||||
// Toot URL of Pleroma does not contain @.
|
||||
if (link.href && link.href.match(/^https:\/\/[a-zA-Z0-9-.]+\/users\/[a-zA-Z0-9-_.]+$/)) {
|
||||
return parsePleromaAccount(link.href)
|
||||
}
|
||||
if (target.parentNode === undefined || target.parentNode === null) {
|
||||
return null
|
||||
}
|
||||
const parent = target.parentNode as HTMLElement
|
||||
if (parent.getAttribute('class') === parentClass) {
|
||||
return null
|
||||
}
|
||||
return findAccount(parent, parentClass)
|
||||
}
|
||||
|
||||
export function parseMastodonAccount(accountURL: string): Account | null {
|
||||
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/(@[a-zA-Z0-9-_.]+)$/)
|
||||
if (!res) {
|
||||
return null
|
||||
}
|
||||
const domainName = res[1]
|
||||
const accountName = res[2]
|
||||
return {
|
||||
username: accountName,
|
||||
acct: `${accountName}@${domainName}`,
|
||||
url: accountURL
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePleromaAccount(accountURL: string): Account | null {
|
||||
const res = accountURL.match(/^https:\/\/([a-zA-Z0-9-.]+)\/users\/([a-zA-Z0-9-_.]+)$/)
|
||||
if (!res) {
|
||||
return null
|
||||
}
|
||||
const domainName = res[1]
|
||||
const accountName = res[2]
|
||||
return {
|
||||
username: `@${accountName}`,
|
||||
acct: `@${accountName}@${domainName}`,
|
||||
url: accountURL
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue