1
0
mirror of https://github.com/elk-zone/elk synced 2024-11-27 14:28:10 +09:00

feat: upgrade to masto.js v6 (#2530)

This commit is contained in:
patak 2024-01-09 09:56:15 +01:00 committed by GitHub
parent d8ea685803
commit 6c5bb83ac3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 262 additions and 263 deletions

View File

@ -19,7 +19,7 @@ const { client } = $(useMasto())
async function unblock() { async function unblock() {
relationship!.blocking = false relationship!.blocking = false
try { try {
const newRel = await client.v1.accounts.unblock(account.id) const newRel = await client.v1.accounts.$select(account.id).unblock()
Object.assign(relationship!, newRel) Object.assign(relationship!, newRel)
} }
catch (err) { catch (err) {
@ -32,7 +32,7 @@ async function unblock() {
async function unmute() { async function unmute() {
relationship!.muting = false relationship!.muting = false
try { try {
const newRel = await client.v1.accounts.unmute(account.id) const newRel = await client.v1.accounts.$select(account.id).unmute()
Object.assign(relationship!, newRel) Object.assign(relationship!, newRel)
} }
catch (err) { catch (err) {

View File

@ -12,7 +12,7 @@ async function authorizeFollowRequest() {
relationship!.requestedBy = false relationship!.requestedBy = false
relationship!.followedBy = true relationship!.followedBy = true
try { try {
const newRel = await client.v1.followRequests.authorize(account.id) const newRel = await client.v1.followRequests.$select(account.id).authorize()
Object.assign(relationship!, newRel) Object.assign(relationship!, newRel)
} }
catch (err) { catch (err) {
@ -25,7 +25,7 @@ async function authorizeFollowRequest() {
async function rejectFollowRequest() { async function rejectFollowRequest() {
relationship!.requestedBy = false relationship!.requestedBy = false
try { try {
const newRel = await client.v1.followRequests.reject(account.id) const newRel = await client.v1.followRequests.$select(account.id).reject()
Object.assign(relationship!, newRel) Object.assign(relationship!, newRel)
} }
catch (err) { catch (err) {

View File

@ -53,7 +53,7 @@ function previewAvatar() {
async function toggleNotifications() { async function toggleNotifications() {
relationship!.notifying = !relationship?.notifying relationship!.notifying = !relationship?.notifying
try { try {
const newRel = await client.v1.accounts.follow(account.id, { notify: relationship?.notifying }) const newRel = await client.v1.accounts.$select(account.id).follow({ notify: relationship?.notifying })
Object.assign(relationship!, newRel) Object.assign(relationship!, newRel)
} }
catch { catch {
@ -97,7 +97,7 @@ async function editNote(event: Event) {
if (relationship.note?.trim() === newNote.trim()) if (relationship.note?.trim() === newNote.trim())
return return
const newNoteApiResult = await client.v1.accounts.createNote(account.id, { comment: newNote }) const newNoteApiResult = await client.v1.accounts.$select(account.id).note.create({ comment: newNote })
relationship.note = newNoteApiResult.note relationship.note = newNoteApiResult.note
personalNoteDraft.value = relationship.note ?? '' personalNoteDraft.value = relationship.note ?? ''
} }

View File

@ -33,7 +33,7 @@ async function toggleReblogs() {
return return
const showingReblogs = !relationship?.showingReblogs const showingReblogs = !relationship?.showingReblogs
relationship = await client.v1.accounts.follow(account.id, { reblogs: showingReblogs }) relationship = await client.v1.accounts.$select(account.id).follow({ reblogs: showingReblogs })
} }
async function addUserNote() { async function addUserNote() {
@ -44,7 +44,7 @@ async function removeUserNote() {
if (!relationship!.note || relationship!.note.length === 0) if (!relationship!.note || relationship!.note.length === 0)
return return
const newNote = await client.v1.accounts.createNote(account.id, { comment: '' }) const newNote = await client.v1.accounts.$select(account.id).note.create({ comment: '' })
relationship!.note = newNote.note relationship!.note = newNote.note
emit('removeNote') emit('removeNote')
} }

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Paginator, mastodon } from 'masto' import type { mastodon } from 'masto'
const { paginator, account, context } = defineProps<{ const { paginator, account, context } = defineProps<{
paginator: Paginator<mastodon.v1.Account[], mastodon.DefaultPaginationParams> paginator: mastodon.Paginator<mastodon.v1.Account[], mastodon.DefaultPaginationParams | undefined>
context?: 'following' | 'followers' context?: 'following' | 'followers'
account?: mastodon.v1.Account account?: mastodon.v1.Account
relationshipContext?: 'followedBy' | 'following' relationshipContext?: 'followedBy' | 'following'

View File

@ -2,7 +2,7 @@
// @ts-expect-error missing types // @ts-expect-error missing types
import { DynamicScroller } from 'vue-virtual-scroller' import { DynamicScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import type { Paginator, WsEvents } from 'masto' import type { mastodon } from 'masto'
import type { UnwrapRef } from 'vue' import type { UnwrapRef } from 'vue'
const { const {
@ -14,10 +14,10 @@ const {
preprocess, preprocess,
endMessage = true, endMessage = true,
} = defineProps<{ } = defineProps<{
paginator: Paginator<T[], O> paginator: mastodon.Paginator<T[], O>
keyProp?: keyof T keyProp?: keyof T
virtualScroller?: boolean virtualScroller?: boolean
stream?: Promise<WsEvents> stream?: mastodon.streaming.Subscription
eventType?: 'notification' | 'update' eventType?: 'notification' | 'update'
preprocess?: (items: (U | T)[]) => U[] preprocess?: (items: (U | T)[]) => U[]
endMessage?: boolean | string endMessage?: boolean | string

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Paginator, mastodon } from 'masto' import type { mastodon } from 'masto'
const { paginator } = defineProps<{ const { paginator } = defineProps<{
paginator: Paginator<mastodon.v1.Conversation[], mastodon.DefaultPaginationParams> paginator: mastodon.Paginator<mastodon.v1.Conversation[], mastodon.DefaultPaginationParams>
}>() }>()
function preprocess(items: mastodon.v1.Conversation[]): mastodon.v1.Conversation[] { function preprocess(items: mastodon.v1.Conversation[]): mastodon.v1.Conversation[] {

View File

@ -16,8 +16,8 @@ const isRemoved = ref(false)
async function edit() { async function edit() {
try { try {
isRemoved.value isRemoved.value
? await client.v1.lists.addAccount(list, { accountIds: [account.id] }) ? await client.v1.lists.$select(list).accounts.create({ accountIds: [account.id] })
: await client.v1.lists.removeAccount(list, { accountIds: [account.id] }) : await client.v1.lists.$select(list).accounts.remove({ accountIds: [account.id] })
isRemoved.value = !isRemoved.value isRemoved.value = !isRemoved.value
} }
catch (err) { catch (err) {

View File

@ -40,7 +40,7 @@ async function cancelEdit() {
const { submit, submitting } = submitter(async () => { const { submit, submitting } = submitter(async () => {
try { try {
list.value = await client.v1.lists.update(form.id, { list.value = await client.v1.lists.$select(form.id).update({
title: form.title, title: form.title,
}) })
cancelEdit() cancelEdit()
@ -70,7 +70,7 @@ async function removeList() {
if (confirmDelete === 'confirm') { if (confirmDelete === 'confirm') {
await nextTick() await nextTick()
try { try {
await client.v1.lists.remove(list.value.id) await client.v1.lists.$select(list.value.id).remove()
emit('listRemoved', list.value.id) emit('listRemoved', list.value.id)
} }
catch (err) { catch (err) {

View File

@ -5,7 +5,7 @@ const { userId } = defineProps<{
const { client } = $(useMasto()) const { client } = $(useMasto())
const paginator = client.v1.lists.list() const paginator = client.v1.lists.list()
const listsWithUser = ref((await client.v1.accounts.listLists(userId)).map(list => list.id)) const listsWithUser = ref((await client.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.addAccount(listId, { accountIds: [userId] }) await client.v1.lists.$select(listId).accounts.create({ accountIds: [userId] })
listsWithUser.value.push(listId) listsWithUser.value.push(listId)
} }
else { else {
await client.v1.lists.removeAccount(listId, { accountIds: [userId] }) await client.v1.lists.$select(listId).accounts.remove({ accountIds: [userId] })
listsWithUser.value = listsWithUser.value.filter(id => id !== listId) listsWithUser.value = listsWithUser.value.filter(id => id !== listId)
} }
} }

View File

@ -15,7 +15,6 @@ const { notification } = defineProps<{
ps-3 pe-4 inset-is-0 ps-3 pe-4 inset-is-0
rounded-ie-be-3 rounded-ie-be-3
py-3 bg-base top-0 py-3 bg-base top-0
:lang="notification.status?.language ?? undefined"
> >
<div i-ri-user-3-line text-xl me-3 color-blue /> <div i-ri-user-3-line text-xl me-3 color-blue />
<AccountDisplayName :account="notification.account" text-primary me-1 font-bold line-clamp-1 ws-pre-wrap break-all /> <AccountDisplayName :account="notification.account" text-primary me-1 font-bold line-clamp-1 ws-pre-wrap break-all />
@ -26,7 +25,6 @@ const { notification } = defineProps<{
<AccountBigCard <AccountBigCard
ms10 ms10
:account="notification.account" :account="notification.account"
:lang="notification.status?.language ?? undefined"
/> />
</NuxtLink> </NuxtLink>
</template> </template>

View File

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
// @ts-expect-error missing types // @ts-expect-error missing types
import { DynamicScrollerItem } from 'vue-virtual-scroller' import { DynamicScrollerItem } from 'vue-virtual-scroller'
import type { Paginator, WsEvents, mastodon } from 'masto' import type { mastodon } from 'masto'
import type { GroupedAccountLike, NotificationSlot } from '~/types' import type { GroupedAccountLike, NotificationSlot } from '~/types'
const { paginator, stream } = defineProps<{ const { paginator, stream } = defineProps<{
paginator: Paginator<mastodon.v1.Notification[], mastodon.v1.ListNotificationsParams> paginator: mastodon.Paginator<mastodon.v1.Notification[], mastodon.rest.v1.ListNotificationsParams>
stream?: Promise<WsEvents> stream?: mastodon.streaming.Subscription
}>() }>()
const virtualScroller = false // TODO: fix flickering issue with virtual scroll const virtualScroller = false // TODO: fix flickering issue with virtual scroll

View File

@ -75,12 +75,13 @@ function trimPollOptions() {
} }
function editPollOptionDraft(event: Event, index: number) { function editPollOptionDraft(event: Event, index: number) {
draft.params.poll!.options[index] = (event.target as HTMLInputElement).value draft.params.poll!.options = Object.assign(draft.params.poll!.options.slice(), { [index]: (event.target as HTMLInputElement).value })
trimPollOptions() trimPollOptions()
} }
function deletePollOption(index: number) { function deletePollOption(index: number) {
draft.params.poll!.options.splice(index, 1) draft.params.poll!.options = draft.params.poll!.options.slice().splice(index, 1)
trimPollOptions() trimPollOptions()
} }

View File

@ -34,11 +34,11 @@ function categoryChosen() {
async function loadStatuses() { async function loadStatuses() {
if (status) { if (status) {
// Load the 5 statuses before and after the reported status // Load the 5 statuses before and after the reported status
const prevStatuses = await client.value.v1.accounts.listStatuses(account.id, { const prevStatuses = await client.value.v1.accounts.$select(account.id).statuses.list({
maxId: status.id, maxId: status.id,
limit: 5, limit: 5,
}) })
const nextStatuses = await client.value.v1.accounts.listStatuses(account.id, { const nextStatuses = await client.value.v1.accounts.$select(account.id).statuses.list({
minId: status.id, minId: status.id,
limit: 5, limit: 5,
}) })
@ -48,7 +48,7 @@ async function loadStatuses() {
else { else {
// Reporting an account directly // Reporting an account directly
// Load the 10 most recent statuses // Load the 10 most recent statuses
const mostRecentStatuses = await client.value.v1.accounts.listStatuses(account.id, { const mostRecentStatuses = await client.value.v1.accounts.$select(account.id).statuses.list({
limit: 10, limit: 10,
}) })
availableStatuses.value = mostRecentStatuses availableStatuses.value = mostRecentStatuses

View File

@ -2,7 +2,7 @@
import type { mastodon } from 'masto' import type { mastodon } from 'masto'
const form = defineModel<{ const form = defineModel<{
fieldsAttributes: NonNullable<mastodon.v1.UpdateCredentialsParams['fieldsAttributes']> fieldsAttributes: NonNullable<mastodon.rest.v1.UpdateCredentialsParams['fieldsAttributes']>
}>({ required: true }) }>({ required: true })
const dropdown = $ref<any>() const dropdown = $ref<any>()

View File

@ -72,7 +72,7 @@ async function deleteStatus() {
return return
removeCachedStatus(status.id) removeCachedStatus(status.id)
await client.v1.statuses.remove(status.id) await client.v1.statuses.$select(status.id).remove()
if (route.name === 'status') if (route.name === 'status')
router.back() router.back()
@ -96,7 +96,7 @@ async function deleteAndRedraft() {
} }
removeCachedStatus(status.id) removeCachedStatus(status.id)
await client.v1.statuses.remove(status.id) await client.v1.statuses.$select(status.id).remove()
await openPublishDialog('dialog', await getDraftFromStatus(status), true) await openPublishDialog('dialog', await getDraftFromStatus(status), 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

View File

@ -6,7 +6,7 @@ const type = ref<'favourited-by' | 'boosted-by'>('favourited-by')
const { client } = $(useMasto()) const { client } = $(useMasto())
function load() { function load() {
return client.v1.statuses[type.value === 'favourited-by' ? 'listFavouritedBy' : 'listRebloggedBy'](favouritedBoostedByStatusId.value!) return client.v1.statuses.$select(favouritedBoostedByStatusId.value!)[type.value === 'favourited-by' ? 'favouritedBy' : 'rebloggedBy'].list()
} }
const paginator = $computed(() => load()) const paginator = $computed(() => load())

View File

@ -36,7 +36,7 @@ async function vote(e: Event) {
cacheStatus({ ...status, poll }, undefined, true) cacheStatus({ ...status, poll }, undefined, true)
await client.v1.polls.vote(poll.id, { choices }) await client.v1.polls.$select(poll.id).votes.create({ choices })
} }
const votersCount = $computed(() => poll.votersCount ?? poll.votesCount ?? 0) const votersCount = $computed(() => poll.votersCount ?? poll.votesCount ?? 0)

View File

@ -6,7 +6,7 @@ const { status } = defineProps<{
status: mastodon.v1.Status status: mastodon.v1.Status
}>() }>()
const paginator = useMastoClient().v1.statuses.listHistory(status.id) const paginator = useMastoClient().v1.statuses.$select(status.id).history.list()
function showHistory(edit: mastodon.v1.StatusEdit) { function showHistory(edit: mastodon.v1.StatusEdit) {
openEditHistoryDialog(edit) openEditHistoryDialog(edit)

View File

@ -20,9 +20,9 @@ async function toggleFollowTag() {
try { try {
if (previousFollowingState) if (previousFollowingState)
await client.v1.tags.unfollow(tag.name) await client.v1.tags.$select(tag.name).unfollow()
else else
await client.v1.tags.follow(tag.name) await client.v1.tags.$select(tag.name).follow()
emit('change') emit('change')
} }

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Paginator, mastodon } from 'masto' import type { mastodon } from 'masto'
const { paginator } = defineProps<{ const { paginator } = defineProps<{
paginator: Paginator<mastodon.v1.Tag[], mastodon.DefaultPaginationParams> paginator: mastodon.Paginator<mastodon.v1.Tag[], mastodon.DefaultPaginationParams>
}>() }>()
</script> </script>

View File

@ -3,7 +3,7 @@ const { client } = $(useMasto())
const paginator = client.v1.domainBlocks.list() const paginator = client.v1.domainBlocks.list()
async function unblock(domain: string) { async function unblock(domain: string) {
await client.v1.domainBlocks.unblock(domain) await client.v1.domainBlocks.remove({ domain })
} }
</script> </script>

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { mastodon } from 'masto' import type { mastodon } from 'masto'
const paginator = useMastoClient().v1.timelines.listHome({ limit: 30 }) const paginator = useMastoClient().v1.timelines.home.list({ limit: 30 })
const stream = $(useStreaming(client => client.v1.stream.streamUser())) const stream = useStreaming(client => client.user.subscribe())
function reorderAndFilter(items: mastodon.v1.Status[]) { function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'home') return reorderedTimeline(items, 'home')
} }

View File

@ -9,7 +9,7 @@ const options = { limit: 30, types: filter ? [filter] : [] }
// Default limit is 20 notifications, and servers are normally caped to 30 // Default limit is 20 notifications, and servers are normally caped to 30
const paginator = useMastoClient().v1.notifications.list(options) const paginator = useMastoClient().v1.notifications.list(options)
const stream = useStreaming(client => client.v1.stream.streamUser()) const stream = useStreaming(client => client.user.subscribe())
const { clearNotifications } = useNotifications() const { clearNotifications } = useNotifications()
onActivated(clearNotifications) onActivated(clearNotifications)

View File

@ -2,11 +2,11 @@
// @ts-expect-error missing types // @ts-expect-error missing types
import { DynamicScrollerItem } from 'vue-virtual-scroller' import { DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import type { Paginator, WsEvents, mastodon } from 'masto' import type { mastodon } from 'masto'
const { paginator, stream, account, buffer = 10, endMessage = true } = defineProps<{ const { paginator, stream, account, buffer = 10, endMessage = true } = defineProps<{
paginator: Paginator<mastodon.v1.Status[], mastodon.v1.ListAccountStatusesParams> paginator: mastodon.Paginator<mastodon.v1.Status[], mastodon.rest.v1.ListAccountStatusesParams>
stream?: Promise<WsEvents> stream?: mastodon.streaming.Subscription
context?: mastodon.v2.FilterContext context?: mastodon.v2.FilterContext
account?: mastodon.v1.Account account?: mastodon.v1.Account
preprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[] preprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[]

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const paginator = useMastoClient().v1.accounts.listStatuses(currentUser.value!.account.id, { pinned: true }) const paginator = useMastoClient().v1.accounts.$select(currentUser.value!.account.id).statuses.list({ pinned: true })
</script> </script>
<template> <template>

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { mastodon } from 'masto' import type { mastodon } from 'masto'
const paginator = useMastoClient().v1.timelines.listPublic({ limit: 30 }) const paginator = useMastoClient().v1.timelines.public.list({ limit: 30 })
const stream = useStreaming(client => client.v1.stream.streamPublicTimeline()) const stream = useStreaming(client => client.public.subscribe())
function reorderAndFilter(items: mastodon.v1.Status[]) { function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'public') return reorderedTimeline(items, 'public')
} }

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
const paginator = useMastoClient().v1.timelines.listPublic({ limit: 30, local: true }) const paginator = useMastoClient().v1.timelines.public.list({ limit: 30, local: true })
const stream = useStreaming(client => client.v1.stream.streamCommunityTimeline()) const stream = useStreaming(client => client.direct.subscribe())
</script> </script>
<template> <template>

View File

@ -24,7 +24,7 @@ export function fetchStatus(id: string, force = false): Promise<mastodon.v1.Stat
const cached = cache.get(key) const cached = cache.get(key)
if (cached && !force) if (cached && !force)
return cached return cached
const promise = useMastoClient().v1.statuses.fetch(id) const promise = useMastoClient().v1.statuses.$select(id).fetch()
.then((status) => { .then((status) => {
cacheStatus(status) cacheStatus(status)
return status return status
@ -44,7 +44,7 @@ export function fetchAccountById(id?: string | null): Promise<mastodon.v1.Accoun
if (cached) if (cached)
return cached return cached
const domain = getInstanceDomainFromServer(server) const domain = getInstanceDomainFromServer(server)
const promise = useMastoClient().v1.accounts.fetch(id) const promise = useMastoClient().v1.accounts.$select(id).fetch()
.then((r) => { .then((r) => {
if (r.acct && !r.acct.includes('@') && domain) if (r.acct && !r.acct.includes('@') && domain)
r.acct = `${r.acct}@${domain}` r.acct = `${r.acct}@${domain}`
@ -74,7 +74,7 @@ export async function fetchAccountByHandle(acct: string): Promise<mastodon.v1.Ac
} }
else { else {
const userAcctDomain = userAcct.includes('@') ? userAcct : `${userAcct}@${domain}` const userAcctDomain = userAcct.includes('@') ? userAcct : `${userAcct}@${domain}`
account = (await client.v1.search({ q: `@${userAcctDomain}`, type: 'accounts' })).accounts[0] account = (await client.v1.search.fetch({ q: `@${userAcctDomain}`, type: 'accounts' })).accounts[0]
} }
if (account.acct && !account.acct.includes('@') && domain) if (account.acct && !account.acct.includes('@') && domain)

View File

@ -1,27 +1,14 @@
import type { Pausable } from '@vueuse/core' import type { Pausable } from '@vueuse/core'
import type { CreateClientParams, WsEvents, mastodon } from 'masto' import type { mastodon } from 'masto'
import { createClient, fetchV1Instance } from 'masto' import { createRestAPIClient, createStreamingAPIClient } from 'masto'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { ElkInstance } from '../users' import type { ElkInstance } from '../users'
import type { Mutable } from '~/types/utils'
import type { UserLogin } from '~/types' import type { UserLogin } from '~/types'
export function createMasto() { export function createMasto() {
let client = $shallowRef<mastodon.Client>(undefined as never)
let params = $ref<Mutable<CreateClientParams>>()
const canStreaming = $computed(() => !!params?.streamingApiUrl)
const setParams = (newParams: Partial<CreateClientParams>) => {
const p = { ...params, ...newParams } as CreateClientParams
client = createClient(p)
params = p
}
return { return {
client: $$(client), client: shallowRef<mastodon.rest.Client>(undefined as never),
params: readonly($$(params)), streamingClient: shallowRef<mastodon.streaming.Client | undefined>(),
canStreaming: $$(canStreaming),
setParams,
} }
} }
export type ElkMasto = ReturnType<typeof createMasto> export type ElkMasto = ReturnType<typeof createMasto>
@ -34,23 +21,25 @@ export function useMastoClient() {
} }
export function mastoLogin(masto: ElkMasto, user: Pick<UserLogin, 'server' | 'token'>) { export function mastoLogin(masto: ElkMasto, user: Pick<UserLogin, 'server' | 'token'>) {
const { setParams } = $(masto)
const server = user.server const server = user.server
const url = `https://${server}` const url = `https://${server}`
const instance: ElkInstance = reactive(getInstanceCache(server) || { uri: server, accountDomain: server }) const instance: ElkInstance = reactive(getInstanceCache(server) || { uri: server, accountDomain: server })
setParams({ const accessToken = user.token
url,
accessToken: user?.token,
disableVersionCheck: true,
streamingApiUrl: instance?.urls?.streamingApi,
})
fetchV1Instance({ url }).then((newInstance) => { const createStreamingClient = (streamingApiUrl: string | undefined) => {
return streamingApiUrl ? createStreamingAPIClient({ streamingApiUrl, accessToken, implementation: globalThis.WebSocket }) : undefined
}
const streamingApiUrl = instance?.urls?.streamingApi
masto.client.value = createRestAPIClient({ url, accessToken })
masto.streamingClient.value = createStreamingClient(streamingApiUrl)
// Refetch instance info in the background on login
masto.client.value.v1.instance.fetch().then((newInstance) => {
Object.assign(instance, newInstance) Object.assign(instance, newInstance)
setParams({ if (newInstance.urls.streamingApi !== streamingApiUrl)
streamingApiUrl: newInstance.urls.streamingApi, masto.streamingClient.value = createStreamingClient(newInstance.urls.streamingApi)
})
instanceStorage.value[server] = newInstance instanceStorage.value[server] = newInstance
}) })
@ -73,21 +62,21 @@ interface UseStreamingOptions<Controls extends boolean> {
} }
export function useStreaming( export function useStreaming(
cb: (client: mastodon.Client) => Promise<WsEvents>, cb: (client: mastodon.streaming.Client) => mastodon.streaming.Subscription,
options: UseStreamingOptions<true>, options: UseStreamingOptions<true>,
): { stream: Ref<Promise<WsEvents> | undefined> } & Pausable ): { stream: Ref<mastodon.streaming.Subscription | undefined> } & Pausable
export function useStreaming( export function useStreaming(
cb: (client: mastodon.Client) => Promise<WsEvents>, cb: (client: mastodon.streaming.Client) => mastodon.streaming.Subscription,
options?: UseStreamingOptions<false>, options?: UseStreamingOptions<false>,
): Ref<Promise<WsEvents> | undefined> ): Ref<mastodon.streaming.Subscription | undefined>
export function useStreaming( export function useStreaming(
cb: (client: mastodon.Client) => Promise<WsEvents>, cb: (client: mastodon.streaming.Client) => mastodon.streaming.Subscription,
{ immediate = true, controls }: UseStreamingOptions<boolean> = {}, { immediate = true, controls }: UseStreamingOptions<boolean> = {},
): ({ stream: Ref<Promise<WsEvents> | undefined> } & Pausable) | Ref<Promise<WsEvents> | undefined> { ): ({ stream: Ref<mastodon.streaming.Subscription | undefined> } & Pausable) | Ref<mastodon.streaming.Subscription | undefined> {
const { canStreaming, client } = useMasto() const { streamingClient } = useMasto()
const isActive = ref(immediate) const isActive = ref(immediate)
const stream = ref<Promise<WsEvents>>() const stream = ref<mastodon.streaming.Subscription>()
function pause() { function pause() {
isActive.value = false isActive.value = false
@ -99,15 +88,15 @@ export function useStreaming(
function cleanup() { function cleanup() {
if (stream.value) { if (stream.value) {
stream.value.then(s => s.disconnect()).catch(() => Promise.resolve()) stream.value.unsubscribe()
stream.value = undefined stream.value = undefined
} }
} }
watchEffect(() => { watchEffect(() => {
cleanup() cleanup()
if (canStreaming.value && isActive.value) if (streamingClient.value && isActive.value)
stream.value = cb(client.value) stream.value = cb(streamingClient.value)
}) })
if (process.client && !process.test) if (process.client && !process.test)

View File

@ -1,11 +1,11 @@
import type { WsEvents } from 'masto' import type { mastodon } from 'masto'
const notifications = reactive<Record<string, undefined | [Promise<WsEvents>, string[]]>>({}) const notifications = reactive<Record<string, undefined | [Promise<mastodon.streaming.Subscription>, string[]]>>({})
export function useNotifications() { export function useNotifications() {
const id = currentUser.value?.account.id const id = currentUser.value?.account.id
const { client, canStreaming } = $(useMasto()) const { client, streamingClient } = $(useMasto())
async function clearNotifications() { async function clearNotifications() {
if (!id || !notifications[id]) if (!id || !notifications[id])
@ -19,21 +19,27 @@ export function useNotifications() {
} }
} }
async function processNotifications(stream: mastodon.streaming.Subscription, id: string) {
for await (const entry of stream) {
if (entry.event === 'notification' && notifications[id])
notifications[id]![1].unshift(entry.payload.id)
}
}
async function connect(): Promise<void> { async function connect(): Promise<void> {
if (!isHydrated.value || !id || notifications[id] || !currentUser.value?.token) if (!isHydrated.value || !id || notifications[id] !== undefined || !currentUser.value?.token)
return return
let resolveStream let resolveStream
const stream = new Promise<WsEvents>(resolve => resolveStream = resolve) const streamPromise = new Promise<mastodon.streaming.Subscription>(resolve => resolveStream = resolve)
notifications[id] = [stream, []] notifications[id] = [streamPromise, []]
await until($$(canStreaming)).toBe(true) await until($$(streamingClient)).toBe(true)
client.v1.stream.streamUser().then(resolveStream) const stream = streamingClient!.user.subscribe()
stream.then(s => s.on('notification', (n) => { resolveStream!(stream)
if (notifications[id])
notifications[id]![1].unshift(n.id) processNotifications(stream, id)
}))
const position = await client.v1.markers.fetch({ timeline: ['notifications'] }) const position = await client.v1.markers.fetch({ timeline: ['notifications'] })
const paginator = client.v1.notifications.list({ limit: 30 }) const paginator = client.v1.notifications.list({ limit: 30 })
@ -55,7 +61,7 @@ export function useNotifications() {
function disconnect(): void { function disconnect(): void {
if (!id || !notifications[id]) if (!id || !notifications[id])
return return
notifications[id]![0].then(stream => stream.disconnect()) notifications[id]![0].then(stream => stream.unsubscribe())
notifications[id] = undefined notifications[id] = undefined
} }
@ -67,7 +73,6 @@ export function useNotifications() {
return { return {
notifications: computed(() => id ? notifications[id]?.[1].length ?? 0 : 0), notifications: computed(() => id ? notifications[id]?.[1].length ?? 0 : 0),
disconnect,
clearNotifications, clearNotifications,
} }
} }

View File

@ -93,7 +93,7 @@ export function usePublish(options: {
language: draft.params.language || preferredLanguage, language: draft.params.language || preferredLanguage,
poll, poll,
...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}), ...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}),
} as mastodon.v1.CreateStatusParams } as mastodon.rest.v1.CreateStatusParams
if (process.dev) { if (process.dev) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -116,14 +116,13 @@ export function usePublish(options: {
} }
else { else {
const updatePayload = { status = await client.v1.statuses.$select(draft.editingStatus.id).update({
...payload, ...payload,
mediaAttributes: draft.attachments.map(media => ({ mediaAttributes: draft.attachments.map(media => ({
id: media.id, id: media.id,
description: media.description, description: media.description,
})), })),
} as mastodon.v1.UpdateStatusParams })
status = await client.v1.statuses.update(draft.editingStatus.id, updatePayload)
} }
if (draft.params.inReplyToId) if (draft.params.inReplyToId)
navigateToStatus({ status }) navigateToStatus({ status })
@ -232,7 +231,7 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
if (draft.attachments.length < limit) { if (draft.attachments.length < limit) {
isExceedingAttachmentLimit = false isExceedingAttachmentLimit = false
try { try {
const attachment = await client.v1.mediaAttachments.create({ const attachment = await client.v1.media.create({
file: await processFile(file), file: await processFile(file),
}) })
draft.attachments.push(attachment) draft.attachments.push(attachment)
@ -266,7 +265,7 @@ 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.editingStatus)
await client.v1.mediaAttachments.update(att.id, { description: att.description }) await client.v1.media.$select(att.id).update({ description: att.description })
} }
function removeAttachment(index: number) { function removeAttachment(index: number) {

View File

@ -27,7 +27,7 @@ export function useRelationship(account: mastodon.v1.Account): Ref<mastodon.v1.R
async function fetchRelationships() { async function fetchRelationships() {
const requested = Array.from(requestedRelationships.entries()).filter(([, r]) => !r.value) const requested = Array.from(requestedRelationships.entries()).filter(([, r]) => !r.value)
const relationships = await useMastoClient().v1.accounts.fetchRelationships(requested.map(([id]) => id)) const relationships = await useMastoClient().v1.accounts.relationships.fetch({ id: requested.map(([id]) => id) })
for (let i = 0; i < requested.length; i++) for (let i = 0; i < requested.length; i++)
requested[i][1].value = relationships[i] requested[i][1].value = relationships[i]
} }
@ -58,7 +58,7 @@ export async function toggleFollowAccount(relationship: mastodon.v1.Relationship
relationship!.following = true relationship!.following = true
} }
relationship = await client.v1.accounts[unfollow ? 'unfollow' : 'follow'](account.id) relationship = await client.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) {
@ -74,10 +74,10 @@ 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.mute(account.id, { ? await client.v1.accounts.$select(account.id).mute({
// TODO support more options // TODO support more options
}) })
: await client.v1.accounts.unmute(account.id) : await client.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) {
@ -92,7 +92,7 @@ export async function toggleBlockAccount(relationship: mastodon.v1.Relationship,
return return
relationship!.blocking = !relationship!.blocking relationship!.blocking = !relationship!.blocking
relationship = await client.v1.accounts[relationship!.blocking ? 'block' : 'unblock'](account.id) relationship = await client.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) {
@ -107,5 +107,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 ? 'block' : 'unblock'](getServerName(account)) await client.v1.domainBlocks[relationship!.domainBlocking ? 'create' : 'remove']({ domain: getServerName(account) })
} }

View File

@ -1,9 +1,9 @@
import type { MaybeRefOrGetter } from '@vueuse/core' import type { MaybeRefOrGetter } from '@vueuse/core'
import type { Paginator, mastodon } from 'masto' import type { mastodon } from 'masto'
import type { RouteLocation } from 'vue-router' import type { RouteLocation } from 'vue-router'
export type UseSearchOptions = MaybeRefOrGetter< export type UseSearchOptions = MaybeRefOrGetter<
Partial<Omit<mastodon.v1.SearchParams, keyof mastodon.DefaultPaginationParams | 'q'>> Partial<Omit<mastodon.rest.v2.SearchParams, keyof mastodon.DefaultPaginationParams | 'q'>>
> >
export interface BuildSearchResult<K extends keyof any, T> { export interface BuildSearchResult<K extends keyof any, T> {
@ -30,7 +30,7 @@ export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOpt
const q = $computed(() => resolveUnref(query).trim()) const q = $computed(() => resolveUnref(query).trim())
let paginator: Paginator<mastodon.v2.Search, mastodon.v2.SearchParams> | undefined let paginator: mastodon.Paginator<mastodon.v2.Search, mastodon.rest.v2.SearchParams> | undefined
const appendResults = (results: mastodon.v2.Search, empty = false) => { const appendResults = (results: mastodon.v2.Search, empty = false) => {
if (empty) { if (empty) {
@ -72,7 +72,7 @@ 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({ paginator = client.v2.search.list({
q, q,
...resolveUnref(options), ...resolveUnref(options),
resolve: !!currentUser.value, resolve: !!currentUser.value,

View File

@ -61,7 +61,7 @@ export function useStatusActions(props: StatusActionsProps) {
const toggleReblog = () => toggleStatusAction( const toggleReblog = () => toggleStatusAction(
'reblogged', 'reblogged',
() => client.v1.statuses[status.reblogged ? 'unreblog' : 'reblog'](status.id).then((res) => { () => client.v1.statuses.$select(status.id)[status.reblogged ? 'unreblog' : 'reblog']().then((res) => {
if (status.reblogged) if (status.reblogged)
// returns the original status // returns the original status
return res.reblog! return res.reblog!
@ -72,23 +72,23 @@ export function useStatusActions(props: StatusActionsProps) {
const toggleFavourite = () => toggleStatusAction( const toggleFavourite = () => toggleStatusAction(
'favourited', 'favourited',
() => client.v1.statuses[status.favourited ? 'unfavourite' : 'favourite'](status.id), () => client.v1.statuses.$select(status.id)[status.favourited ? 'unfavourite' : 'favourite'](),
'favouritesCount', 'favouritesCount',
) )
const toggleBookmark = () => toggleStatusAction( const toggleBookmark = () => toggleStatusAction(
'bookmarked', 'bookmarked',
() => client.v1.statuses[status.bookmarked ? 'unbookmark' : 'bookmark'](status.id), () => client.v1.statuses.$select(status.id)[status.bookmarked ? 'unbookmark' : 'bookmark'](),
) )
const togglePin = async () => toggleStatusAction( const togglePin = async () => toggleStatusAction(
'pinned', 'pinned',
() => client.v1.statuses[status.pinned ? 'unpin' : 'pin'](status.id), () => client.v1.statuses.$select(status.id)[status.pinned ? 'unpin' : 'pin'](),
) )
const toggleMute = async () => toggleStatusAction( const toggleMute = async () => toggleStatusAction(
'muted', 'muted',
() => client.v1.statuses[status.muted ? 'unmute' : 'mute'](status.id), () => client.v1.statuses.$select(status.id)[status.muted ? 'unmute' : 'mute'](),
) )
return { return {

View File

@ -25,7 +25,7 @@ function getDefaultVisibility(currentVisibility: mastodon.v1.StatusVisibility) {
: preferredVisibility : preferredVisibility
} }
export function getDefaultDraft(options: Partial<Mutable<mastodon.v1.CreateStatusParams> & Omit<Draft, 'params'>> = {}): Draft { export function getDefaultDraft(options: Partial<Mutable<mastodon.rest.v1.CreateStatusParams> & Omit<Draft, 'params'>> = {}): Draft {
const { const {
attachments = [], attachments = [],
initialText = '', initialText = '',

View File

@ -1,10 +1,10 @@
import type { Paginator, WsEvents, mastodon } from 'masto' import type { mastodon } from 'masto'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { PaginatorState } from '~/types' import type { PaginatorState } from '~/types'
export function usePaginator<T, P, U = T>( export function usePaginator<T, P, U = T>(
_paginator: Paginator<T[], P>, _paginator: mastodon.Paginator<T[], P>,
stream: Ref<Promise<WsEvents> | undefined>, stream: Ref<mastodon.streaming.Subscription | undefined>,
eventType: 'notification' | 'update' = 'update', eventType: 'notification' | 'update' = 'update',
preprocess: (items: (T | U)[]) => U[] = items => items as unknown as U[], preprocess: (items: (T | U)[]) => U[] = items => items as unknown as U[],
buffer = 10, buffer = 10,
@ -30,10 +30,15 @@ export function usePaginator<T, P, U = T>(
prevItems.value = [] prevItems.value = []
} }
watch(stream, (stream) => { watch(stream, async (stream) => {
stream?.then((s) => { if (!stream)
s.on(eventType, (status) => { return
if ('uri' in status)
for await (const entry of stream) {
if (entry.event === 'update') {
const status = entry.payload
if ('uri' in entry)
cacheStatus(status, undefined, true) cacheStatus(status, undefined, true)
const index = prevItems.value.findIndex((i: any) => i.id === status.id) const index = prevItems.value.findIndex((i: any) => i.id === status.id)
@ -41,27 +46,27 @@ export function usePaginator<T, P, U = T>(
prevItems.value.splice(index, 1) prevItems.value.splice(index, 1)
prevItems.value.unshift(status as any) prevItems.value.unshift(status as any)
}) }
else if (entry.event === 'status.update') {
// TODO: update statuses const status = entry.payload
s.on('status.update', (status) => {
cacheStatus(status, undefined, true) cacheStatus(status, undefined, true)
const data = items.value as mastodon.v1.Status[] const data = items.value as mastodon.v1.Status[]
const index = data.findIndex(s => s.id === status.id) const index = data.findIndex(s => s.id === status.id)
if (index >= 0) if (index >= 0)
data[index] = status data[index] = status
}) }
s.on('delete', (id) => { else if (entry.event === 'delete') {
const id = entry.payload
removeCachedStatus(id) removeCachedStatus(id)
const data = items.value as mastodon.v1.Status[] const data = items.value as mastodon.v1.Status[]
const index = data.findIndex(s => s.id === id) const index = data.findIndex(s => s.id === id)
if (index >= 0) if (index >= 0)
data.splice(index, 1) data.splice(index, 1)
}) }
}) }
}, { immediate: true }) }, { immediate: true })
async function loadNext() { async function loadNext() {

View File

@ -8,7 +8,7 @@ import { PushSubscriptionError } from '~/composables/push-notifications/types'
export async function createPushSubscription(user: RequiredUserLogin, export async function createPushSubscription(user: RequiredUserLogin,
notificationData: CreatePushNotification, notificationData: CreatePushNotification,
policy: mastodon.v1.SubscriptionPolicy = 'all', policy: mastodon.v1.WebPushSubscriptionPolicy = 'all',
force = false): Promise<mastodon.v1.WebPushSubscription | undefined> { force = false): Promise<mastodon.v1.WebPushSubscription | undefined> {
const { server: serverEndpoint, vapidKey } = user const { server: serverEndpoint, vapidKey } = user
@ -115,10 +115,10 @@ async function removePushNotificationDataOnError(e: Error) {
async function sendSubscriptionToBackend( async function sendSubscriptionToBackend(
subscription: PushSubscription, subscription: PushSubscription,
data: CreatePushNotification, data: CreatePushNotification,
policy: mastodon.v1.SubscriptionPolicy, policy: mastodon.v1.WebPushSubscriptionPolicy,
): Promise<mastodon.v1.WebPushSubscription> { ): Promise<mastodon.v1.WebPushSubscription> {
const { endpoint, keys } = subscription.toJSON() const { endpoint, keys } = subscription.toJSON()
const params: mastodon.v1.CreateWebPushSubscriptionParams = { return await useMastoClient().v1.push.subscription.create({
policy, policy,
subscription: { subscription: {
endpoint: endpoint!, endpoint: endpoint!,
@ -128,7 +128,5 @@ async function sendSubscriptionToBackend(
}, },
}, },
data, data,
} })
return await useMastoClient().v1.webPushSubscriptions.create(params)
} }

View File

@ -14,11 +14,11 @@ export interface RequiredUserLogin extends Required<Omit<UserLogin, 'account' |
export interface CreatePushNotification { export interface CreatePushNotification {
alerts?: Partial<mastodon.v1.WebPushSubscriptionAlerts> | null alerts?: Partial<mastodon.v1.WebPushSubscriptionAlerts> | null
policy?: mastodon.v1.SubscriptionPolicy policy?: mastodon.v1.WebPushSubscriptionPolicy
} }
export type PushNotificationRequest = Record<string, boolean> export type PushNotificationRequest = Record<string, boolean>
export type PushNotificationPolicy = Record<string, mastodon.v1.SubscriptionPolicy> export type PushNotificationPolicy = Record<string, mastodon.v1.WebPushSubscriptionPolicy>
export interface CustomEmojisInfo { export interface CustomEmojisInfo {
lastUpdate: number lastUpdate: number

View File

@ -61,7 +61,7 @@ export function usePushManager() {
const subscribe = async ( const subscribe = async (
notificationData?: CreatePushNotification, notificationData?: CreatePushNotification,
policy?: mastodon.v1.SubscriptionPolicy, policy?: mastodon.v1.WebPushSubscriptionPolicy,
force?: boolean, force?: boolean,
): Promise<SubscriptionResult> => { ): Promise<SubscriptionResult> => {
if (!isSupported) if (!isSupported)
@ -116,7 +116,7 @@ export function usePushManager() {
await removePushNotificationData(currentUser.value) await removePushNotificationData(currentUser.value)
} }
const saveSettings = async (policy?: mastodon.v1.SubscriptionPolicy) => { const saveSettings = async (policy?: mastodon.v1.WebPushSubscriptionPolicy) => {
if (policy) if (policy)
pushNotificationData.value.policy = policy pushNotificationData.value.policy = policy
@ -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.webPushSubscriptions.update({ data }) currentUser.value.pushSubscription = await client.v1.push.subscription.update({ data })
policyChanged && await nextTick() policyChanged && await nextTick()
@ -198,7 +198,7 @@ export function usePushManager() {
function createRawSettings( function createRawSettings(
pushSubscription?: mastodon.v1.WebPushSubscription, pushSubscription?: mastodon.v1.WebPushSubscription,
subscriptionPolicy?: mastodon.v1.SubscriptionPolicy, subscriptionPolicy?: mastodon.v1.WebPushSubscriptionPolicy,
) { ) {
return { return {
follow: pushSubscription?.alerts.follow ?? true, follow: pushSubscription?.alerts.follow ?? true,

View File

@ -27,8 +27,8 @@ export const TiptapMentionSuggestion: Partial<SuggestionOptions> = process.serve
if (query.length === 0) if (query.length === 0)
return [] return []
const results = await useMastoClient().v2.search({ q: query, type: 'accounts', limit: 25, resolve: true }) const paginator = useMastoClient().v2.search.list({ q: query, type: 'accounts', limit: 25, resolve: true })
return results.accounts return (await paginator.next()).value?.accounts ?? []
}, },
render: createSuggestionRenderer(TiptapMentionList), render: createSuggestionRenderer(TiptapMentionList),
} }
@ -40,14 +40,14 @@ export const TiptapHashtagSuggestion: Partial<SuggestionOptions> = {
if (query.length === 0) if (query.length === 0)
return [] return []
const results = await useMastoClient().v2.search({ const paginator = useMastoClient().v2.search.list({
q: query, q: query,
type: 'hashtags', type: 'hashtags',
limit: 25, limit: 25,
resolve: false, resolve: false,
excludeUnreviewed: true, excludeUnreviewed: true,
}) })
return results.hashtags return (await paginator.next()).value?.hashtags ?? []
}, },
render: createSuggestionRenderer(TiptapHashtagList), render: createSuggestionRenderer(TiptapHashtagList),
} }

View File

@ -149,7 +149,7 @@ export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { acco
// 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.webPushSubscriptions.fetch().catch(() => Promise.resolve(undefined)) ? client.v1.push.subscription.fetch().catch(() => Promise.resolve(undefined))
: Promise.resolve(undefined), : Promise.resolve(undefined),
]) ])
@ -172,6 +172,7 @@ export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { acco
const accountPreferencesMap = new Map<string, Partial<mastodon.v1.Preference>>() const accountPreferencesMap = new Map<string, Partial<mastodon.v1.Preference>>()
/** /**
* @param account
* @returns `true` when user ticked the preference to always expand posts with content warnings * @returns `true` when user ticked the preference to always expand posts with content warnings
*/ */
export function getExpandSpoilersByDefault(account: mastodon.v1.AccountCredentials) { export function getExpandSpoilersByDefault(account: mastodon.v1.AccountCredentials) {
@ -179,6 +180,7 @@ export function getExpandSpoilersByDefault(account: mastodon.v1.AccountCredentia
} }
/** /**
* @param account
* @returns `true` when user selected "Always show media" as Media Display preference * @returns `true` when user selected "Always show media" as Media Display preference
*/ */
export function getExpandMediaByDefault(account: mastodon.v1.AccountCredentials) { export function getExpandMediaByDefault(account: mastodon.v1.AccountCredentials) {
@ -186,13 +188,14 @@ export function getExpandMediaByDefault(account: mastodon.v1.AccountCredentials)
} }
/** /**
* @param account
* @returns `true` when user selected "Always hide media" as Media Display preference * @returns `true` when user selected "Always hide media" as Media Display preference
*/ */
export function getHideMediaByDefault(account: mastodon.v1.AccountCredentials) { export function getHideMediaByDefault(account: mastodon.v1.AccountCredentials) {
return accountPreferencesMap.get(account.acct)?.['reading:expand:media'] === 'hide_all' ?? false return accountPreferencesMap.get(account.acct)?.['reading:expand:media'] === 'hide_all' ?? false
} }
export async function fetchAccountInfo(client: mastodon.Client, server: string) { export async function fetchAccountInfo(client: mastodon.rest.Client, server: string) {
// Try to fetch user preferences if the backend supports it. // Try to fetch user preferences if the backend supports it.
const fetchPrefs = async (): Promise<Partial<mastodon.v1.Preference>> => { const fetchPrefs = async (): Promise<Partial<mastodon.v1.Preference>> => {
try { try {
@ -267,7 +270,7 @@ export async function removePushNotifications(user: UserLogin) {
return return
// unsubscribe push notifications // unsubscribe push notifications
await useMastoClient().v1.webPushSubscriptions.remove().catch(() => Promise.resolve()) await useMastoClient().v1.push.subscription.remove().catch(() => Promise.resolve())
} }
export async function switchUser(user: UserLogin) { export async function switchUser(user: UserLogin) {
@ -336,6 +339,8 @@ interface UseUserLocalStorageCache {
/** /**
* Create reactive storage for the current user * Create reactive storage for the current user
* @param key
* @param initial
*/ */
export function useUserLocalStorage<T extends object>(key: string, initial: () => T): Ref<T> { export function useUserLocalStorage<T extends object>(key: string, initial: () => T): Ref<T> {
if (process.server || process.test) if (process.server || process.test)
@ -382,6 +387,7 @@ export function useUserLocalStorage<T extends object>(key: string, initial: () =
/** /**
* Clear all storages for the given account * Clear all storages for the given account
* @param account
*/ */
export function clearUserLocalStorage(account?: mastodon.v1.Account) { export function clearUserLocalStorage(account?: mastodon.v1.Account) {
if (!account) if (!account)

View File

@ -43,7 +43,9 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
} }
// If we're logged in, search for the local id the account or status corresponds to // If we're logged in, search for the local id the account or status corresponds to
const { accounts, statuses } = await masto.client.value.v2.search({ q: `https:/${to.fullPath}`, resolve: true, limit: 1 }) const paginator = masto.client.value.v2.search.list({ q: `https:/${to.fullPath}`, resolve: true, limit: 1 })
const { accounts, statuses } = (await paginator.next()).value ?? { accounts: [], statuses: [] }
if (statuses[0]) if (statuses[0])
return getStatusRoute(statuses[0]) return getStatusRoute(statuses[0])

View File

@ -82,7 +82,7 @@
"iso-639-1": "^3.0.0", "iso-639-1": "^3.0.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lru-cache": "^10.0.0", "lru-cache": "^10.0.0",
"masto": "^5.11.3", "masto": "^6.5.2",
"node-emoji": "^2.1.3", "node-emoji": "^2.1.3",
"nuxt-security": "^0.13.1", "nuxt-security": "^0.13.1",
"page-lifecycle": "^0.1.2", "page-lifecycle": "^0.1.2",
@ -108,7 +108,8 @@
"vue-advanced-cropper": "^2.8.8", "vue-advanced-cropper": "^2.8.8",
"vue-virtual-scroller": "2.0.0-beta.8", "vue-virtual-scroller": "2.0.0-beta.8",
"workbox-build": "^7.0.0", "workbox-build": "^7.0.0",
"workbox-window": "^7.0.0" "workbox-window": "^7.0.0",
"ws": "^8.15.1"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.41.3", "@antfu/eslint-config": "^0.41.3",
@ -121,6 +122,7 @@
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/prettier": "^2.7.3", "@types/prettier": "^2.7.3",
"@types/wicg-file-system-access": "^2020.9.6", "@types/wicg-file-system-access": "^2020.9.6",
"@types/ws": "^8.5.10",
"@unlazy/nuxt": "^0.9.3", "@unlazy/nuxt": "^0.9.3",
"@vue/test-utils": "^2.4.3", "@vue/test-utils": "^2.4.3",
"bumpp": "^9.2.0", "bumpp": "^9.2.0",

View File

@ -22,7 +22,7 @@ const { data: status, pending, refresh: refreshStatus } = useAsyncData(
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.fetchContext(id), async () => client.v1.statuses.$select(id).context.fetch(),
{ watch: [isHydrated], immediate: isHydrated.value, lazy: true, default: () => shallowRef() }, { watch: [isHydrated], immediate: isHydrated.value, lazy: true, default: () => shallowRef() },
) )

View File

@ -6,7 +6,7 @@ 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)
const paginator = account ? useMastoClient().v1.accounts.listFollowers(account.id, {}) : null const paginator = account ? useMastoClient().v1.accounts.$select(account.id).followers.list() : null
const isSelf = useSelfAccount(account) const isSelf = useSelfAccount(account)

View File

@ -6,7 +6,7 @@ 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)
const paginator = account ? useMastoClient().v1.accounts.listFollowing(account.id, {}) : null const paginator = account ? useMastoClient().v1.accounts.$select(account.id).following.list() : null
const isSelf = useSelfAccount(account) const isSelf = useSelfAccount(account)

View File

@ -14,7 +14,7 @@ function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'account') return reorderedTimeline(items, 'account')
} }
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { limit: 30, excludeReplies: true }) const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ limit: 30, excludeReplies: true })
if (account) { if (account) {
useHydratedHead({ useHydratedHead({

View File

@ -7,7 +7,7 @@ const handle = $(computedEager(() => params.account as string))
const account = await fetchAccountByHandle(handle) const account = await fetchAccountByHandle(handle)
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { onlyMedia: true, excludeReplies: false }) const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ onlyMedia: true, excludeReplies: false })
if (account) { if (account) {
useHydratedHead({ useHydratedHead({

View File

@ -7,7 +7,7 @@ const handle = $(computedEager(() => params.account as string))
const account = await fetchAccountByHandle(handle) const account = await fetchAccountByHandle(handle)
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { excludeReplies: false }) const paginator = useMastoClient().v1.accounts.$select(account.id).statuses.list({ excludeReplies: false })
if (account) { if (account) {
useHydratedHead({ useHydratedHead({

View File

@ -3,7 +3,7 @@ import { STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS } from '~~/constants'
const { t } = useI18n() const { t } = useI18n()
const paginator = useMastoClient().v1.trends.listStatuses() const paginator = useMastoClient().v1.trends.statuses.list()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS, false) const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS, false)

View File

@ -3,7 +3,7 @@ import { STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS } from '~~/constants'
const { t } = useI18n() const { t } = useI18n()
const paginator = useMastoClient().v1.trends.listLinks() const paginator = useMastoClient().v1.trends.links.list()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS, false) const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS, false)

View File

@ -4,7 +4,7 @@ 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.listTags({ const paginator = client.v1.trends.tags.list({
limit: 20, limit: 20,
}) })

View File

@ -32,7 +32,7 @@ const tabs = $computed<CommonRouteTabOption[]>(() => [
) )
const { client } = $(useMasto()) const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(list), { default: () => shallowRef() })) const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.$select(list).fetch(), { default: () => shallowRef() }))
if (listInfo) { if (listInfo) {
useHydratedHead({ useHydratedHead({

View File

@ -6,7 +6,7 @@ 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.listAccounts(listId) const paginator = useMastoClient().v1.lists.$select(listId).accounts.list()
</script> </script>
<template> <template>

View File

@ -8,8 +8,8 @@ const listId = $(computedEager(() => params.list as string))
const client = useMastoClient() const client = useMastoClient()
const paginator = client.v1.timelines.listList(listId) const paginator = client.v1.timelines.list.$select(listId).list()
const stream = useStreaming(client => client.v1.stream.streamListTimeline(listId)) const stream = useStreaming(client => client.list.subscribe({ list: listId }))
</script> </script>
<template> <template>

View File

@ -7,10 +7,10 @@ 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.fetch(tagName), { default: () => shallowRef() })) const { data: tag, refresh } = $(await useAsyncData(() => client.v1.tags.$select(tagName).fetch(), { default: () => shallowRef() }))
const paginator = client.v1.timelines.listHashtag(tagName) const paginator = client.v1.timelines.tag.$select(tagName).list()
const stream = useStreaming(client => client.v1.stream.streamTagTimeline(tagName)) const stream = useStreaming(client => client.hashtag.subscribe({ tag: tagName }))
if (tag) { if (tag) {
useHydratedHead({ useHydratedHead({

View File

@ -60,7 +60,7 @@ const { submit, submitting } = submitter(async ({ dirtyFields }) => {
if (!isCanSubmit.value) if (!isCanSubmit.value)
return return
const res = await client.v1.accounts.updateCredentials(dirtyFields.value as mastodon.v1.UpdateCredentialsParams) const res = await client.v1.accounts.updateCredentials(dirtyFields.value as mastodon.rest.v1.UpdateCredentialsParams)
.then(account => ({ account })) .then(account => ({ account }))
.catch((error: Error) => ({ error })) .catch((error: Error) => ({ error }))

View File

@ -168,8 +168,8 @@ importers:
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.1 version: 10.0.1
masto: masto:
specifier: ^5.11.3 specifier: ^6.5.2
version: 5.11.3 version: 6.5.2
node-emoji: node-emoji:
specifier: ^2.1.3 specifier: ^2.1.3
version: 2.1.3 version: 2.1.3
@ -211,10 +211,10 @@ importers:
version: 5.0.1 version: 5.0.1
tauri-plugin-log-api: tauri-plugin-log-api:
specifier: github:tauri-apps/tauri-plugin-log specifier: github:tauri-apps/tauri-plugin-log
version: github.com/tauri-apps/tauri-plugin-log/91dd3fe9dcaa69bae62706e75702a0ce18ce9885 version: github.com/tauri-apps/tauri-plugin-log/19f5dcc0425e9127d2c591780e5047b83e77a7c2
tauri-plugin-store-api: tauri-plugin-store-api:
specifier: github:tauri-apps/tauri-plugin-store specifier: github:tauri-apps/tauri-plugin-store
version: github.com/tauri-apps/tauri-plugin-store/3367248b2661abcb73d2a89f30df780c387fb57d version: github.com/tauri-apps/tauri-plugin-store/7d2632996f290b0f18cc5f8a2b2791046400690e
theme-vitesse: theme-vitesse:
specifier: ^0.7.2 specifier: ^0.7.2
version: 0.7.2 version: 0.7.2
@ -248,6 +248,9 @@ importers:
workbox-window: workbox-window:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.0.0 version: 7.0.0
ws:
specifier: ^8.15.1
version: 8.15.1
devDependencies: devDependencies:
'@antfu/eslint-config': '@antfu/eslint-config':
specifier: ^0.41.3 specifier: ^0.41.3
@ -279,6 +282,9 @@ importers:
'@types/wicg-file-system-access': '@types/wicg-file-system-access':
specifier: ^2020.9.6 specifier: ^2020.9.6
version: 2020.9.6 version: 2020.9.6
'@types/ws':
specifier: ^8.5.10
version: 8.5.10
'@unlazy/nuxt': '@unlazy/nuxt':
specifier: ^0.9.3 specifier: ^0.9.3
version: 0.9.3(rollup@2.79.1) version: 0.9.3(rollup@2.79.1)
@ -2699,18 +2705,6 @@ packages:
- encoding - encoding
- supports-color - supports-color
/@mastojs/ponyfills@1.0.4:
resolution: {integrity: sha512-1NaIGmcU7OmyNzx0fk+cYeGTkdXlOJOSdetaC4pStVWsrhht2cdlYSAfe5NDW3FcUmcEm2vVceB9lcClN1RCxw==}
dependencies:
'@types/node': 18.16.19
'@types/node-fetch': 2.6.4
abort-controller: 3.0.0
form-data: 4.0.0
node-fetch: 2.6.12
transitivePeerDependencies:
- encoding
dev: false
/@netlify/functions@2.4.0: /@netlify/functions@2.4.0:
resolution: {integrity: sha512-dIqhdj5u4Lu/8qbYwtYpn8NfvIyPHbSTV2lAP4ocL+iwC9As06AXT0wa/xOpO2vRWJa0IMxdZaqCPnkyHlHiyg==} resolution: {integrity: sha512-dIqhdj5u4Lu/8qbYwtYpn8NfvIyPHbSTV2lAP4ocL+iwC9As06AXT0wa/xOpO2vRWJa0IMxdZaqCPnkyHlHiyg==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -4172,8 +4166,8 @@ packages:
string.prototype.matchall: 4.0.8 string.prototype.matchall: 4.0.8
dev: false dev: false
/@tauri-apps/api@1.5.1: /@tauri-apps/api@1.5.3:
resolution: {integrity: sha512-6unsZDOdlXTmauU3NhWhn+Cx0rODV+rvNvTdvolE5Kls5ybA6cqndQENDt1+FS0tF7ozCP66jwWoH6a5h90BrA==} resolution: {integrity: sha512-zxnDjHHKjOsrIzZm6nO5Xapb/BxqUq1tc7cGkFXsFkGTsSWgCPH1D8mm0XS9weJY2OaR73I3k3S+b7eSzJDfqA==}
engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'}
dev: false dev: false
@ -4574,17 +4568,6 @@ packages:
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
dev: true dev: true
/@types/node-fetch@2.6.4:
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
dependencies:
'@types/node': 20.8.6
form-data: 3.0.1
dev: false
/@types/node@18.16.19:
resolution: {integrity: sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==}
dev: false
/@types/node@20.8.6: /@types/node@20.8.6:
resolution: {integrity: sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==} resolution: {integrity: sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==}
dependencies: dependencies:
@ -4650,6 +4633,12 @@ packages:
resolution: {integrity: sha512-6hogE75Hl2Ov/jgp8ZhDaGmIF/q3J07GtXf8nCJCwKTHq7971po5+DId7grft09zG7plBwpF6ZU0yx9Du4/e1A==} resolution: {integrity: sha512-6hogE75Hl2Ov/jgp8ZhDaGmIF/q3J07GtXf8nCJCwKTHq7971po5+DId7grft09zG7plBwpF6ZU0yx9Du4/e1A==}
dev: true dev: true
/@types/ws@8.5.10:
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
dependencies:
'@types/node': 20.8.6
dev: true
/@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6): /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6):
resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==} resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
@ -6220,13 +6209,6 @@ packages:
/abbrev@1.1.1: /abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
/abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
dependencies:
event-target-shim: 5.0.1
dev: false
/acorn-import-assertions@1.9.0(acorn@8.11.2): /acorn-import-assertions@1.9.0(acorn@8.11.2):
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
peerDependencies: peerDependencies:
@ -8276,13 +8258,13 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
/event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
dev: false
/eventemitter3@5.0.1: /eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
dev: true
/events-to-async@2.0.1:
resolution: {integrity: sha512-RtnLYrMbXp4JkZIoZu+3VTqV21bNVBlJBZ4NmtwvMNqSE3qouhxv2gvLE4JJDaQc54ioPkrX74V6x+hp/hqjkQ==}
dev: false
/events@3.3.0: /events@3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
@ -8476,15 +8458,6 @@ packages:
cross-spawn: 7.0.3 cross-spawn: 7.0.3
signal-exit: 4.1.0 signal-exit: 4.1.0
/form-data@3.0.1:
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/form-data@4.0.0: /form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -9511,12 +9484,12 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: false dev: false
/isomorphic-ws@5.0.0(ws@8.14.2): /isomorphic-ws@5.0.0(ws@8.15.1):
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
peerDependencies: peerDependencies:
ws: '*' ws: '*'
dependencies: dependencies:
ws: 8.14.2 ws: 8.15.1
dev: false dev: false
/jackspeak@2.2.1: /jackspeak@2.2.1:
@ -10015,19 +9988,16 @@ packages:
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
dev: true dev: true
/masto@5.11.3: /masto@6.5.2:
resolution: {integrity: sha512-GtSnrqm5fHPaaU0iwag4LCmvpp82rDng6yOZinmOJHHlUfo6Gnq5QY6x3lJCxCnsPIXpTu1yaX42bWrSQyoQPA==} resolution: {integrity: sha512-JfnG7MSQmhszWnLsvdBuxXc2tcVUyCvlTxnSH/5S+In4dU1tvc1wGhFR87kO+YW8gfDsDw9CHh+AD/z+DbTTfQ==}
dependencies: dependencies:
'@mastojs/ponyfills': 1.0.4
change-case: 4.1.2 change-case: 4.1.2
eventemitter3: 5.0.1 events-to-async: 2.0.1
isomorphic-ws: 5.0.0(ws@8.14.2) isomorphic-ws: 5.0.0(ws@8.15.1)
qs: 6.11.2 ts-custom-error: 3.3.1
semver: 7.5.4 ws: 8.15.1
ws: 8.14.2
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- encoding
- utf-8-validate - utf-8-validate
dev: false dev: false
@ -12310,13 +12280,6 @@ packages:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'} engines: {node: '>=6'}
/qs@6.11.2:
resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.4
dev: false
/queue-microtask@1.2.3: /queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -13231,7 +13194,7 @@ packages:
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@nuxt/kit': ^3.0.0 '@nuxt/kit': ^3.0.0
'@nuxt/schema': 3.8.2 '@nuxt/schema': ^3.0.0
peerDependenciesMeta: peerDependenciesMeta:
'@nuxt/kit': '@nuxt/kit':
optional: true optional: true
@ -13681,6 +13644,11 @@ packages:
typescript: 5.1.6 typescript: 5.1.6
dev: true dev: true
/ts-custom-error@3.3.1:
resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==}
engines: {node: '>=14.0.0'}
dev: false
/tslib@1.14.1: /tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
dev: true dev: true
@ -15354,6 +15322,19 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
/ws@8.15.1:
resolution: {integrity: sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
/xml-name-validator@4.0.0: /xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -15453,18 +15434,18 @@ packages:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: true dev: true
github.com/tauri-apps/tauri-plugin-log/91dd3fe9dcaa69bae62706e75702a0ce18ce9885: github.com/tauri-apps/tauri-plugin-log/19f5dcc0425e9127d2c591780e5047b83e77a7c2:
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/91dd3fe9dcaa69bae62706e75702a0ce18ce9885} resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/19f5dcc0425e9127d2c591780e5047b83e77a7c2}
name: tauri-plugin-log-api name: tauri-plugin-log-api
version: 0.0.0 version: 0.0.0
dependencies: dependencies:
'@tauri-apps/api': 1.5.1 '@tauri-apps/api': 1.5.3
dev: false dev: false
github.com/tauri-apps/tauri-plugin-store/3367248b2661abcb73d2a89f30df780c387fb57d: github.com/tauri-apps/tauri-plugin-store/7d2632996f290b0f18cc5f8a2b2791046400690e:
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/3367248b2661abcb73d2a89f30df780c387fb57d} resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/7d2632996f290b0f18cc5f8a2b2791046400690e}
name: tauri-plugin-store-api name: tauri-plugin-store-api
version: 0.0.0 version: 0.0.0
dependencies: dependencies:
'@tauri-apps/api': 1.5.1 '@tauri-apps/api': 1.5.3
dev: false dev: false

8
tests/setup.ts Normal file
View File

@ -0,0 +1,8 @@
// We have TypeError: AbortSignal.timeout is not a function when running tests against masto.js v6
if (!AbortSignal.timeout) {
AbortSignal.timeout = (ms) => {
const controller = new AbortController()
setTimeout(() => controller.abort(new DOMException('TimeoutError')), ms)
return controller.signal
}
}

View File

@ -47,7 +47,7 @@ export type TranslateFn = ReturnType<typeof useI18n>['t']
export interface Draft { export interface Draft {
editingStatus?: mastodon.v1.Status editingStatus?: mastodon.v1.Status
initialText?: string initialText?: string
params: MarkNonNullable<Mutable<Omit<mastodon.v1.CreateStatusParams, 'poll'>>, 'status' | 'language' | 'sensitive' | 'spoilerText' | 'visibility'> & { poll: Mutable<mastodon.v1.CreateStatusParams['poll']> } params: MarkNonNullable<Mutable<Omit<mastodon.rest.v1.CreateStatusParams, 'poll'>>, 'status' | 'language' | 'sensitive' | 'spoilerText' | 'visibility'> & { poll: Mutable<mastodon.rest.v1.CreateStatusParams['poll']> }
attachments: mastodon.v1.MediaAttachment[] attachments: mastodon.v1.MediaAttachment[]
lastUpdated: number lastUpdated: number
mentions?: string[] mentions?: string[]

View File

@ -4,4 +4,9 @@ export default defineVitestConfig({
define: { define: {
'process.test': 'true', 'process.test': 'true',
}, },
test: {
setupFiles: [
'/tests/setup.ts',
],
},
}) })