forked from Mirrors/elk
refactor: no reactivity transform (#2600)
This commit is contained in:
parent
b9394c2fa5
commit
ccfa7a8d10
102 changed files with 649 additions and 652 deletions
|
@ -6,8 +6,8 @@ defineProps<{
|
||||||
square?: boolean
|
square?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const loaded = $ref(false)
|
const loaded = ref(false)
|
||||||
const error = $ref(false)
|
const error = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -5,7 +5,7 @@ defineOptions({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { account, as = 'div' } = $defineProps<{
|
const { account, as = 'div' } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
as?: string
|
as?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
|
@ -10,35 +10,35 @@ const { account, command, context, ...props } = defineProps<{
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const isSelf = $(useSelfAccount(() => account))
|
const isSelf = useSelfAccount(() => account)
|
||||||
const enable = $computed(() => !isSelf && currentUser.value)
|
const enable = computed(() => !isSelf.value && currentUser.value)
|
||||||
const relationship = $computed(() => props.relationship || useRelationship(account).value)
|
const relationship = computed(() => props.relationship || useRelationship(account).value)
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
async function unblock() {
|
async function unblock() {
|
||||||
relationship!.blocking = false
|
relationship.value!.blocking = false
|
||||||
try {
|
try {
|
||||||
const newRel = await client.v1.accounts.$select(account.id).unblock()
|
const newRel = await client.value.v1.accounts.$select(account.id).unblock()
|
||||||
Object.assign(relationship!, newRel)
|
Object.assign(relationship!, newRel)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
// TODO error handling
|
// TODO error handling
|
||||||
relationship!.blocking = true
|
relationship.value!.blocking = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unmute() {
|
async function unmute() {
|
||||||
relationship!.muting = false
|
relationship.value!.muting = false
|
||||||
try {
|
try {
|
||||||
const newRel = await client.v1.accounts.$select(account.id).unmute()
|
const newRel = await client.value.v1.accounts.$select(account.id).unmute()
|
||||||
Object.assign(relationship!, newRel)
|
Object.assign(relationship!, newRel)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
// TODO error handling
|
// TODO error handling
|
||||||
relationship!.muting = true
|
relationship.value!.muting = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,21 +46,21 @@ useCommand({
|
||||||
scope: 'Actions',
|
scope: 'Actions',
|
||||||
order: -2,
|
order: -2,
|
||||||
visible: () => command && enable,
|
visible: () => command && enable,
|
||||||
name: () => `${relationship?.following ? t('account.unfollow') : t('account.follow')} ${getShortHandle(account)}`,
|
name: () => `${relationship.value?.following ? t('account.unfollow') : t('account.follow')} ${getShortHandle(account)}`,
|
||||||
icon: 'i-ri:star-line',
|
icon: 'i-ri:star-line',
|
||||||
onActivate: () => toggleFollowAccount(relationship!, account),
|
onActivate: () => toggleFollowAccount(relationship.value!, account),
|
||||||
})
|
})
|
||||||
|
|
||||||
const buttonStyle = $computed(() => {
|
const buttonStyle = computed(() => {
|
||||||
if (relationship?.blocking)
|
if (relationship.value?.blocking)
|
||||||
return 'text-inverted bg-red border-red'
|
return 'text-inverted bg-red border-red'
|
||||||
|
|
||||||
if (relationship?.muting)
|
if (relationship.value?.muting)
|
||||||
return 'text-base bg-card border-base'
|
return 'text-base bg-card border-base'
|
||||||
|
|
||||||
// If following, use a label style with a strong border for Mutuals
|
// If following, use a label style with a strong border for Mutuals
|
||||||
if (relationship ? relationship.following : context === 'following')
|
if (relationship.value ? relationship.value.following : context === 'following')
|
||||||
return `text-base ${relationship?.followedBy ? 'border-strong' : 'border-base'}`
|
return `text-base ${relationship.value?.followedBy ? 'border-strong' : 'border-base'}`
|
||||||
|
|
||||||
// If not following, use a button style
|
// If not following, use a button style
|
||||||
return 'text-inverted bg-primary border-primary'
|
return 'text-inverted bg-primary border-primary'
|
||||||
|
|
|
@ -5,32 +5,32 @@ const { account, ...props } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
relationship?: mastodon.v1.Relationship
|
relationship?: mastodon.v1.Relationship
|
||||||
}>()
|
}>()
|
||||||
const relationship = $computed(() => props.relationship || useRelationship(account).value)
|
const relationship = computed(() => props.relationship || useRelationship(account).value)
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
async function authorizeFollowRequest() {
|
async function authorizeFollowRequest() {
|
||||||
relationship!.requestedBy = false
|
relationship.value!.requestedBy = false
|
||||||
relationship!.followedBy = true
|
relationship.value!.followedBy = true
|
||||||
try {
|
try {
|
||||||
const newRel = await client.v1.followRequests.$select(account.id).authorize()
|
const newRel = await client.value.v1.followRequests.$select(account.id).authorize()
|
||||||
Object.assign(relationship!, newRel)
|
Object.assign(relationship!, newRel)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
relationship!.requestedBy = true
|
relationship.value!.requestedBy = true
|
||||||
relationship!.followedBy = false
|
relationship.value!.followedBy = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rejectFollowRequest() {
|
async function rejectFollowRequest() {
|
||||||
relationship!.requestedBy = false
|
relationship.value!.requestedBy = false
|
||||||
try {
|
try {
|
||||||
const newRel = await client.v1.followRequests.$select(account.id).reject()
|
const newRel = await client.value.v1.followRequests.$select(account.id).reject()
|
||||||
Object.assign(relationship!, newRel)
|
Object.assign(relationship!, newRel)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
relationship!.requestedBy = true
|
relationship.value!.requestedBy = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { account } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const serverName = $computed(() => getServerName(account))
|
const serverName = computed(() => getServerName(account))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -6,22 +6,22 @@ const { account } = defineProps<{
|
||||||
command?: boolean
|
command?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const createdAt = $(useFormattedDateTime(() => account.createdAt, {
|
const createdAt = useFormattedDateTime(() => account.createdAt, {
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
}))
|
})
|
||||||
|
|
||||||
const relationship = $(useRelationship(account))
|
const relationship = useRelationship(account)
|
||||||
|
|
||||||
const namedFields = ref<mastodon.v1.AccountField[]>([])
|
const namedFields = ref<mastodon.v1.AccountField[]>([])
|
||||||
const iconFields = ref<mastodon.v1.AccountField[]>([])
|
const iconFields = ref<mastodon.v1.AccountField[]>([])
|
||||||
const isEditingPersonalNote = ref<boolean>(false)
|
const isEditingPersonalNote = ref<boolean>(false)
|
||||||
const hasHeader = $computed(() => !account.header.endsWith('/original/missing.png'))
|
const hasHeader = computed(() => !account.header.endsWith('/original/missing.png'))
|
||||||
const isCopied = ref<boolean>(false)
|
const isCopied = ref<boolean>(false)
|
||||||
|
|
||||||
function getFieldIconTitle(fieldName: string) {
|
function getFieldIconTitle(fieldName: string) {
|
||||||
|
@ -29,7 +29,7 @@ function getFieldIconTitle(fieldName: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotificationIconTitle() {
|
function getNotificationIconTitle() {
|
||||||
return relationship?.notifying ? t('account.notifications_on_post_disable', { username: `@${account.username}` }) : t('account.notifications_on_post_enable', { username: `@${account.username}` })
|
return relationship.value?.notifying ? t('account.notifications_on_post_disable', { username: `@${account.username}` }) : t('account.notifications_on_post_enable', { username: `@${account.username}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
function previewHeader() {
|
function previewHeader() {
|
||||||
|
@ -51,14 +51,14 @@ function previewAvatar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleNotifications() {
|
async function toggleNotifications() {
|
||||||
relationship!.notifying = !relationship?.notifying
|
relationship.value!.notifying = !relationship.value?.notifying
|
||||||
try {
|
try {
|
||||||
const newRel = await client.v1.accounts.$select(account.id).follow({ notify: relationship?.notifying })
|
const newRel = await client.value.v1.accounts.$select(account.id).follow({ notify: relationship.value?.notifying })
|
||||||
Object.assign(relationship!, newRel)
|
Object.assign(relationship!, newRel)
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
// TODO error handling
|
// TODO error handling
|
||||||
relationship!.notifying = !relationship?.notifying
|
relationship.value!.notifying = !relationship.value?.notifying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,35 +75,35 @@ watchEffect(() => {
|
||||||
})
|
})
|
||||||
icons.push({
|
icons.push({
|
||||||
name: 'Joined',
|
name: 'Joined',
|
||||||
value: createdAt,
|
value: createdAt.value,
|
||||||
})
|
})
|
||||||
|
|
||||||
namedFields.value = named
|
namedFields.value = named
|
||||||
iconFields.value = icons
|
iconFields.value = icons
|
||||||
})
|
})
|
||||||
|
|
||||||
const personalNoteDraft = ref(relationship?.note ?? '')
|
const personalNoteDraft = ref(relationship.value?.note ?? '')
|
||||||
watch($$(relationship), (relationship, oldValue) => {
|
watch(relationship, (relationship, oldValue) => {
|
||||||
if (!oldValue && relationship)
|
if (!oldValue && relationship)
|
||||||
personalNoteDraft.value = relationship.note ?? ''
|
personalNoteDraft.value = relationship.note ?? ''
|
||||||
})
|
})
|
||||||
|
|
||||||
async function editNote(event: Event) {
|
async function editNote(event: Event) {
|
||||||
if (!event.target || !('value' in event.target) || !relationship)
|
if (!event.target || !('value' in event.target) || !relationship.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
const newNote = event.target?.value as string
|
const newNote = event.target?.value as string
|
||||||
|
|
||||||
if (relationship.note?.trim() === newNote.trim())
|
if (relationship.value.note?.trim() === newNote.trim())
|
||||||
return
|
return
|
||||||
|
|
||||||
const newNoteApiResult = await client.v1.accounts.$select(account.id).note.create({ comment: newNote })
|
const newNoteApiResult = await client.value.v1.accounts.$select(account.id).note.create({ comment: newNote })
|
||||||
relationship.note = newNoteApiResult.note
|
relationship.value.note = newNoteApiResult.note
|
||||||
personalNoteDraft.value = relationship.note ?? ''
|
personalNoteDraft.value = relationship.value.note ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSelf = $(useSelfAccount(() => account))
|
const isSelf = useSelfAccount(() => account)
|
||||||
const isNotifiedOnPost = $computed(() => !!relationship?.notifying)
|
const isNotifiedOnPost = computed(() => !!relationship.value?.notifying)
|
||||||
|
|
||||||
const personalNoteMaxLength = 2000
|
const personalNoteMaxLength = 2000
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { account } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const relationship = $(useRelationship(account))
|
const relationship = useRelationship(account)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -11,12 +11,12 @@ const emit = defineEmits<{
|
||||||
(evt: 'removeNote'): void
|
(evt: 'removeNote'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let relationship = $(useRelationship(account))
|
const relationship = useRelationship(account)
|
||||||
|
|
||||||
const isSelf = $(useSelfAccount(() => account))
|
const isSelf = useSelfAccount(() => account)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
||||||
const { share, isSupported: isShareSupported } = useShare()
|
const { share, isSupported: isShareSupported } = useShare()
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ function shareAccount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleReblogs() {
|
async function toggleReblogs() {
|
||||||
if (!relationship!.showingReblogs && await openConfirmDialog({
|
if (!relationship.value!.showingReblogs && await openConfirmDialog({
|
||||||
title: t('confirm.show_reblogs.title'),
|
title: t('confirm.show_reblogs.title'),
|
||||||
description: t('confirm.show_reblogs.description', [account.acct]),
|
description: t('confirm.show_reblogs.description', [account.acct]),
|
||||||
confirm: t('confirm.show_reblogs.confirm'),
|
confirm: t('confirm.show_reblogs.confirm'),
|
||||||
|
@ -33,8 +33,8 @@ async function toggleReblogs() {
|
||||||
}) !== 'confirm')
|
}) !== 'confirm')
|
||||||
return
|
return
|
||||||
|
|
||||||
const showingReblogs = !relationship?.showingReblogs
|
const showingReblogs = !relationship.value?.showingReblogs
|
||||||
relationship = await client.v1.accounts.$select(account.id).follow({ reblogs: showingReblogs })
|
relationship.value = await client.value.v1.accounts.$select(account.id).follow({ reblogs: showingReblogs })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addUserNote() {
|
async function addUserNote() {
|
||||||
|
@ -42,11 +42,11 @@ async function addUserNote() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeUserNote() {
|
async function removeUserNote() {
|
||||||
if (!relationship!.note || relationship!.note.length === 0)
|
if (!relationship.value!.note || relationship.value!.note.length === 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
const newNote = await client.v1.accounts.$select(account.id).note.create({ comment: '' })
|
const newNote = await client.value.v1.accounts.$select(account.id).note.create({ comment: '' })
|
||||||
relationship!.note = newNote.note
|
relationship.value!.note = newNote.note
|
||||||
emit('removeNote')
|
emit('removeNote')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,10 +8,10 @@ const { paginator, account, context } = defineProps<{
|
||||||
relationshipContext?: 'followedBy' | 'following'
|
relationshipContext?: 'followedBy' | 'following'
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const fallbackContext = $computed(() => {
|
const fallbackContext = computed(() => {
|
||||||
return ['following', 'followers'].includes(context!)
|
return ['following', 'followers'].includes(context!)
|
||||||
})
|
})
|
||||||
const showOriginSite = $computed(() =>
|
const showOriginSite = computed(() =>
|
||||||
account && account.id !== currentUser.value?.account.id && getServerName(account) !== currentServer.value,
|
account && account.id !== currentUser.value?.account.id && getServerName(account) !== currentServer.value,
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,15 +4,15 @@ import type { CommonRouteTabOption } from '../common/CommonRouteTabs.vue'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const server = $(computedEager(() => route.params.server as string))
|
const server = computedEager(() => route.params.server as string)
|
||||||
const account = $(computedEager(() => route.params.account as string))
|
const account = computedEager(() => route.params.account as string)
|
||||||
|
|
||||||
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
const tabs = computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
name: 'account-index',
|
name: 'account-index',
|
||||||
to: {
|
to: {
|
||||||
name: 'account-index',
|
name: 'account-index',
|
||||||
params: { server, account },
|
params: { server: server.value, account: account.value },
|
||||||
},
|
},
|
||||||
display: t('tab.posts'),
|
display: t('tab.posts'),
|
||||||
icon: 'i-ri:file-list-2-line',
|
icon: 'i-ri:file-list-2-line',
|
||||||
|
@ -21,7 +21,7 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
name: 'account-replies',
|
name: 'account-replies',
|
||||||
to: {
|
to: {
|
||||||
name: 'account-replies',
|
name: 'account-replies',
|
||||||
params: { server, account },
|
params: { server: server.value, account: account.value },
|
||||||
},
|
},
|
||||||
display: t('tab.posts_with_replies'),
|
display: t('tab.posts_with_replies'),
|
||||||
icon: 'i-ri:chat-1-line',
|
icon: 'i-ri:chat-1-line',
|
||||||
|
@ -30,7 +30,7 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
name: 'account-media',
|
name: 'account-media',
|
||||||
to: {
|
to: {
|
||||||
name: 'account-media',
|
name: 'account-media',
|
||||||
params: { server, account },
|
params: { server: server.value, account: account.value },
|
||||||
},
|
},
|
||||||
display: t('tab.media'),
|
display: t('tab.media'),
|
||||||
icon: 'i-ri:camera-2-line',
|
icon: 'i-ri:camera-2-line',
|
||||||
|
|
|
@ -11,16 +11,16 @@ const localeMap = (locales.value as LocaleObject[]).reduce((acc, l) => {
|
||||||
return acc
|
return acc
|
||||||
}, {} as Record<string, string>)
|
}, {} as Record<string, string>)
|
||||||
|
|
||||||
let ariaLive = $ref<AriaLive>('polite')
|
const ariaLive = ref<AriaLive>('polite')
|
||||||
let ariaMessage = $ref<string>('')
|
const ariaMessage = ref<string>('')
|
||||||
|
|
||||||
function onMessage(event: AriaAnnounceType, message?: string) {
|
function onMessage(event: AriaAnnounceType, message?: string) {
|
||||||
if (event === 'announce')
|
if (event === 'announce')
|
||||||
ariaMessage = message!
|
ariaMessage.value = message!
|
||||||
else if (event === 'mute')
|
else if (event === 'mute')
|
||||||
ariaLive = 'off'
|
ariaLive.value = 'off'
|
||||||
else
|
else
|
||||||
ariaLive = 'polite'
|
ariaLive.value = 'polite'
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(locale, (l, ol) => {
|
watch(locale, (l, ol) => {
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ResolvedCommand } from '~/composables/command'
|
import type { ResolvedCommand } from '~/composables/command'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: 'activate'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
cmd,
|
cmd,
|
||||||
index,
|
index,
|
||||||
active = false,
|
active = false,
|
||||||
} = $defineProps<{
|
} = defineProps<{
|
||||||
cmd: ResolvedCommand
|
cmd: ResolvedCommand
|
||||||
index: number
|
index: number
|
||||||
active?: boolean
|
active?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'activate'): void
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const isMac = useIsMac()
|
const isMac = useIsMac()
|
||||||
|
|
||||||
const keys = $computed(() => props.name.toLowerCase().split('+'))
|
const keys = computed(() => props.name.toLowerCase().split('+'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -10,21 +10,21 @@ const registry = useCommandRegistry()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const inputEl = $ref<HTMLInputElement>()
|
const inputEl = ref<HTMLInputElement>()
|
||||||
const resultEl = $ref<HTMLDivElement>()
|
const resultEl = ref<HTMLDivElement>()
|
||||||
|
|
||||||
const scopes = $ref<CommandScope[]>([])
|
const scopes = ref<CommandScope[]>([])
|
||||||
let input = $(commandPanelInput)
|
const input = commandPanelInput
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
inputEl?.focus()
|
inputEl.value?.focus()
|
||||||
})
|
})
|
||||||
|
|
||||||
const commandMode = $computed(() => input.startsWith('>'))
|
const commandMode = computed(() => input.value.startsWith('>'))
|
||||||
|
|
||||||
const query = $computed(() => commandMode ? '' : input.trim())
|
const query = computed(() => commandMode ? '' : input.value.trim())
|
||||||
|
|
||||||
const { accounts, hashtags, loading } = useSearch($$(query))
|
const { accounts, hashtags, loading } = useSearch(query)
|
||||||
|
|
||||||
function toSearchQueryResultItem(search: SearchResultType): QueryResultItem {
|
function toSearchQueryResultItem(search: SearchResultType): QueryResultItem {
|
||||||
return {
|
return {
|
||||||
|
@ -35,8 +35,8 @@ function toSearchQueryResultItem(search: SearchResultType): QueryResultItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchResult = $computed<QueryResult>(() => {
|
const searchResult = computed<QueryResult>(() => {
|
||||||
if (query.length === 0 || loading.value)
|
if (query.value.length === 0 || loading.value)
|
||||||
return { length: 0, items: [], grouped: {} as any }
|
return { length: 0, items: [], grouped: {} as any }
|
||||||
|
|
||||||
// TODO extract this scope
|
// TODO extract this scope
|
||||||
|
@ -61,22 +61,22 @@ const searchResult = $computed<QueryResult>(() => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = $computed<QueryResult>(() => commandMode
|
const result = computed<QueryResult>(() => commandMode
|
||||||
? registry.query(scopes.map(s => s.id).join('.'), input.slice(1).trim())
|
? registry.query(scopes.value.map(s => s.id).join('.'), input.value.slice(1).trim())
|
||||||
: searchResult,
|
: searchResult.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
const isMac = useIsMac()
|
const isMac = useIsMac()
|
||||||
const modifierKeyName = $computed(() => isMac.value ? '⌘' : 'Ctrl')
|
const modifierKeyName = computed(() => isMac.value ? '⌘' : 'Ctrl')
|
||||||
|
|
||||||
let active = $ref(0)
|
const active = ref(0)
|
||||||
watch($$(result), (n, o) => {
|
watch(result, (n, o) => {
|
||||||
if (n.length !== o.length || !n.items.every((i, idx) => i === o.items[idx]))
|
if (n.length !== o.length || !n.items.every((i, idx) => i === o.items[idx]))
|
||||||
active = 0
|
active.value = 0
|
||||||
})
|
})
|
||||||
|
|
||||||
function findItemEl(index: number) {
|
function findItemEl(index: number) {
|
||||||
return resultEl?.querySelector(`[data-index="${index}"]`) as HTMLDivElement | null
|
return resultEl.value?.querySelector(`[data-index="${index}"]`) as HTMLDivElement | null
|
||||||
}
|
}
|
||||||
function onCommandActivate(item: QueryResultItem) {
|
function onCommandActivate(item: QueryResultItem) {
|
||||||
if (item.onActivate) {
|
if (item.onActivate) {
|
||||||
|
@ -84,14 +84,14 @@ function onCommandActivate(item: QueryResultItem) {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
else if (item.onComplete) {
|
else if (item.onComplete) {
|
||||||
scopes.push(item.onComplete())
|
scopes.value.push(item.onComplete())
|
||||||
input = '> '
|
input.value = '> '
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onCommandComplete(item: QueryResultItem) {
|
function onCommandComplete(item: QueryResultItem) {
|
||||||
if (item.onComplete) {
|
if (item.onComplete) {
|
||||||
scopes.push(item.onComplete())
|
scopes.value.push(item.onComplete())
|
||||||
input = '> '
|
input.value = '> '
|
||||||
}
|
}
|
||||||
else if (item.onActivate) {
|
else if (item.onActivate) {
|
||||||
item.onActivate()
|
item.onActivate()
|
||||||
|
@ -105,9 +105,9 @@ function intoView(index: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActive(index: number) {
|
function setActive(index: number) {
|
||||||
const len = result.length
|
const len = result.value.length
|
||||||
active = (index + len) % len
|
active.value = (index + len) % len
|
||||||
intoView(active)
|
intoView(active.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(e: KeyboardEvent) {
|
function onKeyDown(e: KeyboardEvent) {
|
||||||
|
@ -118,7 +118,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
break
|
break
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
setActive(active - 1)
|
setActive(active.value - 1)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
break
|
break
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
setActive(active + 1)
|
setActive(active.value + 1)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -136,9 +136,9 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
case 'Home': {
|
case 'Home': {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
active = 0
|
active.value = 0
|
||||||
|
|
||||||
intoView(active)
|
intoView(active.value)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
case 'End': {
|
case 'End': {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
setActive(result.length - 1)
|
setActive(result.value.length - 1)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
case 'Enter': {
|
case 'Enter': {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const cmd = result.items[active]
|
const cmd = result.value.items[active.value]
|
||||||
if (cmd)
|
if (cmd)
|
||||||
onCommandActivate(cmd)
|
onCommandActivate(cmd)
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
case 'Tab': {
|
case 'Tab': {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const cmd = result.items[active]
|
const cmd = result.value.items[active.value]
|
||||||
if (cmd)
|
if (cmd)
|
||||||
onCommandComplete(cmd)
|
onCommandComplete(cmd)
|
||||||
|
|
||||||
|
@ -172,9 +172,9 @@ function onKeyDown(e: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'Backspace': {
|
case 'Backspace': {
|
||||||
if (input === '>' && scopes.length) {
|
if (input.value === '>' && scopes.value.length) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
scopes.pop()
|
scopes.value.pop()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ defineSlots<{
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const nuxtApp = useNuxtApp()
|
const nuxtApp = useNuxtApp()
|
||||||
|
|
||||||
const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, $$(stream), preprocess)
|
const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, toRef(() => stream), preprocess)
|
||||||
|
|
||||||
nuxtApp.hook('elk-logo:click', () => {
|
nuxtApp.hook('elk-logo:click', () => {
|
||||||
update()
|
update()
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RouteLocationRaw } from 'vue-router'
|
import type { RouteLocationRaw } from 'vue-router'
|
||||||
|
|
||||||
|
const { options, command, replace, preventScrollTop = false, moreOptions } = defineProps<{
|
||||||
|
options: CommonRouteTabOption[]
|
||||||
|
moreOptions?: CommonRouteTabMoreOption
|
||||||
|
command?: boolean
|
||||||
|
replace?: boolean
|
||||||
|
preventScrollTop?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
export interface CommonRouteTabOption {
|
export interface CommonRouteTabOption {
|
||||||
|
@ -18,14 +26,6 @@ export interface CommonRouteTabMoreOption {
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
match?: boolean
|
match?: boolean
|
||||||
}
|
}
|
||||||
const { options, command, replace, preventScrollTop = false, moreOptions } = $defineProps<{
|
|
||||||
options: CommonRouteTabOption[]
|
|
||||||
moreOptions?: CommonRouteTabMoreOption
|
|
||||||
command?: boolean
|
|
||||||
replace?: boolean
|
|
||||||
preventScrollTop?: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
useCommands(() => command
|
useCommands(() => command
|
||||||
|
|
|
@ -10,7 +10,7 @@ const { options, command } = defineProps<{
|
||||||
|
|
||||||
const modelValue = defineModel<string>({ required: true })
|
const modelValue = defineModel<string>({ required: true })
|
||||||
|
|
||||||
const tabs = $computed(() => {
|
const tabs = computed(() => {
|
||||||
return options.map((option) => {
|
return options.map((option) => {
|
||||||
if (typeof option === 'string')
|
if (typeof option === 'string')
|
||||||
return { name: option, display: option }
|
return { name: option, display: option }
|
||||||
|
@ -24,7 +24,7 @@ function toValidName(otpion: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
useCommands(() => command
|
useCommands(() => command
|
||||||
? tabs.map(tab => ({
|
? tabs.value.map(tab => ({
|
||||||
scope: 'Tabs',
|
scope: 'Tabs',
|
||||||
|
|
||||||
name: tab.display,
|
name: tab.display,
|
||||||
|
|
|
@ -4,15 +4,15 @@ import type { mastodon } from 'masto'
|
||||||
const {
|
const {
|
||||||
history,
|
history,
|
||||||
maxDay = 2,
|
maxDay = 2,
|
||||||
} = $defineProps<{
|
} = defineProps<{
|
||||||
history: mastodon.v1.TagHistory[]
|
history: mastodon.v1.TagHistory[]
|
||||||
maxDay?: number
|
maxDay?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const ongoingHot = $computed(() => history.slice(0, maxDay))
|
const ongoingHot = computed(() => history.slice(0, maxDay))
|
||||||
|
|
||||||
const people = $computed(() =>
|
const people = computed(() =>
|
||||||
ongoingHot.reduce((total: number, item) => total + (Number(item.accounts) || 0), 0),
|
ongoingHot.value.reduce((total: number, item) => total + (Number(item.accounts) || 0), 0),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,22 @@ const {
|
||||||
history,
|
history,
|
||||||
width = 60,
|
width = 60,
|
||||||
height = 40,
|
height = 40,
|
||||||
} = $defineProps<{
|
} = defineProps<{
|
||||||
history?: mastodon.v1.TagHistory[]
|
history?: mastodon.v1.TagHistory[]
|
||||||
width?: number
|
width?: number
|
||||||
height?: number
|
height?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const historyNum = $computed(() => {
|
const historyNum = computed(() => {
|
||||||
if (!history)
|
if (!history)
|
||||||
return [1, 1, 1, 1, 1, 1, 1]
|
return [1, 1, 1, 1, 1, 1, 1]
|
||||||
return [...history].reverse().map(item => Number(item.accounts) || 0)
|
return [...history].reverse().map(item => Number(item.accounts) || 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sparklineEl = $ref<SVGSVGElement>()
|
const sparklineEl = ref<SVGSVGElement>()
|
||||||
const sparklineFn = typeof sparkline !== 'function' ? (sparkline as any).default : sparkline
|
const sparklineFn = typeof sparkline !== 'function' ? (sparkline as any).default : sparkline
|
||||||
|
|
||||||
watch([$$(historyNum), $$(sparklineEl)], ([historyNum, sparklineEl]) => {
|
watch([historyNum, sparklineEl], ([historyNum, sparklineEl]) => {
|
||||||
if (!sparklineEl)
|
if (!sparklineEl)
|
||||||
return
|
return
|
||||||
sparklineFn(sparklineEl, historyNum)
|
sparklineFn(sparklineEl, historyNum)
|
||||||
|
|
|
@ -10,9 +10,9 @@ const props = defineProps<{
|
||||||
|
|
||||||
const { formatHumanReadableNumber, formatNumber, forSR } = useHumanReadableNumber()
|
const { formatHumanReadableNumber, formatNumber, forSR } = useHumanReadableNumber()
|
||||||
|
|
||||||
const useSR = $computed(() => forSR(props.count))
|
const useSR = computed(() => forSR(props.count))
|
||||||
const rawNumber = $computed(() => formatNumber(props.count))
|
const rawNumber = computed(() => formatNumber(props.count))
|
||||||
const humanReadableNumber = $computed(() => formatHumanReadableNumber(props.count))
|
const humanReadableNumber = computed(() => formatHumanReadableNumber(props.count))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -6,11 +6,11 @@ defineProps<{
|
||||||
autoBoundaryMaxSize?: boolean
|
autoBoundaryMaxSize?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const dropdown = $ref<any>()
|
const dropdown = ref<any>()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
return dropdown.hide()
|
return dropdown.value.hide()
|
||||||
}
|
}
|
||||||
provide(InjectionKeyDropdownContext, {
|
provide(InjectionKeyDropdownContext, {
|
||||||
hide,
|
hide,
|
||||||
|
|
|
@ -4,7 +4,7 @@ const props = defineProps<{
|
||||||
lang?: string
|
lang?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const raw = $computed(() => decodeURIComponent(props.code).replace(/'/g, '\''))
|
const raw = computed(() => decodeURIComponent(props.code).replace(/'/g, '\''))
|
||||||
|
|
||||||
const langMap: Record<string, string> = {
|
const langMap: Record<string, string> = {
|
||||||
js: 'javascript',
|
js: 'javascript',
|
||||||
|
@ -13,7 +13,7 @@ const langMap: Record<string, string> = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const highlighted = computed(() => {
|
const highlighted = computed(() => {
|
||||||
return props.lang ? highlightCode(raw, (langMap[props.lang] || props.lang) as any) : raw
|
return props.lang ? highlightCode(raw.value, (langMap[props.lang] || props.lang) as any) : raw
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { conversation } = defineProps<{
|
||||||
conversation: mastodon.v1.Conversation
|
conversation: mastodon.v1.Conversation
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const withAccounts = $computed(() =>
|
const withAccounts = computed(() =>
|
||||||
conversation.accounts.filter(account => account.id !== conversation.lastStatus?.account.id),
|
conversation.accounts.filter(account => account.id !== conversation.lastStatus?.account.id),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { as, alt, dataEmojiId } = $defineProps<{
|
const { as, alt, dataEmojiId } = defineProps<{
|
||||||
as: string
|
as: string
|
||||||
alt?: string
|
alt?: string
|
||||||
dataEmojiId?: string
|
dataEmojiId?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let title = $ref<string | undefined>()
|
const title = ref<string | undefined>()
|
||||||
|
|
||||||
if (alt) {
|
if (alt) {
|
||||||
if (alt.startsWith(':')) {
|
if (alt.startsWith(':')) {
|
||||||
title = alt.replace(/:/g, '')
|
title.value = alt.replace(/:/g, '')
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
import('node-emoji').then(({ find }) => {
|
import('node-emoji').then(({ find }) => {
|
||||||
title = find(alt)?.key.replace(/_/g, ' ')
|
title.value = find(alt)?.key.replace(/_/g, ' ')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it has a data-emoji-id, use that as the title instead
|
// if it has a data-emoji-id, use that as the title instead
|
||||||
if (dataEmojiId)
|
if (dataEmojiId)
|
||||||
title = dataEmojiId
|
title.value = dataEmojiId
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -15,23 +15,23 @@ const { form, isDirty, submitter, reset } = useForm({
|
||||||
form: () => ({ ...list.value }),
|
form: () => ({ ...list.value }),
|
||||||
})
|
})
|
||||||
|
|
||||||
let isEditing = $ref<boolean>(false)
|
const isEditing = ref<boolean>(false)
|
||||||
let deleting = $ref<boolean>(false)
|
const deleting = ref<boolean>(false)
|
||||||
let actionError = $ref<string | undefined>(undefined)
|
const actionError = ref<string | undefined>(undefined)
|
||||||
|
|
||||||
const input = ref<HTMLInputElement>()
|
const input = ref<HTMLInputElement>()
|
||||||
const editBtn = ref<HTMLButtonElement>()
|
const editBtn = ref<HTMLButtonElement>()
|
||||||
const deleteBtn = ref<HTMLButtonElement>()
|
const deleteBtn = ref<HTMLButtonElement>()
|
||||||
|
|
||||||
async function prepareEdit() {
|
async function prepareEdit() {
|
||||||
isEditing = true
|
isEditing.value = true
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
await nextTick()
|
await nextTick()
|
||||||
input.value?.focus()
|
input.value?.focus()
|
||||||
}
|
}
|
||||||
async function cancelEdit() {
|
async function cancelEdit() {
|
||||||
isEditing = false
|
isEditing.value = false
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
@ -47,14 +47,14 @@ const { submit, submitting } = submitter(async () => {
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
actionError = (err as Error).message
|
actionError.value = (err as Error).message
|
||||||
await nextTick()
|
await nextTick()
|
||||||
input.value?.focus()
|
input.value?.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function removeList() {
|
async function removeList() {
|
||||||
if (deleting)
|
if (deleting.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
const confirmDelete = await openConfirmDialog({
|
const confirmDelete = await openConfirmDialog({
|
||||||
|
@ -64,8 +64,8 @@ async function removeList() {
|
||||||
cancel: t('confirm.delete_list.cancel'),
|
cancel: t('confirm.delete_list.cancel'),
|
||||||
})
|
})
|
||||||
|
|
||||||
deleting = true
|
deleting.value = true
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
if (confirmDelete === 'confirm') {
|
if (confirmDelete === 'confirm') {
|
||||||
|
@ -76,21 +76,21 @@ async function removeList() {
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
actionError = (err as Error).message
|
actionError.value = (err as Error).message
|
||||||
await nextTick()
|
await nextTick()
|
||||||
deleteBtn.value?.focus()
|
deleteBtn.value?.focus()
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
deleting = false
|
deleting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deleting = false
|
deleting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearError() {
|
async function clearError() {
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
await nextTick()
|
await nextTick()
|
||||||
if (isEditing)
|
if (isEditing)
|
||||||
input.value?.focus()
|
input.value?.focus()
|
||||||
|
|
|
@ -3,9 +3,9 @@ const { userId } = defineProps<{
|
||||||
userId: string
|
userId: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const paginator = client.v1.lists.list()
|
const paginator = client.value.v1.lists.list()
|
||||||
const listsWithUser = ref((await client.v1.accounts.$select(userId).lists.list()).map(list => list.id))
|
const listsWithUser = ref((await client.value.v1.accounts.$select(userId).lists.list()).map(list => list.id))
|
||||||
|
|
||||||
function indexOfUserInList(listId: string) {
|
function indexOfUserInList(listId: string) {
|
||||||
return listsWithUser.value.indexOf(listId)
|
return listsWithUser.value.indexOf(listId)
|
||||||
|
@ -15,11 +15,11 @@ async function edit(listId: string) {
|
||||||
try {
|
try {
|
||||||
const index = indexOfUserInList(listId)
|
const index = indexOfUserInList(listId)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
await client.v1.lists.$select(listId).accounts.create({ accountIds: [userId] })
|
await client.value.v1.lists.$select(listId).accounts.create({ accountIds: [userId] })
|
||||||
listsWithUser.value.push(listId)
|
listsWithUser.value.push(listId)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await client.v1.lists.$select(listId).accounts.remove({ accountIds: [userId] })
|
await client.value.v1.lists.$select(listId).accounts.remove({ accountIds: [userId] })
|
||||||
listsWithUser.value = listsWithUser.value.filter(id => id !== listId)
|
listsWithUser.value = listsWithUser.value.filter(id => id !== listId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ interface ShortcutItemGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMac = useIsMac()
|
const isMac = useIsMac()
|
||||||
const modifierKeyName = $computed(() => isMac.value ? '⌘' : 'Ctrl')
|
const modifierKeyName = computed(() => isMac.value ? '⌘' : 'Ctrl')
|
||||||
|
|
||||||
const shortcutItemGroups: ShortcutItemGroup[] = [
|
const shortcutItemGroups: ShortcutItemGroup[] = [
|
||||||
{
|
{
|
||||||
|
@ -55,11 +55,11 @@ const shortcutItemGroups: ShortcutItemGroup[] = [
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
description: t('magic_keys.groups.actions.search'),
|
description: t('magic_keys.groups.actions.search'),
|
||||||
shortcut: { keys: [modifierKeyName, 'k'], isSequence: false },
|
shortcut: { keys: [modifierKeyName.value, 'k'], isSequence: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: t('magic_keys.groups.actions.command_mode'),
|
description: t('magic_keys.groups.actions.command_mode'),
|
||||||
shortcut: { keys: [modifierKeyName, '/'], isSequence: false },
|
shortcut: { keys: [modifierKeyName.value, '/'], isSequence: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: t('magic_keys.groups.actions.compose'),
|
description: t('magic_keys.groups.actions.compose'),
|
||||||
|
|
|
@ -28,13 +28,13 @@ useCommand({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
let activeClass = $ref('text-primary')
|
const activeClass = ref('text-primary')
|
||||||
onHydrated(async () => {
|
onHydrated(async () => {
|
||||||
// TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
|
// TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
|
||||||
// we don't have currentServer defined until later
|
// we don't have currentServer defined until later
|
||||||
activeClass = ''
|
activeClass.value = ''
|
||||||
await nextTick()
|
await nextTick()
|
||||||
activeClass = 'text-primary'
|
activeClass.value = 'text-primary'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
|
// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
|
||||||
|
|
|
@ -5,10 +5,10 @@ const { items } = defineProps<{
|
||||||
items: GroupedNotifications
|
items: GroupedNotifications
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const count = $computed(() => items.items.length)
|
const count = computed(() => items.items.length)
|
||||||
const isExpanded = ref(false)
|
const isExpanded = ref(false)
|
||||||
const lang = $computed(() => {
|
const lang = computed(() => {
|
||||||
return (count > 1 || count === 0) ? undefined : items.items[0].status?.language
|
return (count.value > 1 || count.value === 0) ? undefined : items.items[0].status?.language
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ const { group } = defineProps<{
|
||||||
}>()
|
}>()
|
||||||
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
||||||
|
|
||||||
const reblogs = $computed(() => group.likes.filter(i => i.reblog))
|
const reblogs = computed(() => group.likes.filter(i => i.reblog))
|
||||||
const likes = $computed(() => group.likes.filter(i => i.favourite && !i.reblog))
|
const likes = computed(() => group.likes.filter(i => i.favourite && !i.reblog))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -17,12 +17,12 @@ const { t } = useI18n()
|
||||||
|
|
||||||
const pwaEnabled = useAppConfig().pwaEnabled
|
const pwaEnabled = useAppConfig().pwaEnabled
|
||||||
|
|
||||||
let busy = $ref<boolean>(false)
|
const busy = ref<boolean>(false)
|
||||||
let animateSave = $ref<boolean>(false)
|
const animateSave = ref<boolean>(false)
|
||||||
let animateSubscription = $ref<boolean>(false)
|
const animateSubscription = ref<boolean>(false)
|
||||||
let animateRemoveSubscription = $ref<boolean>(false)
|
const animateRemoveSubscription = ref<boolean>(false)
|
||||||
let subscribeError = $ref<string>('')
|
const subscribeError = ref<string>('')
|
||||||
let showSubscribeError = $ref<boolean>(false)
|
const showSubscribeError = ref<boolean>(false)
|
||||||
|
|
||||||
function hideNotification() {
|
function hideNotification() {
|
||||||
const key = currentUser.value?.account?.acct
|
const key = currentUser.value?.account?.acct
|
||||||
|
@ -30,7 +30,7 @@ function hideNotification() {
|
||||||
hiddenNotification.value[key] = true
|
hiddenNotification.value[key] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const showWarning = $computed(() => {
|
const showWarning = computed(() => {
|
||||||
if (!pwaEnabled)
|
if (!pwaEnabled)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -40,12 +40,12 @@ const showWarning = $computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
if (busy)
|
if (busy.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
busy = true
|
busy.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
animateSave = true
|
animateSave.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateSubscription()
|
await updateSubscription()
|
||||||
|
@ -55,48 +55,48 @@ async function saveSettings() {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
busy = false
|
busy.value = false
|
||||||
animateSave = false
|
animateSave.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doSubscribe() {
|
async function doSubscribe() {
|
||||||
if (busy)
|
if (busy.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
busy = true
|
busy.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
animateSubscription = true
|
animateSubscription.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await subscribe()
|
const result = await subscribe()
|
||||||
if (result !== 'subscribed') {
|
if (result !== 'subscribed') {
|
||||||
subscribeError = t(`settings.notifications.push_notifications.subscription_error.${result === 'notification-denied' ? 'permission_denied' : 'request_error'}`)
|
subscribeError.value = t(`settings.notifications.push_notifications.subscription_error.${result === 'notification-denied' ? 'permission_denied' : 'request_error'}`)
|
||||||
showSubscribeError = true
|
showSubscribeError.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (err instanceof PushSubscriptionError) {
|
if (err instanceof PushSubscriptionError) {
|
||||||
subscribeError = t(`settings.notifications.push_notifications.subscription_error.${err.code}`)
|
subscribeError.value = t(`settings.notifications.push_notifications.subscription_error.${err.code}`)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
subscribeError = t('settings.notifications.push_notifications.subscription_error.request_error')
|
subscribeError.value = t('settings.notifications.push_notifications.subscription_error.request_error')
|
||||||
}
|
}
|
||||||
showSubscribeError = true
|
showSubscribeError.value = true
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
busy = false
|
busy.value = false
|
||||||
animateSubscription = false
|
animateSubscription.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function removeSubscription() {
|
async function removeSubscription() {
|
||||||
if (busy)
|
if (busy.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
busy = true
|
busy.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
animateRemoveSubscription = true
|
animateRemoveSubscription.value = true
|
||||||
try {
|
try {
|
||||||
await unsubscribe()
|
await unsubscribe()
|
||||||
}
|
}
|
||||||
|
@ -104,11 +104,11 @@ async function removeSubscription() {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
busy = false
|
busy.value = false
|
||||||
animateRemoveSubscription = false
|
animateRemoveSubscription.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onActivated(() => (busy = false))
|
onActivated(() => (busy.value = false))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -19,10 +19,10 @@ const emit = defineEmits<{
|
||||||
const maxDescriptionLength = 1500
|
const maxDescriptionLength = 1500
|
||||||
|
|
||||||
const isEditDialogOpen = ref(false)
|
const isEditDialogOpen = ref(false)
|
||||||
const description = ref(props.attachment.description ?? '')
|
const description = computed(() => props.attachment.description ?? '')
|
||||||
function toggleApply() {
|
function toggleApply() {
|
||||||
isEditDialogOpen.value = false
|
isEditDialogOpen.value = false
|
||||||
emit('setDescription', unref(description))
|
emit('setDescription', description.value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,16 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
const el = $ref<HTMLElement>()
|
const el = ref<HTMLElement>()
|
||||||
let picker = $ref<Picker>()
|
const picker = ref<Picker>()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|
||||||
async function openEmojiPicker() {
|
async function openEmojiPicker() {
|
||||||
await updateCustomEmojis()
|
await updateCustomEmojis()
|
||||||
|
|
||||||
if (picker) {
|
if (picker.value) {
|
||||||
picker.update({
|
picker.value.update({
|
||||||
theme: colorMode.value,
|
theme: colorMode,
|
||||||
custom: customEmojisData.value,
|
custom: customEmojisData.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ async function openEmojiPicker() {
|
||||||
importEmojiLang(locale.value.split('-')[0]),
|
importEmojiLang(locale.value.split('-')[0]),
|
||||||
])
|
])
|
||||||
|
|
||||||
picker = new Picker({
|
picker.value = new Picker({
|
||||||
data: () => dataPromise,
|
data: () => dataPromise,
|
||||||
onEmojiSelect({ native, src, alt, name }: any) {
|
onEmojiSelect({ native, src, alt, name }: any) {
|
||||||
native
|
native
|
||||||
|
@ -37,19 +37,19 @@ async function openEmojiPicker() {
|
||||||
: emit('selectCustom', { src, alt, 'data-emoji-id': name })
|
: emit('selectCustom', { src, alt, 'data-emoji-id': name })
|
||||||
},
|
},
|
||||||
set: 'twitter',
|
set: 'twitter',
|
||||||
theme: colorMode.value,
|
theme: colorMode,
|
||||||
custom: customEmojisData.value,
|
custom: customEmojisData.value,
|
||||||
i18n,
|
i18n,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// TODO: custom picker
|
// TODO: custom picker
|
||||||
el?.appendChild(picker as any as HTMLElement)
|
el.value?.appendChild(picker.value as any as HTMLElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideEmojiPicker() {
|
function hideEmojiPicker() {
|
||||||
if (picker)
|
if (picker.value)
|
||||||
el?.removeChild(picker as any as HTMLElement)
|
el.value?.removeChild(picker.value as any as HTMLElement)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,16 @@ const modelValue = defineModel<string>({ required: true })
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
const languageKeyword = $ref('')
|
const languageKeyword = ref('')
|
||||||
|
|
||||||
const fuse = new Fuse(languagesNameList, {
|
const fuse = new Fuse(languagesNameList, {
|
||||||
keys: ['code', 'nativeName', 'name'],
|
keys: ['code', 'nativeName', 'name'],
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const languages = $computed(() =>
|
const languages = computed(() =>
|
||||||
languageKeyword.trim()
|
languageKeyword.value.trim()
|
||||||
? fuse.search(languageKeyword).map(r => r.item)
|
? fuse.search(languageKeyword.value).map(r => r.item)
|
||||||
: [...languagesNameList].filter(entry => !userSettings.value.disabledTranslationLanguages.includes(entry.code))
|
: [...languagesNameList].filter(entry => !userSettings.value.disabledTranslationLanguages.includes(entry.code))
|
||||||
.sort(({ code: a }, { code: b }) => {
|
.sort(({ code: a }, { code: b }) => {
|
||||||
// Put English on the top
|
// Put English on the top
|
||||||
|
|
|
@ -7,7 +7,7 @@ const modelValue = defineModel<string>({
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentVisibility = $computed(() =>
|
const currentVisibility = computed(() =>
|
||||||
statusVisibilities.find(v => v.value === modelValue.value) || statusVisibilities[0],
|
statusVisibilities.find(v => v.value === modelValue.value) || statusVisibilities[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -27,61 +27,61 @@ const emit = defineEmits<{
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const draftState = useDraft(draftKey, initial)
|
const draftState = useDraft(draftKey, initial)
|
||||||
const { draft } = $(draftState)
|
const { draft } = draftState
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isExceedingAttachmentLimit, isUploading, failedAttachments, isOverDropZone,
|
isExceedingAttachmentLimit, isUploading, failedAttachments, isOverDropZone,
|
||||||
uploadAttachments, pickAttachments, setDescription, removeAttachment,
|
uploadAttachments, pickAttachments, setDescription, removeAttachment,
|
||||||
dropZoneRef,
|
dropZoneRef,
|
||||||
} = $(useUploadMediaAttachment($$(draft)))
|
} = useUploadMediaAttachment(draft)
|
||||||
|
|
||||||
let { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft, failedMessages, preferredLanguage, publishSpoilerText } = $(usePublish(
|
const { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft, failedMessages, preferredLanguage, publishSpoilerText } = usePublish(
|
||||||
{
|
{
|
||||||
draftState,
|
draftState,
|
||||||
...$$({ expanded, isUploading, initialDraft: initial }),
|
...{ expanded: toRef(() => expanded), isUploading, initialDraft: toRef(() => initial) },
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
|
||||||
const { editor } = useTiptap({
|
const { editor } = useTiptap({
|
||||||
content: computed({
|
content: computed({
|
||||||
get: () => draft.params.status,
|
get: () => draft.value.params.status,
|
||||||
set: (newVal) => {
|
set: (newVal) => {
|
||||||
draft.params.status = newVal
|
draft.value.params.status = newVal
|
||||||
draft.lastUpdated = Date.now()
|
draft.value.lastUpdated = Date.now()
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
placeholder: computed(() => placeholder ?? draft.params.inReplyToId ? t('placeholder.replying') : t('placeholder.default_1')),
|
placeholder: computed(() => placeholder ?? draft.value.params.inReplyToId ? t('placeholder.replying') : t('placeholder.default_1')),
|
||||||
autofocus: shouldExpanded,
|
autofocus: shouldExpanded.value,
|
||||||
onSubmit: publish,
|
onSubmit: publish,
|
||||||
onFocus() {
|
onFocus() {
|
||||||
if (!isExpanded && draft.initialText) {
|
if (!isExpanded && draft.value.initialText) {
|
||||||
editor.value?.chain().insertContent(`${draft.initialText} `).focus('end').run()
|
editor.value?.chain().insertContent(`${draft.value.initialText} `).focus('end').run()
|
||||||
draft.initialText = ''
|
draft.value.initialText = ''
|
||||||
}
|
}
|
||||||
isExpanded = true
|
isExpanded.value = true
|
||||||
},
|
},
|
||||||
onPaste: handlePaste,
|
onPaste: handlePaste,
|
||||||
})
|
})
|
||||||
|
|
||||||
function trimPollOptions() {
|
function trimPollOptions() {
|
||||||
const indexLastNonEmpty = draft.params.poll!.options.findLastIndex(option => option.trim().length > 0)
|
const indexLastNonEmpty = draft.value.params.poll!.options.findLastIndex(option => option.trim().length > 0)
|
||||||
const trimmedOptions = draft.params.poll!.options.slice(0, indexLastNonEmpty + 1)
|
const trimmedOptions = draft.value.params.poll!.options.slice(0, indexLastNonEmpty + 1)
|
||||||
|
|
||||||
if (currentInstance.value?.configuration
|
if (currentInstance.value?.configuration
|
||||||
&& trimmedOptions.length >= currentInstance.value?.configuration?.polls.maxOptions)
|
&& trimmedOptions.length >= currentInstance.value?.configuration?.polls.maxOptions)
|
||||||
draft.params.poll!.options = trimmedOptions
|
draft.value.params.poll!.options = trimmedOptions
|
||||||
else
|
else
|
||||||
draft.params.poll!.options = [...trimmedOptions, '']
|
draft.value.params.poll!.options = [...trimmedOptions, '']
|
||||||
}
|
}
|
||||||
|
|
||||||
function editPollOptionDraft(event: Event, index: number) {
|
function editPollOptionDraft(event: Event, index: number) {
|
||||||
draft.params.poll!.options = Object.assign(draft.params.poll!.options.slice(), { [index]: (event.target as HTMLInputElement).value })
|
draft.value.params.poll!.options = Object.assign(draft.value.params.poll!.options.slice(), { [index]: (event.target as HTMLInputElement).value })
|
||||||
|
|
||||||
trimPollOptions()
|
trimPollOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePollOption(index: number) {
|
function deletePollOption(index: number) {
|
||||||
draft.params.poll!.options = draft.params.poll!.options.slice().splice(index, 1)
|
draft.value.params.poll!.options = draft.value.params.poll!.options.slice().splice(index, 1)
|
||||||
trimPollOptions()
|
trimPollOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ const expiresInOptions = computed(() => [
|
||||||
|
|
||||||
const expiresInDefaultOptionIndex = 2
|
const expiresInDefaultOptionIndex = 2
|
||||||
|
|
||||||
const characterCount = $computed(() => {
|
const characterCount = computed(() => {
|
||||||
const text = htmlToText(editor.value?.getHTML() || '')
|
const text = htmlToText(editor.value?.getHTML() || '')
|
||||||
|
|
||||||
let length = stringLength(text)
|
let length = stringLength(text)
|
||||||
|
@ -131,24 +131,24 @@ const characterCount = $computed(() => {
|
||||||
for (const [fullMatch, before, _handle, username] of text.matchAll(countableMentionRegex))
|
for (const [fullMatch, before, _handle, username] of text.matchAll(countableMentionRegex))
|
||||||
length -= fullMatch.length - (before + username).length - 1 // - 1 for the @
|
length -= fullMatch.length - (before + username).length - 1 // - 1 for the @
|
||||||
|
|
||||||
if (draft.mentions) {
|
if (draft.value.mentions) {
|
||||||
// + 1 is needed as mentions always need a space seperator at the end
|
// + 1 is needed as mentions always need a space seperator at the end
|
||||||
length += draft.mentions.map((mention) => {
|
length += draft.value.mentions.map((mention) => {
|
||||||
const [handle] = mention.split('@')
|
const [handle] = mention.split('@')
|
||||||
return `@${handle}`
|
return `@${handle}`
|
||||||
}).join(' ').length + 1
|
}).join(' ').length + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
length += stringLength(publishSpoilerText)
|
length += stringLength(publishSpoilerText.value)
|
||||||
|
|
||||||
return length
|
return length
|
||||||
})
|
})
|
||||||
|
|
||||||
const isExceedingCharacterLimit = $computed(() => {
|
const isExceedingCharacterLimit = computed(() => {
|
||||||
return characterCount > characterLimit.value
|
return characterCount.value > characterLimit.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const postLanguageDisplay = $computed(() => languagesNameList.find(i => i.code === (draft.params.language || preferredLanguage))?.nativeName)
|
const postLanguageDisplay = computed(() => languagesNameList.find(i => i.code === (draft.value.params.language || preferredLanguage))?.nativeName)
|
||||||
|
|
||||||
async function handlePaste(evt: ClipboardEvent) {
|
async function handlePaste(evt: ClipboardEvent) {
|
||||||
const files = evt.clipboardData?.files
|
const files = evt.clipboardData?.files
|
||||||
|
@ -167,7 +167,7 @@ function insertCustomEmoji(image: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleSensitive() {
|
async function toggleSensitive() {
|
||||||
draft.params.sensitive = !draft.params.sensitive
|
draft.value.params.sensitive = !draft.value.params.sensitive
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publish() {
|
async function publish() {
|
||||||
|
|
|
@ -5,16 +5,16 @@ const route = useRoute()
|
||||||
const { formatNumber } = useHumanReadableNumber()
|
const { formatNumber } = useHumanReadableNumber()
|
||||||
const timeAgoOptions = useTimeAgoOptions()
|
const timeAgoOptions = useTimeAgoOptions()
|
||||||
|
|
||||||
let draftKey = $ref('home')
|
const draftKey = ref('home')
|
||||||
|
|
||||||
const draftKeys = $computed(() => Object.keys(currentUserDrafts.value))
|
const draftKeys = computed(() => Object.keys(currentUserDrafts.value))
|
||||||
const nonEmptyDrafts = $computed(() => draftKeys
|
const nonEmptyDrafts = computed(() => draftKeys.value
|
||||||
.filter(i => i !== draftKey && !isEmptyDraft(currentUserDrafts.value[i]))
|
.filter(i => i !== draftKey.value && !isEmptyDraft(currentUserDrafts.value[i]))
|
||||||
.map(i => [i, currentUserDrafts.value[i]] as const),
|
.map(i => [i, currentUserDrafts.value[i]] as const),
|
||||||
)
|
)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
draftKey = route.query.draft?.toString() || 'home'
|
draftKey.value = route.query.draft?.toString() || 'home'
|
||||||
})
|
})
|
||||||
|
|
||||||
onDeactivated(() => {
|
onDeactivated(() => {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
v-if="$pwa?.needRefresh"
|
v-if="useNuxtApp().$pwa?.needRefresh"
|
||||||
bg="primary-fade" relative rounded
|
bg="primary-fade" relative rounded
|
||||||
flex="~ gap-1 center" px3 py1 text-primary
|
flex="~ gap-1 center" px3 py1 text-primary
|
||||||
@click="$pwa.updateServiceWorker()"
|
@click="useNuxtApp().$pwa?.updateServiceWorker()"
|
||||||
>
|
>
|
||||||
<div i-ri-download-cloud-2-line />
|
<div i-ri-download-cloud-2-line />
|
||||||
<h2 flex="~ gap-2" items-center>
|
<h2 flex="~ gap-2" items-center>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="$pwa?.showInstallPrompt && !$pwa?.needRefresh"
|
v-if="useNuxtApp().$pwa?.showInstallPrompt && !useNuxtApp().$pwa?.needRefresh"
|
||||||
m-2 p5 bg="primary-fade" relative
|
m-2 p5 bg="primary-fade" relative
|
||||||
rounded-lg of-hidden
|
rounded-lg of-hidden
|
||||||
flex="~ col gap-3"
|
flex="~ col gap-3"
|
||||||
|
@ -10,10 +10,10 @@
|
||||||
{{ $t('pwa.install_title') }}
|
{{ $t('pwa.install_title') }}
|
||||||
</h2>
|
</h2>
|
||||||
<div flex="~ gap-1">
|
<div flex="~ gap-1">
|
||||||
<button type="button" btn-solid px-4 py-1 text-center text-sm @click="$pwa.install()">
|
<button type="button" btn-solid px-4 py-1 text-center text-sm @click="useNuxtApp().$pwa?.install()">
|
||||||
{{ $t('pwa.install') }}
|
{{ $t('pwa.install') }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" btn-text filter-saturate-0 px-4 py-1 text-center text-sm @click="$pwa.cancelInstall()">
|
<button type="button" btn-text filter-saturate-0 px-4 py-1 text-center text-sm @click="useNuxtApp().$pwa?.cancelInstall()">
|
||||||
{{ $t('pwa.dismiss') }}
|
{{ $t('pwa.dismiss') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="$pwa?.needRefresh"
|
v-if="useNuxtApp().$pwa?.needRefresh"
|
||||||
m-2 p5 bg="primary-fade" relative
|
m-2 p5 bg="primary-fade" relative
|
||||||
rounded-lg of-hidden
|
rounded-lg of-hidden
|
||||||
flex="~ col gap-3"
|
flex="~ col gap-3"
|
||||||
|
@ -9,10 +9,10 @@
|
||||||
{{ $t('pwa.title') }}
|
{{ $t('pwa.title') }}
|
||||||
</h2>
|
</h2>
|
||||||
<div flex="~ gap-1">
|
<div flex="~ gap-1">
|
||||||
<button type="button" btn-solid px-4 py-1 text-center text-sm @click="$pwa.updateServiceWorker()">
|
<button type="button" btn-solid px-4 py-1 text-center text-sm @click="useNuxtApp().$pwa?.updateServiceWorker()">
|
||||||
{{ $t('pwa.update') }}
|
{{ $t('pwa.update') }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" btn-text filter-saturate-0 px-4 py-1 text-center text-sm @click="$pwa.close()">
|
<button type="button" btn-text filter-saturate-0 px-4 py-1 text-center text-sm @click="useNuxtApp().$pwa?.close()">
|
||||||
{{ $t('pwa.dismiss') }}
|
{{ $t('pwa.dismiss') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { hashtag } = defineProps<{
|
||||||
hashtag: mastodon.v1.Tag
|
hashtag: mastodon.v1.Tag
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const totalTrend = $computed(() =>
|
const totalTrend = computed(() =>
|
||||||
hashtag.history?.reduce((total: number, item) => total + (Number(item.accounts) || 0), 0),
|
hashtag.history?.reduce((total: number, item) => total + (Number(item.accounts) || 0), 0),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { mastodon } from 'masto'
|
||||||
const form = defineModel<{
|
const form = defineModel<{
|
||||||
fieldsAttributes: NonNullable<mastodon.rest.v1.UpdateCredentialsParams['fieldsAttributes']>
|
fieldsAttributes: NonNullable<mastodon.rest.v1.UpdateCredentialsParams['fieldsAttributes']>
|
||||||
}>({ required: true })
|
}>({ required: true })
|
||||||
const dropdown = $ref<any>()
|
const dropdown = ref<any>()
|
||||||
|
|
||||||
const fieldIcons = computed(() =>
|
const fieldIcons = computed(() =>
|
||||||
Array.from({ length: maxAccountFieldCount.value }, (_, i) =>
|
Array.from({ length: maxAccountFieldCount.value }, (_, i) =>
|
||||||
|
@ -12,7 +12,7 @@ const fieldIcons = computed(() =>
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const fieldCount = $computed(() => {
|
const fieldCount = computed(() => {
|
||||||
// find last non-empty field
|
// find last non-empty field
|
||||||
const idx = [...form.value.fieldsAttributes].reverse().findIndex(f => f.name || f.value)
|
const idx = [...form.value.fieldsAttributes].reverse().findIndex(f => f.name || f.value)
|
||||||
if (idx === -1)
|
if (idx === -1)
|
||||||
|
@ -25,7 +25,7 @@ const fieldCount = $computed(() => {
|
||||||
|
|
||||||
function chooseIcon(i: number, text: string) {
|
function chooseIcon(i: number, text: string) {
|
||||||
form.value.fieldsAttributes[i].name = text
|
form.value.fieldsAttributes[i].name = text
|
||||||
dropdown[i]?.hide()
|
dropdown.value[i]?.hide()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
import type { ThemeColors } from '~/composables/settings'
|
import type { ThemeColors } from '~/composables/settings'
|
||||||
|
|
||||||
const themes = await import('~/constants/themes.json').then(r => r.default) as [string, ThemeColors][]
|
const themes = await import('~/constants/themes.json').then(r => r.default) as [string, ThemeColors][]
|
||||||
const settings = $(useUserSettings())
|
const settings = useUserSettings()
|
||||||
|
|
||||||
const currentTheme = $computed(() => settings.themeColors?.['--theme-color-name'] || themes[0][1]['--theme-color-name'])
|
const currentTheme = computed(() => settings.value.themeColors?.['--theme-color-name'] || themes[0][1]['--theme-color-name'])
|
||||||
|
|
||||||
function updateTheme(theme: ThemeColors) {
|
function updateTheme(theme: ThemeColors) {
|
||||||
settings.themeColors = theme
|
settings.value.themeColors = theme
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const focusEditor = inject<typeof noop>('focus-editor', noop)
|
const focusEditor = inject<typeof noop>('focus-editor', noop)
|
||||||
|
|
||||||
const { details, command } = $(props)
|
const { details, command } = props // TODO
|
||||||
|
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
||||||
|
@ -21,7 +21,7 @@ const {
|
||||||
toggleBookmark,
|
toggleBookmark,
|
||||||
toggleFavourite,
|
toggleFavourite,
|
||||||
toggleReblog,
|
toggleReblog,
|
||||||
} = $(useStatusActions(props))
|
} = useStatusActions(props)
|
||||||
|
|
||||||
function reply() {
|
function reply() {
|
||||||
if (!checkLogin())
|
if (!checkLogin())
|
||||||
|
@ -29,7 +29,7 @@ function reply() {
|
||||||
if (details)
|
if (details)
|
||||||
focusEditor()
|
focusEditor()
|
||||||
else
|
else
|
||||||
navigateToStatus({ status, focusReply: true })
|
navigateToStatus({ status: status.value, focusReply: true })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const focusEditor = inject<typeof noop>('focus-editor', noop)
|
const focusEditor = inject<typeof noop>('focus-editor', noop)
|
||||||
|
|
||||||
const { details, command } = $(props)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
status,
|
status,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
@ -24,7 +22,7 @@ const {
|
||||||
togglePin,
|
togglePin,
|
||||||
toggleReblog,
|
toggleReblog,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
} = $(useStatusActions(props))
|
} = useStatusActions(props)
|
||||||
|
|
||||||
const clipboard = useClipboard()
|
const clipboard = useClipboard()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
@ -33,9 +31,9 @@ const { t } = useI18n()
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
||||||
|
|
||||||
const isAuthor = $computed(() => status.account.id === currentUser.value?.account.id)
|
const isAuthor = computed(() => status.value.account.id === currentUser.value?.account.id)
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
function getPermalinkUrl(status: mastodon.v1.Status) {
|
function getPermalinkUrl(status: mastodon.v1.Status) {
|
||||||
const url = getStatusPermalinkRoute(status)
|
const url = getStatusPermalinkRoute(status)
|
||||||
|
@ -72,8 +70,8 @@ async function deleteStatus() {
|
||||||
}) !== 'confirm')
|
}) !== 'confirm')
|
||||||
return
|
return
|
||||||
|
|
||||||
removeCachedStatus(status.id)
|
removeCachedStatus(status.value.id)
|
||||||
await client.v1.statuses.$select(status.id).remove()
|
await client.value.v1.statuses.$select(status.value.id).remove()
|
||||||
|
|
||||||
if (route.name === 'status')
|
if (route.name === 'status')
|
||||||
router.back()
|
router.back()
|
||||||
|
@ -97,9 +95,9 @@ async function deleteAndRedraft() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCachedStatus(status.id)
|
removeCachedStatus(status.value.id)
|
||||||
await client.v1.statuses.$select(status.id).remove()
|
await client.value.v1.statuses.$select(status.value.id).remove()
|
||||||
await openPublishDialog('dialog', await getDraftFromStatus(status), true)
|
await openPublishDialog('dialog', await getDraftFromStatus(status.value), true)
|
||||||
|
|
||||||
// Go to the new status, if the page is the old status
|
// Go to the new status, if the page is the old status
|
||||||
if (lastPublishDialogStatus.value && route.name === 'status')
|
if (lastPublishDialogStatus.value && route.name === 'status')
|
||||||
|
@ -109,25 +107,25 @@ async function deleteAndRedraft() {
|
||||||
function reply() {
|
function reply() {
|
||||||
if (!checkLogin())
|
if (!checkLogin())
|
||||||
return
|
return
|
||||||
if (details) {
|
if (props.details) {
|
||||||
focusEditor()
|
focusEditor()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const { key, draft } = getReplyDraft(status)
|
const { key, draft } = getReplyDraft(status.value)
|
||||||
openPublishDialog(key, draft())
|
openPublishDialog(key, draft())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editStatus() {
|
async function editStatus() {
|
||||||
await openPublishDialog(`edit-${status.id}`, {
|
await openPublishDialog(`edit-${status.value.id}`, {
|
||||||
...await getDraftFromStatus(status),
|
...await getDraftFromStatus(status.value),
|
||||||
editingStatus: status,
|
editingStatus: status.value,
|
||||||
}, true)
|
}, true)
|
||||||
emit('afterEdit')
|
emit('afterEdit')
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFavoritedAndBoostedBy() {
|
function showFavoritedAndBoostedBy() {
|
||||||
openFavoridedBoostedByDialog(status.id)
|
openFavoridedBoostedByDialog(status.value.id)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ const {
|
||||||
isPreview?: boolean
|
isPreview?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const src = $computed(() => attachment.previewUrl || attachment.url || attachment.remoteUrl!)
|
const src = computed(() => attachment.previewUrl || attachment.url || attachment.remoteUrl!)
|
||||||
const srcset = $computed(() => [
|
const srcset = computed(() => [
|
||||||
[attachment.url, attachment.meta?.original?.width],
|
[attachment.url, attachment.meta?.original?.width],
|
||||||
[attachment.remoteUrl, attachment.meta?.original?.width],
|
[attachment.remoteUrl, attachment.meta?.original?.width],
|
||||||
[attachment.previewUrl, attachment.meta?.small?.width],
|
[attachment.previewUrl, attachment.meta?.small?.width],
|
||||||
|
@ -53,12 +53,12 @@ const typeExtsMap = {
|
||||||
gifv: ['gifv', 'gif'],
|
gifv: ['gifv', 'gif'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = $computed(() => {
|
const type = computed(() => {
|
||||||
if (attachment.type && attachment.type !== 'unknown')
|
if (attachment.type && attachment.type !== 'unknown')
|
||||||
return attachment.type
|
return attachment.type
|
||||||
// some server returns unknown type, we need to guess it based on file extension
|
// some server returns unknown type, we need to guess it based on file extension
|
||||||
for (const [type, exts] of Object.entries(typeExtsMap)) {
|
for (const [type, exts] of Object.entries(typeExtsMap)) {
|
||||||
if (exts.some(ext => src?.toLowerCase().endsWith(`.${ext}`)))
|
if (exts.some(ext => src.value?.toLowerCase().endsWith(`.${ext}`)))
|
||||||
return type
|
return type
|
||||||
}
|
}
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
@ -66,8 +66,8 @@ const type = $computed(() => {
|
||||||
|
|
||||||
const video = ref<HTMLVideoElement | undefined>()
|
const video = ref<HTMLVideoElement | undefined>()
|
||||||
const prefersReducedMotion = usePreferredReducedMotion()
|
const prefersReducedMotion = usePreferredReducedMotion()
|
||||||
const isAudio = $computed(() => attachment.type === 'audio')
|
const isAudio = computed(() => attachment.type === 'audio')
|
||||||
const isVideo = $computed(() => attachment.type === 'video')
|
const isVideo = computed(() => attachment.type === 'video')
|
||||||
|
|
||||||
const enableAutoplay = usePreferences('enableAutoplay')
|
const enableAutoplay = usePreferences('enableAutoplay')
|
||||||
|
|
||||||
|
@ -100,21 +100,21 @@ function loadAttachment() {
|
||||||
shouldLoadAttachment.value = true
|
shouldLoadAttachment.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const blurHashSrc = $computed(() => {
|
const blurHashSrc = computed(() => {
|
||||||
if (!attachment.blurhash)
|
if (!attachment.blurhash)
|
||||||
return ''
|
return ''
|
||||||
const pixels = decode(attachment.blurhash, 32, 32)
|
const pixels = decode(attachment.blurhash, 32, 32)
|
||||||
return getDataUrlFromArr(pixels, 32, 32)
|
return getDataUrlFromArr(pixels, 32, 32)
|
||||||
})
|
})
|
||||||
|
|
||||||
let videoThumbnail = shouldLoadAttachment.value
|
const videoThumbnail = ref(shouldLoadAttachment.value
|
||||||
? attachment.previewUrl
|
? attachment.previewUrl
|
||||||
: blurHashSrc
|
: blurHashSrc.value)
|
||||||
|
|
||||||
watch(shouldLoadAttachment, () => {
|
watch(shouldLoadAttachment, () => {
|
||||||
videoThumbnail = shouldLoadAttachment
|
videoThumbnail.value = shouldLoadAttachment.value
|
||||||
? attachment.previewUrl
|
? attachment.previewUrl
|
||||||
: blurHashSrc
|
: blurHashSrc.value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const {
|
||||||
const { translation } = useTranslation(status, getLanguageCode())
|
const { translation } = useTranslation(status, getLanguageCode())
|
||||||
|
|
||||||
const emojisObject = useEmojisFallback(() => status.emojis)
|
const emojisObject = useEmojisFallback(() => status.emojis)
|
||||||
const vnode = $computed(() => {
|
const vnode = computed(() => {
|
||||||
if (!status.content)
|
if (!status.content)
|
||||||
return null
|
return null
|
||||||
return contentToVNode(status.content, {
|
return contentToVNode(status.content, {
|
||||||
|
|
|
@ -26,45 +26,45 @@ const props = withDefaults(
|
||||||
|
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
const status = $computed(() => {
|
const status = computed(() => {
|
||||||
if (props.status.reblog && (!props.status.content || props.status.content === props.status.reblog.content))
|
if (props.status.reblog && (!props.status.content || props.status.content === props.status.reblog.content))
|
||||||
return props.status.reblog
|
return props.status.reblog
|
||||||
return props.status
|
return props.status
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use original status, avoid connecting a reblog
|
// Use original status, avoid connecting a reblog
|
||||||
const directReply = $computed(() => props.hasNewer || (!!status.inReplyToId && (status.inReplyToId === props.newer?.id || status.inReplyToId === props.newer?.reblog?.id)))
|
const directReply = computed(() => props.hasNewer || (!!status.value.inReplyToId && (status.value.inReplyToId === props.newer?.id || status.value.inReplyToId === props.newer?.reblog?.id)))
|
||||||
// Use reblogged status, connect it to further replies
|
// Use reblogged status, connect it to further replies
|
||||||
const connectReply = $computed(() => props.hasOlder || status.id === props.older?.inReplyToId || status.id === props.older?.reblog?.inReplyToId)
|
const connectReply = computed(() => props.hasOlder || status.value.id === props.older?.inReplyToId || status.value.id === props.older?.reblog?.inReplyToId)
|
||||||
// Open a detailed status, the replies directly to it
|
// Open a detailed status, the replies directly to it
|
||||||
const replyToMain = $computed(() => props.main && props.main.id === status.inReplyToId)
|
const replyToMain = computed(() => props.main && props.main.id === status.value.inReplyToId)
|
||||||
|
|
||||||
const rebloggedBy = $computed(() => props.status.reblog ? props.status.account : null)
|
const rebloggedBy = computed(() => props.status.reblog ? props.status.account : null)
|
||||||
|
|
||||||
const statusRoute = $computed(() => getStatusRoute(status))
|
const statusRoute = computed(() => getStatusRoute(status.value))
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
function go(evt: MouseEvent | KeyboardEvent) {
|
function go(evt: MouseEvent | KeyboardEvent) {
|
||||||
if (evt.metaKey || evt.ctrlKey) {
|
if (evt.metaKey || evt.ctrlKey) {
|
||||||
window.open(statusRoute.href)
|
window.open(statusRoute.value.href)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cacheStatus(status)
|
cacheStatus(status.value)
|
||||||
router.push(statusRoute)
|
router.push(statusRoute.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdAt = useFormattedDateTime(status.createdAt)
|
const createdAt = useFormattedDateTime(status.value.createdAt)
|
||||||
const timeAgoOptions = useTimeAgoOptions(true)
|
const timeAgoOptions = useTimeAgoOptions(true)
|
||||||
const timeago = useTimeAgo(() => status.createdAt, timeAgoOptions)
|
const timeago = useTimeAgo(() => status.value.createdAt, timeAgoOptions)
|
||||||
|
|
||||||
const isSelfReply = $computed(() => status.inReplyToAccountId === status.account.id)
|
const isSelfReply = computed(() => status.value.inReplyToAccountId === status.value.account.id)
|
||||||
const collapseRebloggedBy = $computed(() => rebloggedBy?.id === status.account.id)
|
const collapseRebloggedBy = computed(() => rebloggedBy.value?.id === status.value.account.id)
|
||||||
const isDM = $computed(() => status.visibility === 'direct')
|
const isDM = computed(() => status.value.visibility === 'direct')
|
||||||
|
|
||||||
const showUpperBorder = $computed(() => props.newer && !directReply)
|
const showUpperBorder = computed(() => props.newer && !directReply)
|
||||||
const showReplyTo = $computed(() => !replyToMain && !directReply)
|
const showReplyTo = computed(() => !replyToMain && !directReply)
|
||||||
|
|
||||||
const forceShow = ref(false)
|
const forceShow = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,28 +9,28 @@ const { status, context } = defineProps<{
|
||||||
inNotification?: boolean
|
inNotification?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const isDM = $computed(() => status.visibility === 'direct')
|
const isDM = computed(() => status.visibility === 'direct')
|
||||||
const isDetails = $computed(() => context === 'details')
|
const isDetails = computed(() => context === 'details')
|
||||||
|
|
||||||
// Content Filter logic
|
// Content Filter logic
|
||||||
const filterResult = $computed(() => status.filtered?.length ? status.filtered[0] : null)
|
const filterResult = computed(() => status.filtered?.length ? status.filtered[0] : null)
|
||||||
const filter = $computed(() => filterResult?.filter)
|
const filter = computed(() => filterResult.value?.filter)
|
||||||
|
|
||||||
const filterPhrase = $computed(() => filter?.title)
|
const filterPhrase = computed(() => filter.value?.title)
|
||||||
const isFiltered = $computed(() => status.account.id !== currentUser.value?.account.id && filterPhrase && context && context !== 'details' && !!filter?.context.includes(context))
|
const isFiltered = computed(() => status.account.id !== currentUser.value?.account.id && filterPhrase && context && context !== 'details' && !!filter.value?.context.includes(context))
|
||||||
|
|
||||||
// check spoiler text or media attachment
|
// check spoiler text or media attachment
|
||||||
// needed to handle accounts that mark all their posts as sensitive
|
// needed to handle accounts that mark all their posts as sensitive
|
||||||
const spoilerTextPresent = $computed(() => !!status.spoilerText && status.spoilerText.trim().length > 0)
|
const spoilerTextPresent = computed(() => !!status.spoilerText && status.spoilerText.trim().length > 0)
|
||||||
const hasSpoilerOrSensitiveMedia = $computed(() => spoilerTextPresent || (status.sensitive && !!status.mediaAttachments.length))
|
const hasSpoilerOrSensitiveMedia = computed(() => spoilerTextPresent.value || (status.sensitive && !!status.mediaAttachments.length))
|
||||||
const isSensitiveNonSpoiler = computed(() => status.sensitive && !status.spoilerText && !!status.mediaAttachments.length)
|
const isSensitiveNonSpoiler = computed(() => status.sensitive && !status.spoilerText && !!status.mediaAttachments.length)
|
||||||
const hideAllMedia = computed(
|
const hideAllMedia = computed(
|
||||||
() => {
|
() => {
|
||||||
return currentUser.value ? (getHideMediaByDefault(currentUser.value.account) && (!!status.mediaAttachments.length || !!status.card?.html)) : false
|
return currentUser.value ? (getHideMediaByDefault(currentUser.value.account) && (!!status.mediaAttachments.length || !!status.card?.html)) : false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const embeddedMediaPreference = $(usePreferences('experimentalEmbeddedMedia'))
|
const embeddedMediaPreference = usePreferences('experimentalEmbeddedMedia')
|
||||||
const allowEmbeddedMedia = $computed(() => status.card?.html && embeddedMediaPreference)
|
const allowEmbeddedMedia = computed(() => status.card?.html && embeddedMediaPreference)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -14,18 +14,18 @@ defineEmits<{
|
||||||
(event: 'refetchStatus'): void
|
(event: 'refetchStatus'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const status = $computed(() => {
|
const status = computed(() => {
|
||||||
if (props.status.reblog && props.status.reblog)
|
if (props.status.reblog && props.status.reblog)
|
||||||
return props.status.reblog
|
return props.status.reblog
|
||||||
return props.status
|
return props.status
|
||||||
})
|
})
|
||||||
|
|
||||||
const createdAt = useFormattedDateTime(status.createdAt)
|
const createdAt = useFormattedDateTime(status.value.createdAt)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHydratedHead({
|
useHydratedHead({
|
||||||
title: () => `${getDisplayName(status.account)} ${t('common.in')} ${t('app_name')}: "${removeHTMLTags(status.content) || ''}"`,
|
title: () => `${getDisplayName(status.value.account)} ${t('common.in')} ${t('app_name')}: "${removeHTMLTags(status.value.content) || ''}"`,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { status } = defineProps<{
|
||||||
status: mastodon.v1.Status
|
status: mastodon.v1.Status
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const vnode = $computed(() => {
|
const vnode = computed(() => {
|
||||||
if (!status.card?.html)
|
if (!status.card?.html)
|
||||||
return null
|
return null
|
||||||
const node = sanitizeEmbeddedIframe(status.card?.html)?.children[0]
|
const node = sanitizeEmbeddedIframe(status.card?.html)?.children[0]
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { favouritedBoostedByStatusId } from '~/composables/dialog'
|
||||||
|
|
||||||
const type = ref<'favourited-by' | 'boosted-by'>('favourited-by')
|
const type = ref<'favourited-by' | 'boosted-by'>('favourited-by')
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
return client.v1.statuses.$select(favouritedBoostedByStatusId.value!)[type.value === 'favourited-by' ? 'favouritedBy' : 'rebloggedBy'].list()
|
return client.value.v1.statuses.$select(favouritedBoostedByStatusId.value!)[type.value === 'favourited-by' ? 'favouritedBy' : 'rebloggedBy'].list()
|
||||||
}
|
}
|
||||||
|
|
||||||
const paginator = $computed(() => load())
|
const paginator = computed(() => load())
|
||||||
|
|
||||||
function showFavouritedBy() {
|
function showFavouritedBy() {
|
||||||
type.value = 'favourited-by'
|
type.value = 'favourited-by'
|
||||||
|
|
|
@ -8,7 +8,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const el = ref<HTMLElement>()
|
const el = ref<HTMLElement>()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const statusRoute = $computed(() => getStatusRoute(props.status))
|
const statusRoute = computed(() => getStatusRoute(props.status))
|
||||||
|
|
||||||
function onclick(evt: MouseEvent | KeyboardEvent) {
|
function onclick(evt: MouseEvent | KeyboardEvent) {
|
||||||
const path = evt.composedPath() as HTMLElement[]
|
const path = evt.composedPath() as HTMLElement[]
|
||||||
|
@ -20,11 +20,11 @@ function onclick(evt: MouseEvent | KeyboardEvent) {
|
||||||
|
|
||||||
function go(evt: MouseEvent | KeyboardEvent) {
|
function go(evt: MouseEvent | KeyboardEvent) {
|
||||||
if (evt.metaKey || evt.ctrlKey) {
|
if (evt.metaKey || evt.ctrlKey) {
|
||||||
window.open(statusRoute.href)
|
window.open(statusRoute.value.href)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cacheStatus(props.status)
|
cacheStatus(props.status)
|
||||||
router.push(statusRoute)
|
router.push(statusRoute.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,7 +15,7 @@ const expiredTimeAgo = useTimeAgo(poll.expiresAt!, timeAgoOptions)
|
||||||
const expiredTimeFormatted = useFormattedDateTime(poll.expiresAt!)
|
const expiredTimeFormatted = useFormattedDateTime(poll.expiresAt!)
|
||||||
const { formatPercentage } = useHumanReadableNumber()
|
const { formatPercentage } = useHumanReadableNumber()
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
async function vote(e: Event) {
|
async function vote(e: Event) {
|
||||||
const formData = new FormData(e.target as HTMLFormElement)
|
const formData = new FormData(e.target as HTMLFormElement)
|
||||||
|
@ -36,10 +36,10 @@ async function vote(e: Event) {
|
||||||
|
|
||||||
cacheStatus({ ...status, poll }, undefined, true)
|
cacheStatus({ ...status, poll }, undefined, true)
|
||||||
|
|
||||||
await client.v1.polls.$select(poll.id).votes.create({ choices })
|
await client.value.v1.polls.$select(poll.id).votes.create({ choices })
|
||||||
}
|
}
|
||||||
|
|
||||||
const votersCount = $computed(() => poll.votersCount ?? poll.votesCount ?? 0)
|
const votersCount = computed(() => poll.votersCount ?? poll.votesCount ?? 0)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -11,7 +11,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const providerName = props.card.providerName
|
const providerName = props.card.providerName
|
||||||
|
|
||||||
const gitHubCards = $(usePreferences('experimentalGitHubCards'))
|
const gitHubCards = usePreferences('experimentalGitHubCards')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -12,14 +12,14 @@ const props = defineProps<{
|
||||||
// mastodon's default max og image width
|
// mastodon's default max og image width
|
||||||
const ogImageWidth = 400
|
const ogImageWidth = 400
|
||||||
|
|
||||||
const alt = $computed(() => `${props.card.title} - ${props.card.title}`)
|
const alt = computed(() => `${props.card.title} - ${props.card.title}`)
|
||||||
const isSquare = $computed(() => (
|
const isSquare = computed(() => (
|
||||||
props.smallPictureOnly
|
props.smallPictureOnly
|
||||||
|| props.card.width === props.card.height
|
|| props.card.width === props.card.height
|
||||||
|| Number(props.card.width || 0) < ogImageWidth
|
|| Number(props.card.width || 0) < ogImageWidth
|
||||||
|| Number(props.card.height || 0) < ogImageWidth / 2
|
|| Number(props.card.height || 0) < ogImageWidth / 2
|
||||||
))
|
))
|
||||||
const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
const providerName = computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
||||||
|
|
||||||
// TODO: handle card.type: 'photo' | 'video' | 'rich';
|
// TODO: handle card.type: 'photo' | 'video' | 'rich';
|
||||||
const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
|
const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
|
||||||
|
|
|
@ -29,7 +29,7 @@ interface Meta {
|
||||||
// /sponsors/user
|
// /sponsors/user
|
||||||
const supportedReservedRoutes = ['sponsors']
|
const supportedReservedRoutes = ['sponsors']
|
||||||
|
|
||||||
const meta = $computed(() => {
|
const meta = computed(() => {
|
||||||
const { url } = props.card
|
const { url } = props.card
|
||||||
const path = url.split('https://github.com/')[1]
|
const path = url.split('https://github.com/')[1]
|
||||||
const [firstName, secondName] = path?.split('/') || []
|
const [firstName, secondName] = path?.split('/') || []
|
||||||
|
@ -64,7 +64,7 @@ const meta = $computed(() => {
|
||||||
const avatar = `https://github.com/${user}.png?size=256`
|
const avatar = `https://github.com/${user}.png?size=256`
|
||||||
|
|
||||||
const author = props.card.authorName
|
const author = props.card.authorName
|
||||||
const info = $ref<Meta>({
|
const info = {
|
||||||
type,
|
type,
|
||||||
user,
|
user,
|
||||||
titleUrl: `https://github.com/${user}${repo ? `/${repo}` : ''}`,
|
titleUrl: `https://github.com/${user}${repo ? `/${repo}` : ''}`,
|
||||||
|
@ -78,7 +78,7 @@ const meta = $computed(() => {
|
||||||
user: author,
|
user: author,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
})
|
}
|
||||||
return info
|
return info
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -19,31 +19,31 @@ interface Meta {
|
||||||
// Protect against long code snippets
|
// Protect against long code snippets
|
||||||
const maxLines = 20
|
const maxLines = 20
|
||||||
|
|
||||||
const meta = $computed(() => {
|
const meta = computed(() => {
|
||||||
const { description } = props.card
|
const { description } = props.card
|
||||||
const meta = description.match(/.*Code Snippet from (.+), lines (\S+)\n\n(.+)/s)
|
const meta = description.match(/.*Code Snippet from (.+), lines (\S+)\n\n(.+)/s)
|
||||||
const file = meta?.[1]
|
const file = meta?.[1]
|
||||||
const lines = meta?.[2]
|
const lines = meta?.[2]
|
||||||
const code = meta?.[3].split('\n').slice(0, maxLines).join('\n')
|
const code = meta?.[3].split('\n').slice(0, maxLines).join('\n')
|
||||||
const project = props.card.title?.replace(' - StackBlitz', '')
|
const project = props.card.title?.replace(' - StackBlitz', '')
|
||||||
const info = $ref<Meta>({
|
const info = {
|
||||||
file,
|
file,
|
||||||
lines,
|
lines,
|
||||||
code,
|
code,
|
||||||
project,
|
project,
|
||||||
})
|
}
|
||||||
return info
|
return info
|
||||||
})
|
})
|
||||||
|
|
||||||
const vnodeCode = $computed(() => {
|
const vnodeCode = computed(() => {
|
||||||
if (!meta.code)
|
if (!meta.value.code)
|
||||||
return null
|
return null
|
||||||
const code = meta.code
|
const code = meta.value.code
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
.replace(/`/g, '`')
|
.replace(/`/g, '`')
|
||||||
|
|
||||||
const vnode = contentToVNode(`<p>\`\`\`${meta.file?.split('.')?.[1] ?? ''}\n${code}\n\`\`\`\</p>`, {
|
const vnode = contentToVNode(`<p>\`\`\`${meta.value.file?.split('.')?.[1] ?? ''}\n${code}\n\`\`\`\</p>`, {
|
||||||
markdown: true,
|
markdown: true,
|
||||||
})
|
})
|
||||||
return vnode
|
return vnode
|
||||||
|
|
|
@ -9,7 +9,7 @@ const {
|
||||||
isSelfReply: boolean
|
isSelfReply: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const isSelf = $computed(() => status.inReplyToAccountId === status.account.id)
|
const isSelf = computed(() => status.inReplyToAccountId === status.account.id)
|
||||||
const account = isSelf ? computed(() => status.account) : useAccountById(status.inReplyToAccountId)
|
const account = isSelf ? computed(() => status.account) : useAccountById(status.inReplyToAccountId)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,14 @@ const showButton = computed(() =>
|
||||||
&& status.content.trim().length,
|
&& status.content.trim().length,
|
||||||
)
|
)
|
||||||
|
|
||||||
let translating = $ref(false)
|
const translating = ref(false)
|
||||||
async function toggleTranslation() {
|
async function toggleTranslation() {
|
||||||
translating = true
|
translating.value = true
|
||||||
try {
|
try {
|
||||||
await _toggleTranslation()
|
await _toggleTranslation()
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
translating = false
|
translating.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { status } = defineProps<{
|
||||||
status: mastodon.v1.Status
|
status: mastodon.v1.Status
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const visibility = $computed(() => statusVisibilities.find(v => v.value === status.visibility)!)
|
const visibility = computed(() => statusVisibilities.find(v => v.value === status.visibility)!)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -9,7 +9,7 @@ const emit = defineEmits<{
|
||||||
(event: 'change'): void
|
(event: 'change'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
async function toggleFollowTag() {
|
async function toggleFollowTag() {
|
||||||
// We save the state so be can do an optimistic UI update, but fallback to the previous state if the API call fails
|
// We save the state so be can do an optimistic UI update, but fallback to the previous state if the API call fails
|
||||||
|
@ -20,9 +20,9 @@ async function toggleFollowTag() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (previousFollowingState)
|
if (previousFollowingState)
|
||||||
await client.v1.tags.$select(tag.name).unfollow()
|
await client.value.v1.tags.$select(tag.name).unfollow()
|
||||||
else
|
else
|
||||||
await client.v1.tags.$select(tag.name).follow()
|
await client.value.v1.tags.$select(tag.name).follow()
|
||||||
|
|
||||||
emit('change')
|
emit('change')
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ import type { mastodon } from 'masto'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tag,
|
tag,
|
||||||
} = $defineProps<{
|
} = defineProps<{
|
||||||
tag: mastodon.v1.Tag
|
tag: mastodon.v1.Tag
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const to = $computed(() => {
|
const to = computed(() => {
|
||||||
const { hostname, pathname } = new URL(tag.url)
|
const { hostname, pathname } = new URL(tag.url)
|
||||||
return `/${hostname}${pathname}`
|
return `/${hostname}${pathname}`
|
||||||
})
|
})
|
||||||
|
@ -24,9 +24,9 @@ function onclick(evt: MouseEvent | KeyboardEvent) {
|
||||||
|
|
||||||
function go(evt: MouseEvent | KeyboardEvent) {
|
function go(evt: MouseEvent | KeyboardEvent) {
|
||||||
if (evt.metaKey || evt.ctrlKey)
|
if (evt.metaKey || evt.ctrlKey)
|
||||||
window.open(to)
|
window.open(to.value)
|
||||||
else
|
else
|
||||||
router.push(to)
|
router.push(to.value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const paginator = client.v1.domainBlocks.list()
|
const paginator = client.value.v1.domainBlocks.list()
|
||||||
|
|
||||||
async function unblock(domain: string) {
|
async function unblock(domain: string) {
|
||||||
await client.v1.domainBlocks.remove({ domain })
|
await client.value.v1.domainBlocks.remove({ domain })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ const { paginator, stream, account, buffer = 10, endMessage = true } = definePro
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { formatNumber } = useHumanReadableNumber()
|
const { formatNumber } = useHumanReadableNumber()
|
||||||
const virtualScroller = $(usePreferences('experimentalVirtualScroller'))
|
const virtualScroller = usePreferences('experimentalVirtualScroller')
|
||||||
|
|
||||||
const showOriginSite = $computed(() =>
|
const showOriginSite = computed(() =>
|
||||||
account && account.id !== currentUser.value?.account.id && getServerName(account) !== currentServer.value,
|
account && account.id !== currentUser.value?.account.id && getServerName(account) !== currentServer.value,
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,10 +37,10 @@ const emojis = computed(() => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let selectedIndex = $ref(0)
|
const selectedIndex = ref(0)
|
||||||
|
|
||||||
watch(items, () => {
|
watch(() => items, () => {
|
||||||
selectedIndex = 0
|
selectedIndex.value = 0
|
||||||
})
|
})
|
||||||
|
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
@ -48,15 +48,15 @@ function onKeyDown(event: KeyboardEvent) {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp') {
|
||||||
selectedIndex = ((selectedIndex + items.length) - 1) % items.length
|
selectedIndex.value = ((selectedIndex.value + items.length) - 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'ArrowDown') {
|
else if (event.key === 'ArrowDown') {
|
||||||
selectedIndex = (selectedIndex + 1) % items.length
|
selectedIndex.value = (selectedIndex.value + 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'Enter') {
|
else if (event.key === 'Enter') {
|
||||||
selectItem(selectedIndex)
|
selectItem(selectedIndex.value)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ const { items, command } = defineProps<{
|
||||||
isPending?: boolean
|
isPending?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let selectedIndex = $ref(0)
|
const selectedIndex = ref(0)
|
||||||
|
|
||||||
watch(items, () => {
|
watch(() => items, () => {
|
||||||
selectedIndex = 0
|
selectedIndex.value = 0
|
||||||
})
|
})
|
||||||
|
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
@ -20,15 +20,15 @@ function onKeyDown(event: KeyboardEvent) {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp') {
|
||||||
selectedIndex = ((selectedIndex + items.length) - 1) % items.length
|
selectedIndex.value = ((selectedIndex.value + items.length) - 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'ArrowDown') {
|
else if (event.key === 'ArrowDown') {
|
||||||
selectedIndex = (selectedIndex + 1) % items.length
|
selectedIndex.value = (selectedIndex.value + 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'Enter') {
|
else if (event.key === 'Enter') {
|
||||||
selectItem(selectedIndex)
|
selectItem(selectedIndex.value)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ const { items, command } = defineProps<{
|
||||||
isPending?: boolean
|
isPending?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let selectedIndex = $ref(0)
|
const selectedIndex = ref(0)
|
||||||
|
|
||||||
watch(items, () => {
|
watch(() => items, () => {
|
||||||
selectedIndex = 0
|
selectedIndex.value = 0
|
||||||
})
|
})
|
||||||
|
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
@ -20,15 +20,15 @@ function onKeyDown(event: KeyboardEvent) {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp') {
|
||||||
selectedIndex = ((selectedIndex + items.length) - 1) % items.length
|
selectedIndex.value = ((selectedIndex.value + items.length) - 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'ArrowDown') {
|
else if (event.key === 'ArrowDown') {
|
||||||
selectedIndex = (selectedIndex + 1) % items.length
|
selectedIndex.value = (selectedIndex.value + 1) % items.length
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (event.key === 'Enter') {
|
else if (event.key === 'Enter') {
|
||||||
selectItem(selectedIndex)
|
selectItem(selectedIndex.value)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
|
|
||||||
const input = ref<HTMLInputElement | undefined>()
|
const input = ref<HTMLInputElement | undefined>()
|
||||||
let knownServers = $ref<string[]>([])
|
const knownServers = ref<string[]>([])
|
||||||
let autocompleteIndex = $ref(0)
|
const autocompleteIndex = ref(0)
|
||||||
let autocompleteShow = $ref(false)
|
const autocompleteShow = ref(false)
|
||||||
|
|
||||||
const { busy, error, displayError, server, oauth } = useSignIn(input)
|
const { busy, error, displayError, server, oauth } = useSignIn(input)
|
||||||
|
|
||||||
let fuse = $shallowRef(new Fuse([] as string[]))
|
const fuse = shallowRef(new Fuse([] as string[]))
|
||||||
|
|
||||||
const filteredServers = $computed(() => {
|
const filteredServers = computed(() => {
|
||||||
if (!server.value)
|
if (!server.value)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
const results = fuse.search(server.value, { limit: 6 }).map(result => result.item)
|
const results = fuse.value.search(server.value, { limit: 6 }).map(result => result.item)
|
||||||
if (results[0] === server.value)
|
if (results[0] === server.value)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -44,52 +44,52 @@ async function handleInput() {
|
||||||
isValidUrl(`https://${input}`)
|
isValidUrl(`https://${input}`)
|
||||||
&& input.match(/^[a-z0-9-]+(\.[a-z0-9-]+)+(:[0-9]+)?$/i)
|
&& input.match(/^[a-z0-9-]+(\.[a-z0-9-]+)+(:[0-9]+)?$/i)
|
||||||
// Do not hide the autocomplete if a result has an exact substring match on the input
|
// Do not hide the autocomplete if a result has an exact substring match on the input
|
||||||
&& !filteredServers.some(s => s.includes(input))
|
&& !filteredServers.value.some(s => s.includes(input))
|
||||||
)
|
)
|
||||||
autocompleteShow = false
|
autocompleteShow.value = false
|
||||||
else
|
else
|
||||||
autocompleteShow = true
|
autocompleteShow.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function toSelector(server: string) {
|
function toSelector(server: string) {
|
||||||
return server.replace(/[^\w-]/g, '-')
|
return server.replace(/[^\w-]/g, '-')
|
||||||
}
|
}
|
||||||
function move(delta: number) {
|
function move(delta: number) {
|
||||||
if (filteredServers.length === 0) {
|
if (filteredServers.value.length === 0) {
|
||||||
autocompleteIndex = 0
|
autocompleteIndex.value = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
autocompleteIndex = ((autocompleteIndex + delta) + filteredServers.length) % filteredServers.length
|
autocompleteIndex.value = ((autocompleteIndex.value + delta) + filteredServers.value.length) % filteredServers.value.length
|
||||||
document.querySelector(`#${toSelector(filteredServers[autocompleteIndex])}`)?.scrollIntoView(false)
|
document.querySelector(`#${toSelector(filteredServers.value[autocompleteIndex.value])}`)?.scrollIntoView(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnter(e: KeyboardEvent) {
|
function onEnter(e: KeyboardEvent) {
|
||||||
if (autocompleteShow === true && filteredServers[autocompleteIndex]) {
|
if (autocompleteShow.value === true && filteredServers.value[autocompleteIndex.value]) {
|
||||||
server.value = filteredServers[autocompleteIndex]
|
server.value = filteredServers.value[autocompleteIndex.value]
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
autocompleteShow = false
|
autocompleteShow.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeAutocomplete(evt: KeyboardEvent) {
|
function escapeAutocomplete(evt: KeyboardEvent) {
|
||||||
if (!autocompleteShow)
|
if (!autocompleteShow)
|
||||||
return
|
return
|
||||||
autocompleteShow = false
|
autocompleteShow.value = false
|
||||||
evt.stopPropagation()
|
evt.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(index: number) {
|
function select(index: number) {
|
||||||
server.value = filteredServers[index]
|
server.value = filteredServers.value[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
input?.value?.focus()
|
input?.value?.focus()
|
||||||
knownServers = await (globalThis.$fetch as any)('/api/list-servers')
|
knownServers.value = await (globalThis.$fetch as any)('/api/list-servers')
|
||||||
fuse = new Fuse(knownServers, { shouldSort: true })
|
fuse.value = new Fuse(knownServers.value, { shouldSort: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
onClickOutside(input, () => {
|
onClickOutside(input, () => {
|
||||||
autocompleteShow = false
|
autocompleteShow.value = false
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -20,18 +20,18 @@ export function useAriaAnnouncer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAriaLog() {
|
export function useAriaLog() {
|
||||||
let logs = $ref<any[]>([])
|
const logs = ref<any[]>([])
|
||||||
|
|
||||||
const announceLogs = (messages: any[]) => {
|
const announceLogs = (messages: any[]) => {
|
||||||
logs = messages
|
logs.value = messages
|
||||||
}
|
}
|
||||||
|
|
||||||
const appendLogs = (messages: any[]) => {
|
const appendLogs = (messages: any[]) => {
|
||||||
logs = logs.concat(messages)
|
logs.value = logs.value.concat(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearLogs = () => {
|
const clearLogs = () => {
|
||||||
logs = []
|
logs.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -43,14 +43,14 @@ export function useAriaLog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAriaStatus() {
|
export function useAriaStatus() {
|
||||||
let status = $ref<any>('')
|
const status = ref<any>('')
|
||||||
|
|
||||||
const announceStatus = (message: any) => {
|
const announceStatus = (message: any) => {
|
||||||
status = message
|
status.value = message
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearStatus = () => {
|
const clearStatus = () => {
|
||||||
status = ''
|
status.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -19,8 +19,8 @@ export async function updateCustomEmojis() {
|
||||||
if (Date.now() - currentCustomEmojis.value.lastUpdate < TTL)
|
if (Date.now() - currentCustomEmojis.value.lastUpdate < TTL)
|
||||||
return
|
return
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const emojis = await client.v1.customEmojis.list()
|
const emojis = await client.value.v1.customEmojis.list()
|
||||||
Object.assign(currentCustomEmojis.value, {
|
Object.assign(currentCustomEmojis.value, {
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
emojis,
|
emojis,
|
||||||
|
|
|
@ -32,10 +32,10 @@ export function useHumanReadableNumber() {
|
||||||
export function useFormattedDateTime(value: MaybeRefOrGetter<string | number | Date | undefined | null>,
|
export function useFormattedDateTime(value: MaybeRefOrGetter<string | number | Date | undefined | null>,
|
||||||
options: Intl.DateTimeFormatOptions = { dateStyle: 'long', timeStyle: 'medium' }) {
|
options: Intl.DateTimeFormatOptions = { dateStyle: 'long', timeStyle: 'medium' }) {
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
const formatter = $computed(() => Intl.DateTimeFormat(locale.value, options))
|
const formatter = computed(() => Intl.DateTimeFormat(locale.value, options))
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
const v = resolveUnref(value)
|
const v = resolveUnref(value)
|
||||||
return v ? formatter.format(new Date(v)) : ''
|
return v ? formatter.value.format(new Date(v)) : ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const notifications = reactive<Record<string, undefined | [Promise<mastodon.stre
|
||||||
export function useNotifications() {
|
export function useNotifications() {
|
||||||
const id = currentUser.value?.account.id
|
const id = currentUser.value?.account.id
|
||||||
|
|
||||||
const { client, streamingClient } = $(useMasto())
|
const { client, streamingClient } = useMasto()
|
||||||
|
|
||||||
async function clearNotifications() {
|
async function clearNotifications() {
|
||||||
if (!id || !notifications[id])
|
if (!id || !notifications[id])
|
||||||
|
@ -15,7 +15,7 @@ export function useNotifications() {
|
||||||
notifications[id]![1] = []
|
notifications[id]![1] = []
|
||||||
|
|
||||||
if (lastReadId) {
|
if (lastReadId) {
|
||||||
await client.v1.markers.create({
|
await client.value.v1.markers.create({
|
||||||
notifications: { lastReadId },
|
notifications: { lastReadId },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,15 @@ export function useNotifications() {
|
||||||
const streamPromise = new Promise<mastodon.streaming.Subscription>(resolve => resolveStream = resolve)
|
const streamPromise = new Promise<mastodon.streaming.Subscription>(resolve => resolveStream = resolve)
|
||||||
notifications[id] = [streamPromise, []]
|
notifications[id] = [streamPromise, []]
|
||||||
|
|
||||||
await until($$(streamingClient)).toBeTruthy()
|
await until(streamingClient).toBeTruthy()
|
||||||
|
|
||||||
const stream = streamingClient!.user.subscribe()
|
const stream = streamingClient.value!.user.subscribe()
|
||||||
resolveStream!(stream)
|
resolveStream!(stream)
|
||||||
|
|
||||||
processNotifications(stream, id)
|
processNotifications(stream, id)
|
||||||
|
|
||||||
const position = await client.v1.markers.fetch({ timeline: ['notifications'] })
|
const position = await client.value.v1.markers.fetch({ timeline: ['notifications'] })
|
||||||
const paginator = client.v1.notifications.list({ limit: 30 })
|
const paginator = client.value.v1.notifications.list({ limit: 30 })
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const result = await paginator.next()
|
const result = await paginator.next()
|
||||||
|
|
|
@ -10,69 +10,68 @@ export function usePublish(options: {
|
||||||
isUploading: Ref<boolean>
|
isUploading: Ref<boolean>
|
||||||
initialDraft: Ref<() => Draft>
|
initialDraft: Ref<() => Draft>
|
||||||
}) {
|
}) {
|
||||||
const { expanded, isUploading, initialDraft } = $(options)
|
const { draft, isEmpty } = options.draftState
|
||||||
let { draft, isEmpty } = $(options.draftState)
|
const { client } = useMasto()
|
||||||
const { client } = $(useMasto())
|
|
||||||
const settings = useUserSettings()
|
const settings = useUserSettings()
|
||||||
|
|
||||||
const preferredLanguage = $computed(() => (currentUser.value?.account.source.language || settings.value?.language || 'en').split('-')[0])
|
const preferredLanguage = computed(() => (currentUser.value?.account.source.language || settings.value?.language || 'en').split('-')[0])
|
||||||
|
|
||||||
let isSending = $ref(false)
|
const isSending = ref(false)
|
||||||
const isExpanded = $ref(false)
|
const isExpanded = ref(false)
|
||||||
const failedMessages = $ref<string[]>([])
|
const failedMessages = ref<string[]>([])
|
||||||
|
|
||||||
const publishSpoilerText = $computed({
|
const publishSpoilerText = computed({
|
||||||
get() {
|
get() {
|
||||||
return draft.params.sensitive ? draft.params.spoilerText : ''
|
return draft.value.params.sensitive ? draft.value.params.spoilerText : ''
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
if (!draft.params.sensitive)
|
if (!draft.value.params.sensitive)
|
||||||
return
|
return
|
||||||
draft.params.spoilerText = val
|
draft.value.params.spoilerText = val
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const shouldExpanded = $computed(() => expanded || isExpanded || !isEmpty)
|
const shouldExpanded = computed(() => options.expanded.value || isExpanded.value || !isEmpty.value)
|
||||||
const isPublishDisabled = $computed(() => {
|
const isPublishDisabled = computed(() => {
|
||||||
const firstEmptyInputIndex = draft.params.poll?.options.findIndex(option => option.trim().length === 0)
|
const { params, attachments } = draft.value
|
||||||
|
const firstEmptyInputIndex = params.poll?.options.findIndex(option => option.trim().length === 0)
|
||||||
return isEmpty
|
return isEmpty.value
|
||||||
|| isUploading
|
|| options.isUploading.value
|
||||||
|| isSending
|
|| isSending.value
|
||||||
|| (draft.attachments.length === 0 && !draft.params.status)
|
|| (attachments.length === 0 && !params.status)
|
||||||
|| failedMessages.length > 0
|
|| failedMessages.value.length > 0
|
||||||
|| (draft.attachments.length > 0 && draft.params.poll !== null && draft.params.poll !== undefined)
|
|| (attachments.length > 0 && params.poll !== null && params.poll !== undefined)
|
||||||
|| ((draft.params.poll !== null && draft.params.poll !== undefined)
|
|| ((params.poll !== null && params.poll !== undefined)
|
||||||
&& (
|
&& (
|
||||||
(firstEmptyInputIndex !== -1
|
(firstEmptyInputIndex !== -1
|
||||||
&& firstEmptyInputIndex !== draft.params.poll.options.length - 1
|
&& firstEmptyInputIndex !== params.poll.options.length - 1
|
||||||
)
|
)
|
||||||
|| draft.params.poll.options.findLastIndex(option => option.trim().length > 0) + 1 < 2
|
|| params.poll.options.findLastIndex(option => option.trim().length > 0) + 1 < 2
|
||||||
|| (new Set(draft.params.poll.options).size !== draft.params.poll.options.length)
|
|| (new Set(params.poll.options).size !== params.poll.options.length)
|
||||||
|| (currentInstance.value?.configuration?.polls.maxCharactersPerOption !== undefined
|
|| (currentInstance.value?.configuration?.polls.maxCharactersPerOption !== undefined
|
||||||
&& draft.params.poll.options.find(option => option.length > currentInstance.value!.configuration!.polls.maxCharactersPerOption) !== undefined
|
&& params.poll.options.find(option => option.length > currentInstance.value!.configuration!.polls.maxCharactersPerOption) !== undefined
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => draft, () => {
|
watch(() => draft, () => {
|
||||||
if (failedMessages.length > 0)
|
if (failedMessages.value.length > 0)
|
||||||
failedMessages.length = 0
|
failedMessages.value.length = 0
|
||||||
}, { deep: true })
|
}, { deep: true })
|
||||||
|
|
||||||
async function publishDraft() {
|
async function publishDraft() {
|
||||||
if (isPublishDisabled)
|
if (isPublishDisabled.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
let content = htmlToText(draft.params.status || '')
|
let content = htmlToText(draft.value.params.status || '')
|
||||||
if (draft.mentions?.length)
|
if (draft.value.mentions?.length)
|
||||||
content = `${draft.mentions.map(i => `@${i}`).join(' ')} ${content}`
|
content = `${draft.value.mentions.map(i => `@${i}`).join(' ')} ${content}`
|
||||||
|
|
||||||
let poll
|
let poll
|
||||||
|
|
||||||
if (draft.params.poll) {
|
if (draft.value.params.poll) {
|
||||||
let options = draft.params.poll.options
|
let options = draft.value.params.poll.options
|
||||||
|
|
||||||
if (currentInstance.value?.configuration !== undefined
|
if (currentInstance.value?.configuration !== undefined
|
||||||
&& (
|
&& (
|
||||||
|
@ -82,15 +81,15 @@ export function usePublish(options: {
|
||||||
)
|
)
|
||||||
options = options.slice(0, options.length - 1)
|
options = options.slice(0, options.length - 1)
|
||||||
|
|
||||||
poll = { ...draft.params.poll, options }
|
poll = { ...draft.value.params.poll, options }
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...draft.params,
|
...draft.value.params,
|
||||||
spoilerText: publishSpoilerText,
|
spoilerText: publishSpoilerText.value,
|
||||||
status: content,
|
status: content,
|
||||||
mediaIds: draft.attachments.map(a => a.id),
|
mediaIds: draft.value.attachments.map(a => a.id),
|
||||||
language: draft.params.language || preferredLanguage,
|
language: draft.value.params.language || preferredLanguage.value,
|
||||||
poll,
|
poll,
|
||||||
...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}),
|
...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}),
|
||||||
} as mastodon.rest.v1.CreateStatusParams
|
} as mastodon.rest.v1.CreateStatusParams
|
||||||
|
@ -98,7 +97,7 @@ export function usePublish(options: {
|
||||||
if (process.dev) {
|
if (process.dev) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.info({
|
console.info({
|
||||||
raw: draft.params.status,
|
raw: draft.value.params.status,
|
||||||
...payload,
|
...payload,
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line no-alert
|
// eslint-disable-next-line no-alert
|
||||||
|
@ -108,39 +107,39 @@ export function usePublish(options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isSending = true
|
isSending.value = true
|
||||||
|
|
||||||
let status: mastodon.v1.Status
|
let status: mastodon.v1.Status
|
||||||
if (!draft.editingStatus) {
|
if (!draft.value.editingStatus) {
|
||||||
status = await client.v1.statuses.create(payload)
|
status = await client.value.v1.statuses.create(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
status = await client.v1.statuses.$select(draft.editingStatus.id).update({
|
status = await client.value.v1.statuses.$select(draft.value.editingStatus.id).update({
|
||||||
...payload,
|
...payload,
|
||||||
mediaAttributes: draft.attachments.map(media => ({
|
mediaAttributes: draft.value.attachments.map(media => ({
|
||||||
id: media.id,
|
id: media.id,
|
||||||
description: media.description,
|
description: media.description,
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (draft.params.inReplyToId)
|
if (draft.value.params.inReplyToId)
|
||||||
navigateToStatus({ status })
|
navigateToStatus({ status })
|
||||||
|
|
||||||
draft = initialDraft()
|
draft.value = options.initialDraft.value()
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
failedMessages.push((err as Error).message)
|
failedMessages.value.push((err as Error).message)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
isSending = false
|
isSending.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $$({
|
return {
|
||||||
isSending,
|
isSending,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
shouldExpanded,
|
shouldExpanded,
|
||||||
|
@ -149,22 +148,21 @@ export function usePublish(options: {
|
||||||
preferredLanguage,
|
preferredLanguage,
|
||||||
publishSpoilerText,
|
publishSpoilerText,
|
||||||
publishDraft,
|
publishDraft,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MediaAttachmentUploadError = [filename: string, message: string]
|
export type MediaAttachmentUploadError = [filename: string, message: string]
|
||||||
|
|
||||||
export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
export function useUploadMediaAttachment(draft: Ref<Draft>) {
|
||||||
const draft = $(draftRef)
|
const { client } = useMasto()
|
||||||
const { client } = $(useMasto())
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
let isUploading = $ref<boolean>(false)
|
const isUploading = ref<boolean>(false)
|
||||||
let isExceedingAttachmentLimit = $ref<boolean>(false)
|
const isExceedingAttachmentLimit = ref<boolean>(false)
|
||||||
let failedAttachments = $ref<MediaAttachmentUploadError[]>([])
|
const failedAttachments = ref<MediaAttachmentUploadError[]>([])
|
||||||
const dropZoneRef = ref<HTMLDivElement>()
|
const dropZoneRef = ref<HTMLDivElement>()
|
||||||
|
|
||||||
const maxPixels = $computed(() => {
|
const maxPixels = computed(() => {
|
||||||
return currentInstance.value?.configuration?.mediaAttachments?.imageMatrixLimit
|
return currentInstance.value?.configuration?.mediaAttachments?.imageMatrixLimit
|
||||||
?? 4096 ** 2
|
?? 4096 ** 2
|
||||||
})
|
})
|
||||||
|
@ -186,8 +184,8 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
|
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
|
|
||||||
const resizedWidth = canvas.width = Math.round(Math.sqrt(maxPixels * aspectRatio))
|
const resizedWidth = canvas.width = Math.round(Math.sqrt(maxPixels.value * aspectRatio))
|
||||||
const resizedHeight = canvas.height = Math.round(Math.sqrt(maxPixels / aspectRatio))
|
const resizedHeight = canvas.height = Math.round(Math.sqrt(maxPixels.value / aspectRatio))
|
||||||
|
|
||||||
const context = canvas.getContext('2d')
|
const context = canvas.getContext('2d')
|
||||||
|
|
||||||
|
@ -202,7 +200,7 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
try {
|
try {
|
||||||
const image = await loadImage(file) as HTMLImageElement
|
const image = await loadImage(file) as HTMLImageElement
|
||||||
|
|
||||||
if (image.width * image.height > maxPixels)
|
if (image.width * image.height > maxPixels.value)
|
||||||
file = await resizeImage(image, file.type) as File
|
file = await resizeImage(image, file.type) as File
|
||||||
|
|
||||||
return file
|
return file
|
||||||
|
@ -222,32 +220,32 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadAttachments(files: File[]) {
|
async function uploadAttachments(files: File[]) {
|
||||||
isUploading = true
|
isUploading.value = true
|
||||||
failedAttachments = []
|
failedAttachments.value = []
|
||||||
// TODO: display some kind of message if too many media are selected
|
// TODO: display some kind of message if too many media are selected
|
||||||
// DONE
|
// DONE
|
||||||
const limit = currentInstance.value!.configuration?.statuses.maxMediaAttachments || 4
|
const limit = currentInstance.value!.configuration?.statuses.maxMediaAttachments || 4
|
||||||
for (const file of files.slice(0, limit)) {
|
for (const file of files.slice(0, limit)) {
|
||||||
if (draft.attachments.length < limit) {
|
if (draft.value.attachments.length < limit) {
|
||||||
isExceedingAttachmentLimit = false
|
isExceedingAttachmentLimit.value = false
|
||||||
try {
|
try {
|
||||||
const attachment = await client.v1.media.create({
|
const attachment = await client.value.v1.media.create({
|
||||||
file: await processFile(file),
|
file: await processFile(file),
|
||||||
})
|
})
|
||||||
draft.attachments.push(attachment)
|
draft.value.attachments.push(attachment)
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// TODO: add some human-readable error message, problem is that masto api will not return response code
|
// TODO: add some human-readable error message, problem is that masto api will not return response code
|
||||||
console.error(e)
|
console.error(e)
|
||||||
failedAttachments = [...failedAttachments, [file.name, (e as Error).message]]
|
failedAttachments.value = [...failedAttachments.value, [file.name, (e as Error).message]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
isExceedingAttachmentLimit = true
|
isExceedingAttachmentLimit.value = true
|
||||||
failedAttachments = [...failedAttachments, [file.name, t('state.attachments_limit_error')]]
|
failedAttachments.value = [...failedAttachments.value, [file.name, t('state.attachments_limit_error')]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isUploading = false
|
isUploading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pickAttachments() {
|
async function pickAttachments() {
|
||||||
|
@ -264,12 +262,12 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
|
|
||||||
async function setDescription(att: mastodon.v1.MediaAttachment, description: string) {
|
async function setDescription(att: mastodon.v1.MediaAttachment, description: string) {
|
||||||
att.description = description
|
att.description = description
|
||||||
if (!draft.editingStatus)
|
if (!draft.value.editingStatus)
|
||||||
await client.v1.media.$select(att.id).update({ description: att.description })
|
await client.value.v1.media.$select(att.id).update({ description: att.description })
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeAttachment(index: number) {
|
function removeAttachment(index: number) {
|
||||||
draft.attachments.splice(index, 1)
|
draft.value.attachments.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onDrop(files: File[] | null) {
|
async function onDrop(files: File[] | null) {
|
||||||
|
@ -279,7 +277,7 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
|
|
||||||
const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
|
const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
|
||||||
|
|
||||||
return $$({
|
return {
|
||||||
isUploading,
|
isUploading,
|
||||||
isExceedingAttachmentLimit,
|
isExceedingAttachmentLimit,
|
||||||
isOverDropZone,
|
isOverDropZone,
|
||||||
|
@ -291,5 +289,5 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
pickAttachments,
|
pickAttachments,
|
||||||
setDescription,
|
setDescription,
|
||||||
removeAttachment,
|
removeAttachment,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ async function fetchRelationships() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleFollowAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
export async function toggleFollowAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const i18n = useNuxtApp().$i18n
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
const unfollow = relationship!.following || relationship!.requested
|
const unfollow = relationship!.following || relationship!.requested
|
||||||
|
@ -59,11 +59,11 @@ export async function toggleFollowAccount(relationship: mastodon.v1.Relationship
|
||||||
relationship!.following = true
|
relationship!.following = true
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship = await client.v1.accounts.$select(account.id)[unfollow ? 'unfollow' : 'follow']()
|
relationship = await client.value.v1.accounts.$select(account.id)[unfollow ? 'unfollow' : 'follow']()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleMuteAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
export async function toggleMuteAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const i18n = useNuxtApp().$i18n
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
if (!relationship!.muting && await openConfirmDialog({
|
if (!relationship!.muting && await openConfirmDialog({
|
||||||
|
@ -76,14 +76,14 @@ export async function toggleMuteAccount(relationship: mastodon.v1.Relationship,
|
||||||
|
|
||||||
relationship!.muting = !relationship!.muting
|
relationship!.muting = !relationship!.muting
|
||||||
relationship = relationship!.muting
|
relationship = relationship!.muting
|
||||||
? await client.v1.accounts.$select(account.id).mute({
|
? await client.value.v1.accounts.$select(account.id).mute({
|
||||||
// TODO support more options
|
// TODO support more options
|
||||||
})
|
})
|
||||||
: await client.v1.accounts.$select(account.id).unmute()
|
: await client.value.v1.accounts.$select(account.id).unmute()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleBlockAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
export async function toggleBlockAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const i18n = useNuxtApp().$i18n
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
if (!relationship!.blocking && await openConfirmDialog({
|
if (!relationship!.blocking && await openConfirmDialog({
|
||||||
|
@ -95,11 +95,11 @@ export async function toggleBlockAccount(relationship: mastodon.v1.Relationship,
|
||||||
return
|
return
|
||||||
|
|
||||||
relationship!.blocking = !relationship!.blocking
|
relationship!.blocking = !relationship!.blocking
|
||||||
relationship = await client.v1.accounts.$select(account.id)[relationship!.blocking ? 'block' : 'unblock']()
|
relationship = await client.value.v1.accounts.$select(account.id)[relationship!.blocking ? 'block' : 'unblock']()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleBlockDomain(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
export async function toggleBlockDomain(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const i18n = useNuxtApp().$i18n
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
if (!relationship!.domainBlocking && await openConfirmDialog({
|
if (!relationship!.domainBlocking && await openConfirmDialog({
|
||||||
|
@ -111,5 +111,5 @@ export async function toggleBlockDomain(relationship: mastodon.v1.Relationship,
|
||||||
return
|
return
|
||||||
|
|
||||||
relationship!.domainBlocking = !relationship!.domainBlocking
|
relationship!.domainBlocking = !relationship!.domainBlocking
|
||||||
await client.v1.domainBlocks[relationship!.domainBlocking ? 'create' : 'remove']({ domain: getServerName(account) })
|
await client.value.v1.domainBlocks[relationship!.domainBlocking ? 'create' : 'remove']({ domain: getServerName(account) })
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,13 @@ export type SearchResult = HashTagSearchResult | AccountSearchResult | StatusSea
|
||||||
|
|
||||||
export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOptions = {}) {
|
export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOptions = {}) {
|
||||||
const done = ref(false)
|
const done = ref(false)
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const accounts = ref<AccountSearchResult[]>([])
|
const accounts = ref<AccountSearchResult[]>([])
|
||||||
const hashtags = ref<HashTagSearchResult[]>([])
|
const hashtags = ref<HashTagSearchResult[]>([])
|
||||||
const statuses = ref<StatusSearchResult[]>([])
|
const statuses = ref<StatusSearchResult[]>([])
|
||||||
|
|
||||||
const q = $computed(() => resolveUnref(query).trim())
|
const q = computed(() => resolveUnref(query).trim())
|
||||||
|
|
||||||
let paginator: mastodon.Paginator<mastodon.v2.Search, mastodon.rest.v2.SearchParams> | undefined
|
let paginator: mastodon.Paginator<mastodon.v2.Search, mastodon.rest.v2.SearchParams> | undefined
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOpt
|
||||||
* Based on the source it seems like modifying the params when calling next would result in a new search,
|
* Based on the source it seems like modifying the params when calling next would result in a new search,
|
||||||
* but that doesn't seem to be the case. So instead we just create a new paginator with the new params.
|
* but that doesn't seem to be the case. So instead we just create a new paginator with the new params.
|
||||||
*/
|
*/
|
||||||
paginator = client.v2.search.list({
|
paginator = client.value.v2.search.list({
|
||||||
q,
|
q: q.value,
|
||||||
...resolveUnref(options),
|
...resolveUnref(options),
|
||||||
resolve: !!currentUser.value,
|
resolve: !!currentUser.value,
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,17 +8,17 @@ export interface StatusActionsProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useStatusActions(props: StatusActionsProps) {
|
export function useStatusActions(props: StatusActionsProps) {
|
||||||
let status = $ref<mastodon.v1.Status>({ ...props.status })
|
const status = ref<mastodon.v1.Status>({ ...props.status })
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.status,
|
() => props.status,
|
||||||
val => status = { ...val },
|
val => status.value = { ...val },
|
||||||
{ deep: true, immediate: true },
|
{ deep: true, immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use different states to let the user press different actions right after the other
|
// Use different states to let the user press different actions right after the other
|
||||||
const isLoading = $ref({
|
const isLoading = ref({
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
|
@ -32,10 +32,10 @@ export function useStatusActions(props: StatusActionsProps) {
|
||||||
if (!checkLogin())
|
if (!checkLogin())
|
||||||
return
|
return
|
||||||
|
|
||||||
const prevCount = countField ? status[countField] : undefined
|
const prevCount = countField ? status.value[countField] : undefined
|
||||||
|
|
||||||
isLoading[action] = true
|
isLoading.value[action] = true
|
||||||
const isCancel = status[action]
|
const isCancel = status.value[action]
|
||||||
fetchNewStatus().then((newStatus) => {
|
fetchNewStatus().then((newStatus) => {
|
||||||
// when the action is cancelled, the count is not updated highly likely (if they're the same)
|
// when the action is cancelled, the count is not updated highly likely (if they're the same)
|
||||||
// issue of Mastodon API
|
// issue of Mastodon API
|
||||||
|
@ -45,24 +45,24 @@ export function useStatusActions(props: StatusActionsProps) {
|
||||||
Object.assign(status, newStatus)
|
Object.assign(status, newStatus)
|
||||||
cacheStatus(newStatus, undefined, true)
|
cacheStatus(newStatus, undefined, true)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
isLoading[action] = false
|
isLoading.value[action] = false
|
||||||
})
|
})
|
||||||
// Optimistic update
|
// Optimistic update
|
||||||
status[action] = !status[action]
|
status.value[action] = !status.value[action]
|
||||||
cacheStatus(status, undefined, true)
|
cacheStatus(status.value, undefined, true)
|
||||||
if (countField)
|
if (countField)
|
||||||
status[countField] += status[action] ? 1 : -1
|
status.value[countField] += status.value[action] ? 1 : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
const canReblog = $computed(() =>
|
const canReblog = computed(() =>
|
||||||
status.visibility !== 'direct'
|
status.value.visibility !== 'direct'
|
||||||
&& (status.visibility !== 'private' || status.account.id === currentUser.value?.account.id),
|
&& (status.value.visibility !== 'private' || status.value.account.id === currentUser.value?.account.id),
|
||||||
)
|
)
|
||||||
|
|
||||||
const toggleReblog = () => toggleStatusAction(
|
const toggleReblog = () => toggleStatusAction(
|
||||||
'reblogged',
|
'reblogged',
|
||||||
() => client.v1.statuses.$select(status.id)[status.reblogged ? 'unreblog' : 'reblog']().then((res) => {
|
() => client.value.v1.statuses.$select(status.value.id)[status.value.reblogged ? 'unreblog' : 'reblog']().then((res) => {
|
||||||
if (status.reblogged)
|
if (status.value.reblogged)
|
||||||
// returns the original status
|
// returns the original status
|
||||||
return res.reblog!
|
return res.reblog!
|
||||||
return res
|
return res
|
||||||
|
@ -72,29 +72,29 @@ export function useStatusActions(props: StatusActionsProps) {
|
||||||
|
|
||||||
const toggleFavourite = () => toggleStatusAction(
|
const toggleFavourite = () => toggleStatusAction(
|
||||||
'favourited',
|
'favourited',
|
||||||
() => client.v1.statuses.$select(status.id)[status.favourited ? 'unfavourite' : 'favourite'](),
|
() => client.value.v1.statuses.$select(status.value.id)[status.value.favourited ? 'unfavourite' : 'favourite'](),
|
||||||
'favouritesCount',
|
'favouritesCount',
|
||||||
)
|
)
|
||||||
|
|
||||||
const toggleBookmark = () => toggleStatusAction(
|
const toggleBookmark = () => toggleStatusAction(
|
||||||
'bookmarked',
|
'bookmarked',
|
||||||
() => client.v1.statuses.$select(status.id)[status.bookmarked ? 'unbookmark' : 'bookmark'](),
|
() => client.value.v1.statuses.$select(status.value.id)[status.value.bookmarked ? 'unbookmark' : 'bookmark'](),
|
||||||
)
|
)
|
||||||
|
|
||||||
const togglePin = async () => toggleStatusAction(
|
const togglePin = async () => toggleStatusAction(
|
||||||
'pinned',
|
'pinned',
|
||||||
() => client.v1.statuses.$select(status.id)[status.pinned ? 'unpin' : 'pin'](),
|
() => client.value.v1.statuses.$select(status.value.id)[status.value.pinned ? 'unpin' : 'pin'](),
|
||||||
)
|
)
|
||||||
|
|
||||||
const toggleMute = async () => toggleStatusAction(
|
const toggleMute = async () => toggleStatusAction(
|
||||||
'muted',
|
'muted',
|
||||||
() => client.v1.statuses.$select(status.id)[status.muted ? 'unmute' : 'mute'](),
|
() => client.value.v1.statuses.$select(status.value.id)[status.value.muted ? 'unmute' : 'mute'](),
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: $$(status),
|
status,
|
||||||
isLoading: $$(isLoading),
|
isLoading,
|
||||||
canReblog: $$(canReblog),
|
canReblog,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
toggleReblog,
|
toggleReblog,
|
||||||
toggleFavourite,
|
toggleFavourite,
|
||||||
|
|
|
@ -60,7 +60,7 @@ interface TranslationErr {
|
||||||
|
|
||||||
export async function translateText(text: string, from: string | null | undefined, to: string) {
|
export async function translateText(text: string, from: string | null | undefined, to: string) {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const status = $ref({
|
const status = ref({
|
||||||
success: false,
|
success: false,
|
||||||
error: '',
|
error: '',
|
||||||
text: '',
|
text: '',
|
||||||
|
@ -77,9 +77,9 @@ export async function translateText(text: string, from: string | null | undefine
|
||||||
api_key: '',
|
api_key: '',
|
||||||
},
|
},
|
||||||
}) as TranslationResponse
|
}) as TranslationResponse
|
||||||
status.success = true
|
status.value.success = true
|
||||||
// replace the translated links with the original
|
// replace the translated links with the original
|
||||||
status.text = response.translatedText.replace(regex, (match) => {
|
status.value.text = response.translatedText.replace(regex, (match) => {
|
||||||
const tagLink = regex.exec(text)
|
const tagLink = regex.exec(text)
|
||||||
return tagLink ? tagLink[0] : match
|
return tagLink ? tagLink[0] : match
|
||||||
})
|
})
|
||||||
|
@ -87,9 +87,9 @@ export async function translateText(text: string, from: string | null | undefine
|
||||||
catch (err) {
|
catch (err) {
|
||||||
// TODO: improve type
|
// TODO: improve type
|
||||||
if ((err as TranslationErr).data?.error)
|
if ((err as TranslationErr).data?.error)
|
||||||
status.error = (err as TranslationErr).data!.error!
|
status.value.error = (err as TranslationErr).data!.error!
|
||||||
else
|
else
|
||||||
status.error = 'Unknown Error, Please check your console in browser devtool.'
|
status.value.error = 'Unknown Error, Please check your console in browser devtool.'
|
||||||
console.error('Translate Post Error: ', err)
|
console.error('Translate Post Error: ', err)
|
||||||
}
|
}
|
||||||
return status
|
return status
|
||||||
|
@ -115,10 +115,10 @@ export function useTranslation(status: mastodon.v1.Status | mastodon.v1.StatusEd
|
||||||
return
|
return
|
||||||
|
|
||||||
if (!translation.text) {
|
if (!translation.text) {
|
||||||
const { success, text, error } = await translateText(status.content, status.language, to)
|
const translated = await translateText(status.content, status.language, to)
|
||||||
translation.error = error
|
translation.error = translated.value.error
|
||||||
translation.text = text
|
translation.text = translated.value.text
|
||||||
translation.success = success
|
translation.success = translated.value.success
|
||||||
}
|
}
|
||||||
|
|
||||||
translation.visible = !translation.visible
|
translation.visible = !translation.visible
|
||||||
|
|
|
@ -19,8 +19,8 @@ export function usePaginator<T, P, U = T>(
|
||||||
const prevItems = ref<T[]>([])
|
const prevItems = ref<T[]>([])
|
||||||
|
|
||||||
const endAnchor = ref<HTMLDivElement>()
|
const endAnchor = ref<HTMLDivElement>()
|
||||||
const bound = reactive(useElementBounding(endAnchor))
|
const bound = useElementBounding(endAnchor)
|
||||||
const isInScreen = $computed(() => bound.top < window.innerHeight * 2)
|
const isInScreen = computed(() => bound.top.value < window.innerHeight * 2)
|
||||||
const error = ref<unknown | undefined>()
|
const error = ref<unknown | undefined>()
|
||||||
const deactivated = useDeactivated()
|
const deactivated = useDeactivated()
|
||||||
|
|
||||||
|
@ -29,11 +29,11 @@ export function usePaginator<T, P, U = T>(
|
||||||
prevItems.value = []
|
prevItems.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(stream, async (stream) => {
|
watch(() => stream, async (stream) => {
|
||||||
if (!stream)
|
if (!stream.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
for await (const entry of stream) {
|
for await (const entry of stream.value) {
|
||||||
if (entry.event === 'update') {
|
if (entry.event === 'update') {
|
||||||
const status = entry.payload
|
const status = entry.payload
|
||||||
|
|
||||||
|
@ -115,11 +115,10 @@ export function usePaginator<T, P, U = T>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watchEffect(
|
||||||
() => [isInScreen, state],
|
|
||||||
() => {
|
() => {
|
||||||
if (
|
if (
|
||||||
isInScreen
|
isInScreen.value
|
||||||
&& state.value === 'idle'
|
&& state.value === 'idle'
|
||||||
// No new content is loaded when the keepAlive page enters the background
|
// No new content is loaded when the keepAlive page enters the background
|
||||||
&& deactivated.value === false
|
&& deactivated.value === false
|
||||||
|
|
|
@ -14,7 +14,7 @@ const supportsPushNotifications = typeof window !== 'undefined'
|
||||||
&& 'getKey' in PushSubscription.prototype
|
&& 'getKey' in PushSubscription.prototype
|
||||||
|
|
||||||
export function usePushManager() {
|
export function usePushManager() {
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const isSubscribed = ref(false)
|
const isSubscribed = ref(false)
|
||||||
const notificationPermission = ref<PermissionState | undefined>(
|
const notificationPermission = ref<PermissionState | undefined>(
|
||||||
Notification.permission === 'denied'
|
Notification.permission === 'denied'
|
||||||
|
@ -25,7 +25,7 @@ export function usePushManager() {
|
||||||
? 'prompt'
|
? 'prompt'
|
||||||
: undefined,
|
: undefined,
|
||||||
)
|
)
|
||||||
const isSupported = $computed(() => supportsPushNotifications)
|
const isSupported = computed(() => supportsPushNotifications)
|
||||||
const hiddenNotification = useLocalStorage<PushNotificationRequest>(STORAGE_KEY_NOTIFICATION, {})
|
const hiddenNotification = useLocalStorage<PushNotificationRequest>(STORAGE_KEY_NOTIFICATION, {})
|
||||||
const configuredPolicy = useLocalStorage<PushNotificationPolicy>(STORAGE_KEY_NOTIFICATION_POLICY, {})
|
const configuredPolicy = useLocalStorage<PushNotificationPolicy>(STORAGE_KEY_NOTIFICATION_POLICY, {})
|
||||||
const pushNotificationData = ref(createRawSettings(
|
const pushNotificationData = ref(createRawSettings(
|
||||||
|
@ -173,7 +173,7 @@ export function usePushManager() {
|
||||||
if (policyChanged)
|
if (policyChanged)
|
||||||
await subscribe(data, policy, true)
|
await subscribe(data, policy, true)
|
||||||
else
|
else
|
||||||
currentUser.value.pushSubscription = await client.v1.push.subscription.update({ data })
|
currentUser.value.pushSubscription = await client.value.v1.push.subscription.update({ data })
|
||||||
|
|
||||||
policyChanged && await nextTick()
|
policyChanged && await nextTick()
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ export function useSelfAccount(user: MaybeRefOrGetter<mastodon.v1.Account | unde
|
||||||
export const characterLimit = computed(() => currentInstance.value?.configuration?.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT)
|
export const characterLimit = computed(() => currentInstance.value?.configuration?.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT)
|
||||||
|
|
||||||
export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>) {
|
export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>) {
|
||||||
const { client } = $(masto)
|
const { client } = masto
|
||||||
const instance = mastoLogin(masto, user)
|
const instance = mastoLogin(masto, user)
|
||||||
|
|
||||||
// GoToSocial only API
|
// GoToSocial only API
|
||||||
|
@ -145,11 +145,11 @@ export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { acco
|
||||||
currentUserHandle.value = account.acct
|
currentUserHandle.value = account.acct
|
||||||
|
|
||||||
const [me, pushSubscription] = await Promise.all([
|
const [me, pushSubscription] = await Promise.all([
|
||||||
fetchAccountInfo(client, user.server),
|
fetchAccountInfo(client.value, user.server),
|
||||||
// if PWA is not enabled, don't get push subscription
|
// if PWA is not enabled, don't get push subscription
|
||||||
useAppConfig().pwaEnabled
|
useAppConfig().pwaEnabled
|
||||||
// we get 404 response instead empty data
|
// we get 404 response instead empty data
|
||||||
? client.v1.push.subscription.fetch().catch(() => Promise.resolve(undefined))
|
? client.value.v1.push.subscription.fetch().catch(() => Promise.resolve(undefined))
|
||||||
: Promise.resolve(undefined),
|
: Promise.resolve(undefined),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,13 @@ export default defineNuxtConfig({
|
||||||
],
|
],
|
||||||
vue: {
|
vue: {
|
||||||
defineModel: true,
|
defineModel: true,
|
||||||
|
propsDestructure: true,
|
||||||
},
|
},
|
||||||
macros: {
|
macros: {
|
||||||
setupSFC: true,
|
setupSFC: true,
|
||||||
betterDefine: false,
|
betterDefine: false,
|
||||||
defineModels: false,
|
defineModels: false,
|
||||||
|
reactivityTransform: false,
|
||||||
},
|
},
|
||||||
devtools: {
|
devtools: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|
|
@ -11,18 +11,18 @@ definePageMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = $(computedEager(() => route.params.status as string))
|
const id = computedEager(() => route.params.status as string)
|
||||||
const main = ref<ComponentPublicInstance | null>(null)
|
const main = ref<ComponentPublicInstance | null>(null)
|
||||||
|
|
||||||
const { data: status, pending, refresh: refreshStatus } = useAsyncData(
|
const { data: status, pending, refresh: refreshStatus } = useAsyncData(
|
||||||
`status:${id}`,
|
`status:${id.value}`,
|
||||||
() => fetchStatus(id, true),
|
() => fetchStatus(id.value, true),
|
||||||
{ watch: [isHydrated], immediate: isHydrated.value, default: () => shallowRef() },
|
{ watch: [isHydrated], immediate: isHydrated.value, default: () => shallowRef() },
|
||||||
)
|
)
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const { data: context, pending: pendingContext, refresh: refreshContext } = useAsyncData(
|
const { data: context, pending: pendingContext, refresh: refreshContext } = useAsyncData(
|
||||||
`context:${id}`,
|
`context:${id}`,
|
||||||
async () => client.v1.statuses.$select(id).context.fetch(),
|
async () => client.value.v1.statuses.$select(id.value).context.fetch(),
|
||||||
{ watch: [isHydrated], immediate: isHydrated.value, lazy: true, default: () => shallowRef() },
|
{ watch: [isHydrated], immediate: isHydrated.value, lazy: true, default: () => shallowRef() },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ watch(publishWidget, () => {
|
||||||
focusEditor()
|
focusEditor()
|
||||||
})
|
})
|
||||||
|
|
||||||
const replyDraft = $computed(() => status.value ? getReplyDraft(status.value) : null)
|
const replyDraft = computed(() => status.value ? getReplyDraft(status.value) : null)
|
||||||
|
|
||||||
onReactivated(() => {
|
onReactivated(() => {
|
||||||
// Silently update data when reentering the page
|
// Silently update data when reentering the page
|
||||||
|
|
|
@ -4,12 +4,12 @@ definePageMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const accountName = $(computedEager(() => toShortHandle(params.account as string)))
|
const accountName = computedEager(() => toShortHandle(params.account as string))
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const { data: account, pending, refresh } = $(await useAsyncData(() => fetchAccountByHandle(accountName).catch(() => null), { immediate: process.client, default: () => shallowRef() }))
|
const { data: account, pending, refresh } = await useAsyncData(() => fetchAccountByHandle(accountName.value).catch(() => null), { immediate: process.client, default: () => shallowRef() })
|
||||||
const relationship = $computed(() => account ? useRelationship(account).value : undefined)
|
const relationship = computed(() => account ? useRelationship(account.value).value : undefined)
|
||||||
|
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const handle = $(computedEager(() => params.account as string))
|
const handle = computedEager(() => params.account as string)
|
||||||
|
|
||||||
definePageMeta({ name: 'account-followers' })
|
definePageMeta({ name: 'account-followers' })
|
||||||
|
|
||||||
const account = await fetchAccountByHandle(handle)
|
const account = await fetchAccountByHandle(handle.value)
|
||||||
const paginator = account ? useMastoClient().v1.accounts.$select(account.id).followers.list() : null
|
const paginator = account ? useMastoClient().v1.accounts.$select(account.id).followers.list() : null
|
||||||
|
|
||||||
const isSelf = useSelfAccount(account)
|
const isSelf = useSelfAccount(account)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const handle = $(computedEager(() => params.account as string))
|
const handle = computedEager(() => params.account as string)
|
||||||
|
|
||||||
definePageMeta({ name: 'account-following' })
|
definePageMeta({ name: 'account-following' })
|
||||||
|
|
||||||
const account = await fetchAccountByHandle(handle)
|
const account = await fetchAccountByHandle(handle.value)
|
||||||
const paginator = account ? useMastoClient().v1.accounts.$select(account.id).following.list() : null
|
const paginator = account ? useMastoClient().v1.accounts.$select(account.id).following.list() : null
|
||||||
|
|
||||||
const isSelf = useSelfAccount(account)
|
const isSelf = useSelfAccount(account)
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
import type { mastodon } from 'masto'
|
import type { mastodon } from 'masto'
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const handle = $(computedEager(() => params.account as string))
|
const handle = computedEager(() => params.account as string)
|
||||||
|
|
||||||
definePageMeta({ name: 'account-index' })
|
definePageMeta({ name: 'account-index' })
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const account = await fetchAccountByHandle(handle)
|
const account = await fetchAccountByHandle(handle.value)
|
||||||
|
|
||||||
function reorderAndFilter(items: mastodon.v1.Status[]) {
|
function reorderAndFilter(items: mastodon.v1.Status[]) {
|
||||||
return reorderedTimeline(items, 'account')
|
return reorderedTimeline(items, 'account')
|
||||||
|
|
|
@ -3,9 +3,9 @@ definePageMeta({ name: 'account-media' })
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const handle = $(computedEager(() => params.account as string))
|
const handle = computedEager(() => params.account as string)
|
||||||
|
|
||||||
const account = await fetchAccountByHandle(handle)
|
const account = await fetchAccountByHandle(handle.value)
|
||||||
|
|
||||||
const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ onlyMedia: true, excludeReplies: false })
|
const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ onlyMedia: true, excludeReplies: false })
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ definePageMeta({ name: 'account-replies' })
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const handle = $(computedEager(() => params.account as string))
|
const handle = computedEager(() => params.account as string)
|
||||||
|
|
||||||
const account = await fetchAccountByHandle(handle)
|
const account = await fetchAccountByHandle(handle.value)
|
||||||
|
|
||||||
const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ excludeReplies: false })
|
const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ excludeReplies: false })
|
||||||
|
|
||||||
|
|
|
@ -3,20 +3,20 @@ import type { CommonRouteTabOption } from '~/components/common/CommonRouteTabs.v
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const search = $ref<{ input?: HTMLInputElement }>()
|
const search = ref<{ input?: HTMLInputElement }>()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (isMediumOrLargeScreen && route.name === 'explore' && search?.input)
|
if (isMediumOrLargeScreen && route.name === 'explore' && search.value?.input)
|
||||||
search?.input?.focus()
|
search.value?.input?.focus()
|
||||||
})
|
})
|
||||||
onActivated(() =>
|
onActivated(() =>
|
||||||
search?.input?.focus(),
|
search.value?.input?.focus(),
|
||||||
)
|
)
|
||||||
onDeactivated(() => search?.input?.blur())
|
onDeactivated(() => search.value?.input?.blur())
|
||||||
|
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
const tabs = computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
to: isHydrated.value ? `/${currentServer.value}/explore` : '/explore',
|
to: isHydrated.value ? `/${currentServer.value}/explore` : '/explore',
|
||||||
display: isHydrated.value ? t('tab.posts') : '',
|
display: isHydrated.value ? t('tab.posts') : '',
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS } from '~~/constants'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const paginator = client.v1.trends.tags.list({
|
const paginator = client.value.v1.trends.tags.list({
|
||||||
limit: 20,
|
limit: 20,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,14 @@ definePageMeta({
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const list = $computed(() => route.params.list as string)
|
const list = computed(() => route.params.list as string)
|
||||||
const server = $computed(() => (route.params.server ?? currentServer.value) as string)
|
const server = computed(() => (route.params.server ?? currentServer.value) as string)
|
||||||
|
|
||||||
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
const tabs = computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
to: {
|
to: {
|
||||||
name: 'list',
|
name: 'list',
|
||||||
params: { server, list },
|
params: { server: server.value, list: list.value },
|
||||||
},
|
},
|
||||||
display: t('tab.posts'),
|
display: t('tab.posts'),
|
||||||
icon: 'i-ri:list-unordered',
|
icon: 'i-ri:list-unordered',
|
||||||
|
@ -23,7 +23,7 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
to: {
|
to: {
|
||||||
name: 'list-accounts',
|
name: 'list-accounts',
|
||||||
params: { server, list },
|
params: { server: server.value, list: list.value },
|
||||||
},
|
},
|
||||||
display: t('tab.accounts'),
|
display: t('tab.accounts'),
|
||||||
icon: 'i-ri:user-line',
|
icon: 'i-ri:user-line',
|
||||||
|
@ -31,12 +31,12 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.$select(list).fetch(), { default: () => shallowRef() }))
|
const { data: listInfo, refresh } = await useAsyncData(() => client.value.v1.lists.$select(list.value).fetch(), { default: () => shallowRef() })
|
||||||
|
|
||||||
if (listInfo) {
|
if (listInfo) {
|
||||||
useHydratedHead({
|
useHydratedHead({
|
||||||
title: () => `${listInfo.title} | ${route.fullPath.endsWith('/accounts') ? t('tab.accounts') : t('tab.posts')} | ${t('nav.lists')}`,
|
title: () => `${listInfo.value.title} | ${route.fullPath.endsWith('/accounts') ? t('tab.accounts') : t('tab.posts')} | ${t('nav.lists')}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ definePageMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const listId = $(computedEager(() => params.list as string))
|
const listId = computedEager(() => params.list as string)
|
||||||
|
|
||||||
const paginator = useMastoClient().v1.lists.$select(listId).accounts.list()
|
const paginator = useMastoClient().v1.lists.$select(listId.value).accounts.list()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -4,12 +4,12 @@ definePageMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const listId = $(computedEager(() => params.list as string))
|
const listId = computedEager(() => params.list as string)
|
||||||
|
|
||||||
const client = useMastoClient()
|
const client = useMastoClient()
|
||||||
|
|
||||||
const paginator = client.v1.timelines.list.$select(listId).list()
|
const paginator = client.v1.timelines.list.$select(listId.value).list()
|
||||||
const stream = useStreaming(client => client.list.subscribe({ list: listId }))
|
const stream = useStreaming(client => client.list.subscribe({ list: listId.value }))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -17,17 +17,17 @@ useHydratedHead({
|
||||||
|
|
||||||
const paginatorRef = ref()
|
const paginatorRef = ref()
|
||||||
const inputRef = ref<HTMLInputElement>()
|
const inputRef = ref<HTMLInputElement>()
|
||||||
let actionError = $ref<string | undefined>(undefined)
|
const actionError = ref<string | undefined>(undefined)
|
||||||
let busy = $ref<boolean>(false)
|
const busy = ref<boolean>(false)
|
||||||
const createText = ref('')
|
const createText = ref('')
|
||||||
const enableSubmit = computed(() => createText.value.length > 0)
|
const enableSubmit = computed(() => createText.value.length > 0)
|
||||||
|
|
||||||
async function createList() {
|
async function createList() {
|
||||||
if (busy || !enableSubmit.value)
|
if (busy.value || !enableSubmit.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
busy = true
|
busy.value = true
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
await nextTick()
|
await nextTick()
|
||||||
try {
|
try {
|
||||||
const newEntry = await client.v1.lists.create({
|
const newEntry = await client.v1.lists.create({
|
||||||
|
@ -38,18 +38,18 @@ async function createList() {
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
actionError = (err as Error).message
|
actionError.value = (err as Error).message
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
inputRef.value?.focus()
|
inputRef.value?.focus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
busy = false
|
busy.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearError(focusBtn: boolean) {
|
function clearError(focusBtn: boolean) {
|
||||||
actionError = undefined
|
actionError.value = undefined
|
||||||
focusBtn && nextTick(() => {
|
focusBtn && nextTick(() => {
|
||||||
inputRef.value?.focus()
|
inputRef.value?.focus()
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,15 +6,15 @@ useHydratedHead({
|
||||||
title: () => t('nav.search'),
|
title: () => t('nav.search'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const search = $ref<{ input?: HTMLInputElement }>()
|
const search = ref<{ input?: HTMLInputElement }>()
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (search?.input)
|
if (search.value?.input)
|
||||||
search?.input?.focus()
|
search.value?.input?.focus()
|
||||||
})
|
})
|
||||||
onActivated(() =>
|
onActivated(() =>
|
||||||
search?.input?.focus(),
|
search.value?.input?.focus(),
|
||||||
)
|
)
|
||||||
onDeactivated(() => search?.input?.blur())
|
onDeactivated(() => search.value?.input?.blur())
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -4,17 +4,17 @@ definePageMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
const tagName = $(computedEager(() => params.tag as string))
|
const tagName = computedEager(() => params.tag as string)
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = useMasto()
|
||||||
const { data: tag, refresh } = $(await useAsyncData(() => client.v1.tags.$select(tagName).fetch(), { default: () => shallowRef() }))
|
const { data: tag, refresh } = await useAsyncData(() => client.value.v1.tags.$select(tagName.value).fetch(), { default: () => shallowRef() })
|
||||||
|
|
||||||
const paginator = client.v1.timelines.tag.$select(tagName).list()
|
const paginator = client.value.v1.timelines.tag.$select(tagName.value).list()
|
||||||
const stream = useStreaming(client => client.hashtag.subscribe({ tag: tagName }))
|
const stream = useStreaming(client => client.hashtag.subscribe({ tag: tagName.value }))
|
||||||
|
|
||||||
if (tag) {
|
if (tag.value) {
|
||||||
useHydratedHead({
|
useHydratedHead({
|
||||||
title: () => `#${tag.name}`,
|
title: () => `#${tag.value.name}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const route = useRoute()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const pwaEnabled = useAppConfig().pwaEnabled
|
const pwaEnabled = useAppConfig().pwaEnabled
|
||||||
|
|
||||||
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
const tabs = computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
name: 'all',
|
name: 'all',
|
||||||
to: '/notifications',
|
to: '/notifications',
|
||||||
|
@ -27,7 +27,7 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const filter = $computed<mastodon.v1.NotificationType | undefined>(() => {
|
const filter = computed<mastodon.v1.NotificationType | undefined>(() => {
|
||||||
if (!isHydrated.value)
|
if (!isHydrated.value)
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
|
@ -50,22 +50,22 @@ const filterIconMap: Record<mastodon.v1.NotificationType, string> = {
|
||||||
'admin.report': 'i-ri:flag-line',
|
'admin.report': 'i-ri:flag-line',
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterText = $computed(() => (`${t('tab.notifications_more_tooltip')}${filter ? `: ${t(`tab.notifications_${filter}`)}` : ''}`))
|
const filterText = computed(() => (`${t('tab.notifications_more_tooltip')}${filter ? `: ${t(`tab.notifications_${filter}`)}` : ''}`))
|
||||||
|
|
||||||
const notificationFilterRoutes = $computed<CommonRouteTabOption[]>(() => NOTIFICATION_FILTER_TYPES.map(
|
const notificationFilterRoutes = computed<CommonRouteTabOption[]>(() => NOTIFICATION_FILTER_TYPES.map(
|
||||||
name => ({
|
name => ({
|
||||||
name,
|
name,
|
||||||
to: `/notifications/${name}`,
|
to: `/notifications/${name}`,
|
||||||
display: isHydrated.value ? t(`tab.notifications_${name}`) : '',
|
display: isHydrated.value ? t(`tab.notifications_${name}`) : '',
|
||||||
icon: filterIconMap[name],
|
icon: filterIconMap[name],
|
||||||
match: name === filter,
|
match: name === filter.value,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
const moreOptions = $computed<CommonRouteTabMoreOption>(() => ({
|
const moreOptions = computed<CommonRouteTabMoreOption>(() => ({
|
||||||
options: notificationFilterRoutes,
|
options: notificationFilterRoutes.value,
|
||||||
icon: 'i-ri:filter-2-line',
|
icon: 'i-ri:filter-2-line',
|
||||||
tooltip: filterText,
|
tooltip: filterText.value,
|
||||||
match: !!filter,
|
match: !!filter.value,
|
||||||
}))
|
}))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { mastodon } from 'masto'
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const filter = $computed<mastodon.v1.NotificationType | undefined>(() => {
|
const filter = computed<mastodon.v1.NotificationType | undefined>(() => {
|
||||||
if (!isHydrated.value)
|
if (!isHydrated.value)
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ useHydratedHead({
|
||||||
title: () => `${t('settings.about.label')} | ${t('nav.settings')}`,
|
title: () => `${t('settings.about.label')} | ${t('nav.settings')}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
let showCommit = $ref(buildInfo.env !== 'release' && buildInfo.env !== 'dev')
|
const showCommit = ref(buildInfo.env !== 'release' && buildInfo.env !== 'dev')
|
||||||
const builtTime = useFormattedDateTime(buildInfo.time)
|
const builtTime = useFormattedDateTime(buildInfo.time)
|
||||||
|
|
||||||
function handleShowCommit() {
|
function handleShowCommit() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showCommit = true
|
showCommit.value = true
|
||||||
}, 50)
|
}, 50)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue