forked from Mirrors/elk
Merge branch 'main' into userquin/feat-add-pinch-to-zoom-setting
# Conflicts: # styles/global.css
This commit is contained in:
commit
d8960ff691
28 changed files with 124 additions and 429 deletions
|
@ -30,7 +30,7 @@ const emit = defineEmits<{
|
||||||
</p>
|
</p>
|
||||||
{{ $t('help.desc_para3') }}
|
{{ $t('help.desc_para3') }}
|
||||||
<p flex="~ gap-2 wrap" mxa>
|
<p flex="~ gap-2 wrap" mxa>
|
||||||
<template v-for="team of teams" :key="team.github">
|
<template v-for="team of elkTeamMembers" :key="team.github">
|
||||||
<NuxtLink :href="`https://github.com/sponsors/${team.github}`" target="_blank" external rounded-full transition duration-300 border="~ transparent" hover="scale-105 border-primary">
|
<NuxtLink :href="`https://github.com/sponsors/${team.github}`" target="_blank" external rounded-full transition duration-300 border="~ transparent" hover="scale-105 border-primary">
|
||||||
<img :src="`/avatars/${team.github}-100x100.png`" :alt="team.display" rounded-full w-15 h-15 height="60" width="60">
|
<img :src="`/avatars/${team.github}-100x100.png`" :alt="team.display" rounded-full w-15 h-15 height="60" width="60">
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
|
@ -34,7 +34,7 @@ const toggleApply = () => {
|
||||||
text-white px2 py2 rounded-full cursor-pointer
|
text-white px2 py2 rounded-full cursor-pointer
|
||||||
@click="$emit('remove')"
|
@click="$emit('remove')"
|
||||||
>
|
>
|
||||||
<div i-ri:close-line text-3 :class="[isHydrated && isSmallScreen ? 'text-6' : 'text-3']" />
|
<div i-ri:close-line text-3 text-6 md:text-3 />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div absolute right-2 bottom-2>
|
<div absolute right-2 bottom-2>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ISO6391 from 'iso-639-1'
|
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
|
|
||||||
let { modelValue } = $defineModel<{
|
let { modelValue } = $defineModel<{
|
||||||
|
@ -10,17 +9,7 @@ const { t } = useI18n()
|
||||||
|
|
||||||
const languageKeyword = $ref('')
|
const languageKeyword = $ref('')
|
||||||
|
|
||||||
const languageList: {
|
const fuse = new Fuse(languagesNameList, {
|
||||||
code: string
|
|
||||||
nativeName: string
|
|
||||||
name: string
|
|
||||||
}[] = ISO6391.getAllCodes().map(code => ({
|
|
||||||
code,
|
|
||||||
nativeName: ISO6391.getNativeName(code),
|
|
||||||
name: ISO6391.getName(code),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const fuse = new Fuse(languageList, {
|
|
||||||
keys: ['code', 'nativeName', 'name'],
|
keys: ['code', 'nativeName', 'name'],
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
})
|
})
|
||||||
|
@ -28,7 +17,7 @@ const fuse = new Fuse(languageList, {
|
||||||
const languages = $computed(() =>
|
const languages = $computed(() =>
|
||||||
languageKeyword.trim()
|
languageKeyword.trim()
|
||||||
? fuse.search(languageKeyword).map(r => r.item)
|
? fuse.search(languageKeyword).map(r => r.item)
|
||||||
: [...languageList].sort(({ code: a }, { code: b }) => {
|
: [...languagesNameList].sort(({ code: a }, { code: b }) => {
|
||||||
return a === modelValue ? -1 : b === modelValue ? 1 : a.localeCompare(b)
|
return a === modelValue ? -1 : b === modelValue ? 1 : a.localeCompare(b)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -39,13 +28,15 @@ function chooseLanguage(language: string) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div relative of-x-hidden>
|
||||||
<input
|
<div p2>
|
||||||
v-model="languageKeyword"
|
<input
|
||||||
:placeholder="t('language.search')"
|
v-model="languageKeyword"
|
||||||
p2 mb2 border-rounded w-full bg-transparent
|
:placeholder="t('language.search')"
|
||||||
outline-none border="~ base"
|
p2 border-rounded w-full bg-transparent
|
||||||
>
|
outline-none border="~ base"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div max-h-40vh overflow-auto>
|
<div max-h-40vh overflow-auto>
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
v-for="{ code, nativeName, name } in languages"
|
v-for="{ code, nativeName, name } in languages"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type { Draft } from '~/types'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
draftKey,
|
draftKey,
|
||||||
initial = getDefaultDraft() as never /* Bug of vue-core */,
|
initial = getDefaultDraft,
|
||||||
expanded = false,
|
expanded = false,
|
||||||
placeholder,
|
placeholder,
|
||||||
dialogLabelledBy,
|
dialogLabelledBy,
|
||||||
|
@ -35,7 +35,7 @@ const {
|
||||||
dropZoneRef,
|
dropZoneRef,
|
||||||
} = $(useUploadMediaAttachment($$(draft)))
|
} = $(useUploadMediaAttachment($$(draft)))
|
||||||
|
|
||||||
let { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft, failedMessages } = $(usePublish(
|
let { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft, failedMessages, preferredLanguage } = $(usePublish(
|
||||||
{
|
{
|
||||||
draftState,
|
draftState,
|
||||||
...$$({ expanded, isUploading, initialDraft: initial }),
|
...$$({ expanded, isUploading, initialDraft: initial }),
|
||||||
|
@ -62,6 +62,7 @@ const { editor } = useTiptap({
|
||||||
},
|
},
|
||||||
onPaste: handlePaste,
|
onPaste: handlePaste,
|
||||||
})
|
})
|
||||||
|
|
||||||
const characterCount = $computed(() => {
|
const characterCount = $computed(() => {
|
||||||
let length = stringLength(htmlToText(editor.value?.getHTML() || ''))
|
let length = stringLength(htmlToText(editor.value?.getHTML() || ''))
|
||||||
|
|
||||||
|
@ -76,6 +77,8 @@ const characterCount = $computed(() => {
|
||||||
return length
|
return length
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const postLanguageDisplay = $computed(() => languagesNameList.find(i => i.code === (draft.params.language || preferredLanguage))?.nativeName)
|
||||||
|
|
||||||
async function handlePaste(evt: ClipboardEvent) {
|
async function handlePaste(evt: ClipboardEvent) {
|
||||||
const files = evt.clipboardData?.files
|
const files = evt.clipboardData?.files
|
||||||
if (!files || files.length === 0)
|
if (!files || files.length === 0)
|
||||||
|
@ -147,7 +150,7 @@ defineExpose({
|
||||||
>
|
>
|
||||||
<ContentMentionGroup v-if="draft.mentions?.length && shouldExpanded" replying>
|
<ContentMentionGroup v-if="draft.mentions?.length && shouldExpanded" replying>
|
||||||
<button v-for="m, i of draft.mentions" :key="m" text-primary hover:color-red @click="draft.mentions?.splice(i, 1)">
|
<button v-for="m, i of draft.mentions" :key="m" text-primary hover:color-red @click="draft.mentions?.splice(i, 1)">
|
||||||
{{ acctToShortHandle(m) }}
|
{{ accountToShortHandle(m) }}
|
||||||
</button>
|
</button>
|
||||||
</ContentMentionGroup>
|
</ContentMentionGroup>
|
||||||
|
|
||||||
|
@ -278,6 +281,20 @@ defineExpose({
|
||||||
{{ characterCount ?? 0 }}<span text-secondary-light>/</span><span text-secondary-light>{{ characterLimit }}</span>
|
{{ characterCount ?? 0 }}<span text-secondary-light>/</span><span text-secondary-light>{{ characterLimit }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CommonTooltip placement="top" :content="$t('tooltip.change_language')">
|
||||||
|
<CommonDropdown placement="bottom" auto-boundary-max-size>
|
||||||
|
<button btn-action-icon :aria-label="$t('tooltip.change_language')" w-max mr1>
|
||||||
|
<span v-if="postLanguageDisplay" text-secondary text-sm ml1>{{ postLanguageDisplay }}</span>
|
||||||
|
<div v-else i-ri:translate-2 />
|
||||||
|
<div i-ri:arrow-down-s-line text-sm text-secondary me--1 />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<template #popper>
|
||||||
|
<PublishLanguagePicker v-model="draft.params.language" min-w-80 />
|
||||||
|
</template>
|
||||||
|
</CommonDropdown>
|
||||||
|
</CommonTooltip>
|
||||||
|
|
||||||
<CommonTooltip placement="top" :content="$t('tooltip.add_content_warning')">
|
<CommonTooltip placement="top" :content="$t('tooltip.add_content_warning')">
|
||||||
<button btn-action-icon :aria-label="$t('tooltip.add_content_warning')" @click="toggleSensitive">
|
<button btn-action-icon :aria-label="$t('tooltip.add_content_warning')" @click="toggleSensitive">
|
||||||
<div v-if="draft.params.sensitive" i-ri:alarm-warning-fill text-orange />
|
<div v-if="draft.params.sensitive" i-ri:alarm-warning-fill text-orange />
|
||||||
|
@ -285,19 +302,6 @@ defineExpose({
|
||||||
</button>
|
</button>
|
||||||
</CommonTooltip>
|
</CommonTooltip>
|
||||||
|
|
||||||
<CommonTooltip placement="top" :content="$t('tooltip.change_language')">
|
|
||||||
<CommonDropdown placement="bottom" auto-boundary-max-size>
|
|
||||||
<button btn-action-icon :aria-label="$t('tooltip.change_language')" w-12 mr--1>
|
|
||||||
<div i-ri:translate-2 />
|
|
||||||
<div i-ri:arrow-down-s-line text-sm text-secondary me--1 />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<template #popper>
|
|
||||||
<PublishLanguagePicker v-model="draft.params.language" min-w-80 p3 />
|
|
||||||
</template>
|
|
||||||
</CommonDropdown>
|
|
||||||
</CommonTooltip>
|
|
||||||
|
|
||||||
<PublishVisibilityPicker v-model="draft.params.visibility" :editing="!!draft.editingStatus">
|
<PublishVisibilityPicker v-model="draft.params.visibility" :editing="!!draft.editingStatus">
|
||||||
<template #default="{ visibility }">
|
<template #default="{ visibility }">
|
||||||
<button :disabled="!!draft.editingStatus" :aria-label="$t('tooltip.change_content_visibility')" btn-action-icon :class="{ 'w-12': !draft.editingStatus }">
|
<button :disabled="!!draft.editingStatus" :aria-label="$t('tooltip.change_content_visibility')" btn-action-icon :class="{ 'w-12': !draft.editingStatus }">
|
||||||
|
|
|
@ -5,7 +5,7 @@ const all = useUsers()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const clickUser = (user: UserLogin) => {
|
const clickUser = (user: UserLogin) => {
|
||||||
if (user.account.id === currentUser.value?.account.id)
|
if (user.account.acct === currentUser.value?.account.acct)
|
||||||
router.push(getAccountRoute(user.account))
|
router.push(getAccountRoute(user.account))
|
||||||
else
|
else
|
||||||
switchUser(user)
|
switchUser(user)
|
||||||
|
@ -21,7 +21,7 @@ const clickUser = (user: UserLogin) => {
|
||||||
flex rounded
|
flex rounded
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
aria-label="Switch user"
|
aria-label="Switch user"
|
||||||
:class="user.account.id === currentUser?.account.id ? '' : 'op25 grayscale'"
|
:class="user.account.acct === currentUser?.account.acct ? '' : 'op25 grayscale'"
|
||||||
hover="filter-none op100"
|
hover="filter-none op100"
|
||||||
@click="clickUser(user)"
|
@click="clickUser(user)"
|
||||||
>
|
>
|
||||||
|
|
|
@ -51,7 +51,7 @@ const clickUser = (user: UserLogin) => {
|
||||||
:text="$t('user.sign_out_account', [getFullHandle(currentUser.account)])"
|
:text="$t('user.sign_out_account', [getFullHandle(currentUser.account)])"
|
||||||
icon="i-ri:logout-box-line rtl-flip"
|
icon="i-ri:logout-box-line rtl-flip"
|
||||||
w-full
|
w-full
|
||||||
@click="signout"
|
@click="signOut"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ export interface Team {
|
||||||
mastodon: string
|
mastodon: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const teams: Team[] = [
|
export const elkTeamMembers: Team[] = [
|
||||||
{
|
{
|
||||||
github: 'antfu',
|
github: 'antfu',
|
||||||
display: 'Anthony Fu',
|
display: 'Anthony Fu',
|
||||||
|
|
|
@ -349,7 +349,7 @@ export const provideGlobalCommands = () => {
|
||||||
icon: 'i-ri:logout-box-line',
|
icon: 'i-ri:logout-box-line',
|
||||||
|
|
||||||
onActivate() {
|
onActivate() {
|
||||||
signout()
|
signOut()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function contentToVNode(
|
||||||
return h(Fragment, (tree.children as Node[] || []).map(n => treeToVNode(n)))
|
return h(Fragment, (tree.children as Node[] || []).map(n => treeToVNode(n)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nodeToVNode(node: Node): VNode | string | null {
|
function nodeToVNode(node: Node): VNode | string | null {
|
||||||
if (node.type === TEXT_NODE)
|
if (node.type === TEXT_NODE)
|
||||||
return node.value
|
return node.value
|
||||||
|
|
||||||
|
|
11
composables/langugage.ts
Normal file
11
composables/langugage.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import ISO6391 from 'iso-639-1'
|
||||||
|
|
||||||
|
export const languagesNameList: {
|
||||||
|
code: string
|
||||||
|
nativeName: string
|
||||||
|
name: string
|
||||||
|
}[] = ISO6391.getAllCodes().map(code => ({
|
||||||
|
code,
|
||||||
|
nativeName: ISO6391.getNativeName(code),
|
||||||
|
name: ISO6391.getName(code),
|
||||||
|
}))
|
|
@ -7,14 +7,14 @@ export function getDisplayName(account: mastodon.v1.Account, options?: { rich?:
|
||||||
return displayName.replace(/:([\w-]+?):/g, '')
|
return displayName.replace(/:([\w-]+?):/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function acctToShortHandle(acct: string) {
|
export function accountToShortHandle(acct: string) {
|
||||||
return `@${acct.includes('@') ? acct.split('@')[0] : acct}`
|
return `@${acct.includes('@') ? acct.split('@')[0] : acct}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getShortHandle({ acct }: mastodon.v1.Account) {
|
export function getShortHandle({ acct }: mastodon.v1.Account) {
|
||||||
if (!acct)
|
if (!acct)
|
||||||
return ''
|
return ''
|
||||||
return acctToShortHandle(acct)
|
return accountToShortHandle(acct)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServerName(account: mastodon.v1.Account) {
|
export function getServerName(account: mastodon.v1.Account) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ export function mastoLogin(masto: ElkMasto, user: Pick<UserLogin, 'server' | 'to
|
||||||
setParams({
|
setParams({
|
||||||
streamingApiUrl: newInstance.urls.streamingApi,
|
streamingApiUrl: newInstance.urls.streamingApi,
|
||||||
})
|
})
|
||||||
instances.value[server] = newInstance
|
instanceStorage.value[server] = newInstance
|
||||||
})
|
})
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
|
@ -4,15 +4,18 @@ import type { mastodon } from 'masto'
|
||||||
import type { UseDraft } from './statusDrafts'
|
import type { UseDraft } from './statusDrafts'
|
||||||
import type { Draft } from '~~/types'
|
import type { Draft } from '~~/types'
|
||||||
|
|
||||||
export const usePublish = (options: {
|
export function usePublish(options: {
|
||||||
draftState: UseDraft
|
draftState: UseDraft
|
||||||
expanded: Ref<boolean>
|
expanded: Ref<boolean>
|
||||||
isUploading: Ref<boolean>
|
isUploading: Ref<boolean>
|
||||||
initialDraft: Ref<() => Draft>
|
initialDraft: Ref<() => Draft>
|
||||||
}) => {
|
}) {
|
||||||
const { expanded, isUploading, initialDraft } = $(options)
|
const { expanded, isUploading, initialDraft } = $(options)
|
||||||
let { draft, isEmpty } = $(options.draftState)
|
let { draft, isEmpty } = $(options.draftState)
|
||||||
const { client } = $(useMasto())
|
const { client } = $(useMasto())
|
||||||
|
const settings = useUserSettings()
|
||||||
|
|
||||||
|
const preferredLanguage = $computed(() => (settings.value?.language || 'en').split('-')[0])
|
||||||
|
|
||||||
let isSending = $ref(false)
|
let isSending = $ref(false)
|
||||||
const isExpanded = $ref(false)
|
const isExpanded = $ref(false)
|
||||||
|
@ -31,6 +34,7 @@ export const usePublish = (options: {
|
||||||
async function publishDraft() {
|
async function publishDraft() {
|
||||||
if (isPublishDisabled)
|
if (isPublishDisabled)
|
||||||
return
|
return
|
||||||
|
|
||||||
let content = htmlToText(draft.params.status || '')
|
let content = htmlToText(draft.params.status || '')
|
||||||
if (draft.mentions?.length)
|
if (draft.mentions?.length)
|
||||||
content = `${draft.mentions.map(i => `@${i}`).join(' ')} ${content}`
|
content = `${draft.mentions.map(i => `@${i}`).join(' ')} ${content}`
|
||||||
|
@ -39,11 +43,12 @@ export const usePublish = (options: {
|
||||||
...draft.params,
|
...draft.params,
|
||||||
status: content,
|
status: content,
|
||||||
mediaIds: draft.attachments.map(a => a.id),
|
mediaIds: draft.attachments.map(a => a.id),
|
||||||
|
language: draft.params.language || preferredLanguage,
|
||||||
...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}),
|
...(isGlitchEdition.value ? { 'content-type': 'text/markdown' } : {}),
|
||||||
} as mastodon.v1.CreateStatusParams
|
} as mastodon.v1.CreateStatusParams
|
||||||
|
|
||||||
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.params.status,
|
||||||
...payload,
|
...payload,
|
||||||
|
@ -60,6 +65,7 @@ export const usePublish = (options: {
|
||||||
let status: mastodon.v1.Status
|
let status: mastodon.v1.Status
|
||||||
if (!draft.editingStatus)
|
if (!draft.editingStatus)
|
||||||
status = await client.v1.statuses.create(payload)
|
status = await client.v1.statuses.create(payload)
|
||||||
|
|
||||||
else
|
else
|
||||||
status = await client.v1.statuses.update(draft.editingStatus.id, payload)
|
status = await client.v1.statuses.update(draft.editingStatus.id, payload)
|
||||||
if (draft.params.inReplyToId)
|
if (draft.params.inReplyToId)
|
||||||
|
@ -84,14 +90,14 @@ export const usePublish = (options: {
|
||||||
shouldExpanded,
|
shouldExpanded,
|
||||||
isPublishDisabled,
|
isPublishDisabled,
|
||||||
failedMessages,
|
failedMessages,
|
||||||
|
preferredLanguage,
|
||||||
publishDraft,
|
publishDraft,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MediaAttachmentUploadError = [filename: string, message: string]
|
export type MediaAttachmentUploadError = [filename: string, message: string]
|
||||||
|
|
||||||
export const useUploadMediaAttachment = (draftRef: Ref<Draft>) => {
|
export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||||
const draft = $(draftRef)
|
const draft = $(draftRef)
|
||||||
const { client } = $(useMasto())
|
const { client } = $(useMasto())
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
@ -117,7 +123,7 @@ export const useUploadMediaAttachment = (draftRef: Ref<Draft>) => {
|
||||||
draft.attachments.push(attachment)
|
draft.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 = [...failedAttachments, [file.name, (e as Error).message]]
|
||||||
}
|
}
|
||||||
|
@ -159,9 +165,10 @@ export const useUploadMediaAttachment = (draftRef: Ref<Draft>) => {
|
||||||
return $$({
|
return $$({
|
||||||
isUploading,
|
isUploading,
|
||||||
isExceedingAttachmentLimit,
|
isExceedingAttachmentLimit,
|
||||||
|
isOverDropZone,
|
||||||
|
|
||||||
failedAttachments,
|
failedAttachments,
|
||||||
dropZoneRef,
|
dropZoneRef,
|
||||||
isOverDropZone,
|
|
||||||
|
|
||||||
uploadAttachments,
|
uploadAttachments,
|
||||||
pickAttachments,
|
pickAttachments,
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function getDefaultDraft(options: Partial<Mutable<mastodon.v1.CreateStatu
|
||||||
visibility: visibility || 'public',
|
visibility: visibility || 'public',
|
||||||
sensitive: sensitive ?? false,
|
sensitive: sensitive ?? false,
|
||||||
spoilerText: spoilerText || '',
|
spoilerText: spoilerText || '',
|
||||||
language: language || getDefaultLanguage(),
|
language: language || '', // auto inferred from current language on posting
|
||||||
},
|
},
|
||||||
mentions,
|
mentions,
|
||||||
lastUpdated: Date.now(),
|
lastUpdated: Date.now(),
|
||||||
|
@ -52,16 +52,6 @@ export async function getDraftFromStatus(status: mastodon.v1.Status): Promise<Dr
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultLanguage() {
|
|
||||||
const userSettings = useUserSettings()
|
|
||||||
const defaultLanguage = userSettings.value.language
|
|
||||||
|
|
||||||
if (defaultLanguage)
|
|
||||||
return defaultLanguage.split('-')[0]
|
|
||||||
|
|
||||||
return 'en'
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAccountsToMention(status: mastodon.v1.Status) {
|
function getAccountsToMention(status: mastodon.v1.Status) {
|
||||||
const userId = currentUser.value?.account.id
|
const userId = currentUser.value?.account.id
|
||||||
const accountsToMention = new Set<string>()
|
const accountsToMention = new Set<string>()
|
||||||
|
|
|
@ -2,6 +2,4 @@ import { breakpointsTailwind } from '@vueuse/core'
|
||||||
|
|
||||||
export const breakpoints = useBreakpoints(breakpointsTailwind)
|
export const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
|
|
||||||
export const isSmallScreen = breakpoints.smallerOrEqual('md')
|
|
||||||
export const isMediumScreen = breakpoints.smallerOrEqual('lg')
|
|
||||||
export const isMediumOrLargeScreen = breakpoints.between('sm', 'xl')
|
export const isMediumOrLargeScreen = breakpoints.between('sm', 'xl')
|
||||||
|
|
|
@ -7,7 +7,6 @@ import type { UserLogin } from '~/types'
|
||||||
import type { Overwrite } from '~/types/utils'
|
import type { Overwrite } from '~/types/utils'
|
||||||
import {
|
import {
|
||||||
DEFAULT_POST_CHARS_LIMIT,
|
DEFAULT_POST_CHARS_LIMIT,
|
||||||
STORAGE_KEY_CURRENT_USER,
|
|
||||||
STORAGE_KEY_CURRENT_USER_HANDLE,
|
STORAGE_KEY_CURRENT_USER_HANDLE,
|
||||||
STORAGE_KEY_NODES,
|
STORAGE_KEY_NODES,
|
||||||
STORAGE_KEY_NOTIFICATION,
|
STORAGE_KEY_NOTIFICATION,
|
||||||
|
@ -44,20 +43,20 @@ const initializeUsers = async (): Promise<Ref<UserLogin[]> | RemovableRef<UserLo
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = await initializeUsers()
|
const users = await initializeUsers()
|
||||||
export const instances = useLocalStorage<Record<string, mastodon.v1.Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true })
|
const nodes = useLocalStorage<Record<string, any>>(STORAGE_KEY_NODES, {}, { deep: true })
|
||||||
export const nodes = useLocalStorage<Record<string, any>>(STORAGE_KEY_NODES, {}, { deep: true })
|
const currentUserHandle = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER_HANDLE, mock ? mock.user.account.id : '')
|
||||||
const currentUserId = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER, mock ? mock.user.account.id : '')
|
export const instanceStorage = useLocalStorage<Record<string, mastodon.v1.Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true })
|
||||||
|
|
||||||
export type ElkInstance = Partial<mastodon.v1.Instance> & {
|
export type ElkInstance = Partial<mastodon.v1.Instance> & {
|
||||||
uri: string
|
uri: string
|
||||||
/** support GoToSocial */
|
/** support GoToSocial */
|
||||||
accountDomain?: string | null
|
accountDomain?: string | null
|
||||||
}
|
}
|
||||||
export const getInstanceCache = (server: string): mastodon.v1.Instance | undefined => instances.value[server]
|
export const getInstanceCache = (server: string): mastodon.v1.Instance | undefined => instanceStorage.value[server]
|
||||||
|
|
||||||
export const currentUser = computed<UserLogin | undefined>(() => {
|
export const currentUser = computed<UserLogin | undefined>(() => {
|
||||||
if (currentUserId.value) {
|
if (currentUserHandle.value) {
|
||||||
const user = users.value.find(user => user.account?.id === currentUserId.value)
|
const user = users.value.find(user => user.account?.acct === currentUserHandle.value)
|
||||||
if (user)
|
if (user)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,7 @@ export const currentUser = computed<UserLogin | undefined>(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const publicInstance = ref<ElkInstance | null>(null)
|
const publicInstance = ref<ElkInstance | null>(null)
|
||||||
export const currentInstance = computed<null | ElkInstance>(() => currentUser.value ? instances.value[currentUser.value.server] ?? null : publicInstance.value)
|
export const currentInstance = computed<null | ElkInstance>(() => currentUser.value ? instanceStorage.value[currentUser.value.server] ?? null : publicInstance.value)
|
||||||
|
|
||||||
export function getInstanceDomain(instance: ElkInstance) {
|
export function getInstanceDomain(instance: ElkInstance) {
|
||||||
return instance.accountDomain || withoutProtocol(instance.uri)
|
return instance.accountDomain || withoutProtocol(instance.uri)
|
||||||
|
@ -84,12 +83,12 @@ if (process.client) {
|
||||||
const windowReload = () => {
|
const windowReload = () => {
|
||||||
document.visibilityState === 'visible' && window.location.reload()
|
document.visibilityState === 'visible' && window.location.reload()
|
||||||
}
|
}
|
||||||
watch(currentUserId, async (id, oldId) => {
|
watch(currentUserHandle, async (handle, oldHandle) => {
|
||||||
// when sign in or switch account
|
// when sign in or switch account
|
||||||
if (id) {
|
if (handle) {
|
||||||
if (id === currentUser.value?.account?.id) {
|
if (handle === currentUser.value?.account?.acct) {
|
||||||
// when sign in, the other tab will not have the user, idb is not reactive
|
// when sign in, the other tab will not have the user, idb is not reactive
|
||||||
const newUser = users.value.find(user => user.account?.id === id)
|
const newUser = users.value.find(user => user.account?.acct === handle)
|
||||||
// if the user is there, then we are switching account
|
// if the user is there, then we are switching account
|
||||||
if (newUser) {
|
if (newUser) {
|
||||||
// check if the change is on current tab: if so, don't reload
|
// check if the change is on current tab: if so, don't reload
|
||||||
|
@ -101,19 +100,13 @@ if (process.client) {
|
||||||
window.addEventListener('visibilitychange', windowReload, { capture: true })
|
window.addEventListener('visibilitychange', windowReload, { capture: true })
|
||||||
}
|
}
|
||||||
// when sign out
|
// when sign out
|
||||||
else if (oldId) {
|
else if (oldHandle) {
|
||||||
const oldUser = users.value.find(user => user.account?.id === oldId)
|
const oldUser = users.value.find(user => user.account?.acct === oldHandle)
|
||||||
// when sign out, the other tab will not have the user, idb is not reactive
|
// when sign out, the other tab will not have the user, idb is not reactive
|
||||||
if (oldUser)
|
if (oldUser)
|
||||||
window.addEventListener('visibilitychange', windowReload, { capture: true })
|
window.addEventListener('visibilitychange', windowReload, { capture: true })
|
||||||
}
|
}
|
||||||
}, { immediate: true, flush: 'post' })
|
}, { immediate: true, flush: 'post' })
|
||||||
|
|
||||||
// for injected script to read
|
|
||||||
const currentUserHandle = computed(() => currentUser.value?.account.acct || '')
|
|
||||||
watchEffect(() => {
|
|
||||||
localStorage.setItem(STORAGE_KEY_CURRENT_USER_HANDLE, currentUserHandle.value)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUsers = () => users
|
export const useUsers = () => users
|
||||||
|
@ -144,7 +137,7 @@ export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { acco
|
||||||
|
|
||||||
const account = getUser()?.account
|
const account = getUser()?.account
|
||||||
if (account)
|
if (account)
|
||||||
currentUserId.value = account.id
|
currentUserHandle.value = account.acct
|
||||||
|
|
||||||
const [me, pushSubscription] = await Promise.all([
|
const [me, pushSubscription] = await Promise.all([
|
||||||
fetchAccountInfo(client, user.server),
|
fetchAccountInfo(client, user.server),
|
||||||
|
@ -168,7 +161,7 @@ export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { acco
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
currentUserId.value = me.id
|
currentUserHandle.value = me.acct
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAccountInfo(client: mastodon.Client, server: string) {
|
export async function fetchAccountInfo(client: mastodon.Client, server: string) {
|
||||||
|
@ -238,7 +231,7 @@ export async function switchUser(user: UserLogin) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signout() {
|
export async function signOut() {
|
||||||
// TODO: confirm
|
// TODO: confirm
|
||||||
if (!currentUser.value)
|
if (!currentUser.value)
|
||||||
return
|
return
|
||||||
|
@ -253,21 +246,21 @@ export async function signout() {
|
||||||
// Clear stale data
|
// Clear stale data
|
||||||
clearUserLocalStorage()
|
clearUserLocalStorage()
|
||||||
if (!users.value.some((u, i) => u.server === currentUser.value!.server && i !== index))
|
if (!users.value.some((u, i) => u.server === currentUser.value!.server && i !== index))
|
||||||
delete instances.value[currentUser.value.server]
|
delete instanceStorage.value[currentUser.value.server]
|
||||||
|
|
||||||
await removePushNotifications(currentUser.value)
|
await removePushNotifications(currentUser.value)
|
||||||
|
|
||||||
await removePushNotificationData(currentUser.value)
|
await removePushNotificationData(currentUser.value)
|
||||||
|
|
||||||
currentUserId.value = ''
|
currentUserHandle.value = ''
|
||||||
// Remove the current user from the users
|
// Remove the current user from the users
|
||||||
users.value.splice(index, 1)
|
users.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set currentUserId to next user if available
|
// Set currentUserId to next user if available
|
||||||
currentUserId.value = users.value[0]?.account?.id
|
currentUserHandle.value = users.value[0]?.account?.acct
|
||||||
|
|
||||||
if (!currentUserId.value)
|
if (!currentUserHandle.value)
|
||||||
await useRouter().push('/')
|
await useRouter().push('/')
|
||||||
|
|
||||||
loginTo(masto, currentUser.value)
|
loginTo(masto, currentUser.value)
|
||||||
|
|
|
@ -7,7 +7,6 @@ export const STORAGE_KEY_DRAFTS = 'elk-drafts'
|
||||||
export const STORAGE_KEY_USERS = 'elk-users'
|
export const STORAGE_KEY_USERS = 'elk-users'
|
||||||
export const STORAGE_KEY_SERVERS = 'elk-servers'
|
export const STORAGE_KEY_SERVERS = 'elk-servers'
|
||||||
export const STORAGE_KEY_NODES = 'elk-nodes'
|
export const STORAGE_KEY_NODES = 'elk-nodes'
|
||||||
export const STORAGE_KEY_CURRENT_USER = 'elk-current-user'
|
|
||||||
export const STORAGE_KEY_CURRENT_USER_HANDLE = 'elk-current-user-handle'
|
export const STORAGE_KEY_CURRENT_USER_HANDLE = 'elk-current-user-handle'
|
||||||
export const STORAGE_KEY_NOTIFY_TAB = 'elk-notify-tab'
|
export const STORAGE_KEY_NOTIFY_TAB = 'elk-notify-tab'
|
||||||
export const STORAGE_KEY_FIRST_VISIT = 'elk-first-visit'
|
export const STORAGE_KEY_FIRST_VISIT = 'elk-first-visit'
|
||||||
|
|
|
@ -22,6 +22,7 @@ export default defineNuxtModule({
|
||||||
...nuxt.options.alias,
|
...nuxt.options.alias,
|
||||||
'unstorage/drivers/fs': 'unenv/runtime/mock/proxy',
|
'unstorage/drivers/fs': 'unenv/runtime/mock/proxy',
|
||||||
'unstorage/drivers/cloudflare-kv-http': 'unenv/runtime/mock/proxy',
|
'unstorage/drivers/cloudflare-kv-http': 'unenv/runtime/mock/proxy',
|
||||||
|
'#storage-config': resolve('./runtime/storage-config'),
|
||||||
'node:events': 'unenv/runtime/node/events/index',
|
'node:events': 'unenv/runtime/node/events/index',
|
||||||
'#build-info': resolve('./runtime/build-info'),
|
'#build-info': resolve('./runtime/build-info'),
|
||||||
}
|
}
|
||||||
|
|
2
modules/tauri/runtime/storage-config.ts
Normal file
2
modules/tauri/runtime/storage-config.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export const driver = undefined
|
||||||
|
export const fsBase = ''
|
|
@ -1,4 +1,4 @@
|
||||||
import { createResolver } from '@nuxt/kit'
|
import { createResolver, useNuxt } from '@nuxt/kit'
|
||||||
import Inspect from 'vite-plugin-inspect'
|
import Inspect from 'vite-plugin-inspect'
|
||||||
import { isCI, isDevelopment, isWindows } from 'std-env'
|
import { isCI, isDevelopment, isWindows } from 'std-env'
|
||||||
import { isPreview } from './config/env'
|
import { isPreview } from './config/env'
|
||||||
|
@ -86,6 +86,11 @@ export default defineNuxtConfig({
|
||||||
'postcss-nested': {},
|
'postcss-nested': {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
appConfig: {
|
||||||
|
storage: {
|
||||||
|
driver: process.env.NUXT_STORAGE_DRIVER ?? (isCI ? 'cloudflare' : 'fs'),
|
||||||
|
},
|
||||||
|
},
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
adminKey: '',
|
adminKey: '',
|
||||||
cloudflare: {
|
cloudflare: {
|
||||||
|
@ -102,8 +107,7 @@ export default defineNuxtConfig({
|
||||||
defaultServer: 'm.webtoo.ls',
|
defaultServer: 'm.webtoo.ls',
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
driver: isCI ? 'cloudflare' : 'fs',
|
fsBase: 'node_modules/.cache/app',
|
||||||
fsBase: 'node_modules/.cache/servers',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
routeRules: {
|
routeRules: {
|
||||||
|
@ -126,6 +130,13 @@ export default defineNuxtConfig({
|
||||||
ignore: ['/settings'],
|
ignore: ['/settings'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
hooks: {
|
||||||
|
'nitro:config': function (config) {
|
||||||
|
const nuxt = useNuxt()
|
||||||
|
config.virtual = config.virtual || {}
|
||||||
|
config.virtual['#storage-config'] = `export const driver = ${JSON.stringify(nuxt.options.appConfig.storage.driver)}`
|
||||||
|
},
|
||||||
|
},
|
||||||
app: {
|
app: {
|
||||||
keepalive: true,
|
keepalive: true,
|
||||||
head: {
|
head: {
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
"unplugin-vue-inspector": "^0.0.2",
|
"unplugin-vue-inspector": "^0.0.2",
|
||||||
"vite-plugin-inspect": "^0.7.14",
|
"vite-plugin-inspect": "^0.7.14",
|
||||||
"vite-plugin-pwa": "^0.14.1",
|
"vite-plugin-pwa": "^0.14.1",
|
||||||
"vitest": "^0.28.1",
|
"vitest": "^0.28.3",
|
||||||
"vue-tsc": "^1.0.24",
|
"vue-tsc": "^1.0.24",
|
||||||
"workbox-build": "^6.5.4",
|
"workbox-build": "^6.5.4",
|
||||||
"workbox-window": "^6.5.4"
|
"workbox-window": "^6.5.4"
|
||||||
|
|
|
@ -115,7 +115,7 @@ const handleShowCommit = () => {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
v-for="team in teams" :key="team.github"
|
v-for="team in elkTeamMembers" :key="team.github"
|
||||||
:text="team.display"
|
:text="team.display"
|
||||||
:to="`https://github.com/sponsors/${team.github}`"
|
:to="`https://github.com/sponsors/${team.github}`"
|
||||||
external target="_blank"
|
external target="_blank"
|
||||||
|
|
|
@ -119,7 +119,7 @@ importers:
|
||||||
unplugin-vue-inspector: ^0.0.2
|
unplugin-vue-inspector: ^0.0.2
|
||||||
vite-plugin-inspect: ^0.7.14
|
vite-plugin-inspect: ^0.7.14
|
||||||
vite-plugin-pwa: ^0.14.1
|
vite-plugin-pwa: ^0.14.1
|
||||||
vitest: ^0.28.1
|
vitest: ^0.28.3
|
||||||
vue-advanced-cropper: ^2.8.8
|
vue-advanced-cropper: ^2.8.8
|
||||||
vue-tsc: ^1.0.24
|
vue-tsc: ^1.0.24
|
||||||
vue-virtual-scroller: 2.0.0-beta.7
|
vue-virtual-scroller: 2.0.0-beta.7
|
||||||
|
@ -216,7 +216,7 @@ importers:
|
||||||
unplugin-auto-import: 0.13.0_@vueuse+core@9.11.1
|
unplugin-auto-import: 0.13.0_@vueuse+core@9.11.1
|
||||||
unplugin-vue-inspector: 0.0.2
|
unplugin-vue-inspector: 0.0.2
|
||||||
vite-plugin-inspect: 0.7.14
|
vite-plugin-inspect: 0.7.14
|
||||||
vite-plugin-pwa: 0.14.1_tz3vz2xt4jvid2diblkpydcyn4
|
vite-plugin-pwa: 0.14.1
|
||||||
vitest: 0.28.3_jsdom@21.1.0
|
vitest: 0.28.3_jsdom@21.1.0
|
||||||
vue-tsc: 1.0.24_typescript@4.9.4
|
vue-tsc: 1.0.24_typescript@4.9.4
|
||||||
workbox-build: 6.5.4
|
workbox-build: 6.5.4
|
||||||
|
@ -12949,12 +12949,10 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-pwa/0.14.1_tz3vz2xt4jvid2diblkpydcyn4:
|
/vite-plugin-pwa/0.14.1:
|
||||||
resolution: {integrity: sha512-5zx7yhQ8RTLwV71+GA9YsQQ63ALKG8XXIMqRJDdZkR8ZYftFcRgnzM7wOWmQZ/DATspyhPih5wCdcZnAIsM+mA==}
|
resolution: {integrity: sha512-5zx7yhQ8RTLwV71+GA9YsQQ63ALKG8XXIMqRJDdZkR8ZYftFcRgnzM7wOWmQZ/DATspyhPih5wCdcZnAIsM+mA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^3.1.0 || ^4.0.0
|
vite: ^3.1.0 || ^4.0.0
|
||||||
workbox-build: ^6.5.4
|
|
||||||
workbox-window: ^6.5.4
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/plugin-replace': 5.0.2_rollup@3.10.1
|
'@rollup/plugin-replace': 5.0.2_rollup@3.10.1
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
|
@ -12964,6 +12962,7 @@ packages:
|
||||||
workbox-build: 6.5.4
|
workbox-build: 6.5.4
|
||||||
workbox-window: 6.5.4
|
workbox-window: 6.5.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
- '@types/babel__core'
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { join, resolve } from 'pathe'
|
import { join, resolve } from 'pathe'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { $fetch } from 'ohmyfetch'
|
import { $fetch } from 'ohmyfetch'
|
||||||
import { teams } from '../composables/about'
|
import { elkTeamMembers } from '../composables/about'
|
||||||
|
|
||||||
const avatarsDir = resolve('./public/avatars/')
|
const avatarsDir = resolve('./public/avatars/')
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ async function download(url: string, fileName: string) {
|
||||||
async function fetchAvatars() {
|
async function fetchAvatars() {
|
||||||
await fs.ensureDir(avatarsDir)
|
await fs.ensureDir(avatarsDir)
|
||||||
|
|
||||||
await Promise.all(teams.reduce((acc, { github }) => {
|
await Promise.all(elkTeamMembers.reduce((acc, { github }) => {
|
||||||
acc.push(...sizes.map(s => download(`https://github.com/${github}.png?size=${s}`, join(avatarsDir, `${github}-${s}x${s}.png`))))
|
acc.push(...sizes.map(s => download(`https://github.com/${github}.png?size=${s}`, join(avatarsDir, `${github}-${s}x${s}.png`))))
|
||||||
return acc
|
return acc
|
||||||
}, [] as Promise<void>[]))
|
}, [] as Promise<void>[]))
|
||||||
|
|
|
@ -14,29 +14,31 @@ import cached from './cache-driver'
|
||||||
|
|
||||||
// @ts-expect-error virtual import
|
// @ts-expect-error virtual import
|
||||||
import { env } from '#build-info'
|
import { env } from '#build-info'
|
||||||
|
// @ts-expect-error virtual import
|
||||||
|
import { driver } from '#storage-config'
|
||||||
|
|
||||||
import type { AppInfo } from '~/types'
|
import type { AppInfo } from '~/types'
|
||||||
import { APP_NAME } from '~/constants'
|
import { APP_NAME } from '~/constants'
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
|
|
||||||
const fs = _fs as typeof import('unstorage/dist/drivers/fs')['default']
|
const fs = _fs as typeof import('unstorage/dist/drivers/fs')['default']
|
||||||
const kv = _kv as typeof import('unstorage/dist/drivers/cloudflare-kv-http')['default']
|
const kv = _kv as typeof import('unstorage/dist/drivers/cloudflare-kv-http')['default']
|
||||||
const memory = _memory as typeof import('unstorage/dist/drivers/memory')['default']
|
const memory = _memory as typeof import('unstorage/dist/drivers/memory')['default']
|
||||||
|
|
||||||
const storage = useStorage() as Storage
|
const storage = useStorage() as Storage
|
||||||
|
|
||||||
if (config.storage.driver === 'fs') {
|
if (driver === 'fs') {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
storage.mount('servers', fs({ base: config.storage.fsBase }))
|
storage.mount('servers', fs({ base: config.storage.fsBase }))
|
||||||
}
|
}
|
||||||
else if (config.storage.driver === 'cloudflare') {
|
else if (driver === 'cloudflare') {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
storage.mount('servers', cached(kv({
|
storage.mount('servers', cached(kv({
|
||||||
accountId: config.cloudflare.accountId,
|
accountId: config.cloudflare.accountId,
|
||||||
namespaceId: config.cloudflare.namespaceId,
|
namespaceId: config.cloudflare.namespaceId,
|
||||||
apiToken: config.cloudflare.apiToken,
|
apiToken: config.cloudflare.apiToken,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
else if (config.storage.driver === 'memory') {
|
else if (driver === 'memory') {
|
||||||
storage.mount('servers', memory())
|
storage.mount('servers', memory())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
// Vitest Snapshot v1
|
|
||||||
|
|
||||||
exports[`content-rich > block with backticks 1`] = `"<p><pre class=\\"code-block\\">[(\`number string) (\`tag string)]</pre></p>"`;
|
|
||||||
|
|
||||||
exports[`content-rich > block with injected html, with a known language 1`] = `
|
|
||||||
"<pre>
|
|
||||||
<code class=\\"language-js\\">
|
|
||||||
<a href="javascript:alert(1)">click me</a>
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > block with injected html, with an unknown language 1`] = `
|
|
||||||
"<pre>
|
|
||||||
<code class=\\"language-xyzzy\\">
|
|
||||||
<a href="javascript:alert(1)">click me</a>
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > block with injected html, without language 1`] = `
|
|
||||||
"<pre>
|
|
||||||
<code>
|
|
||||||
<a href="javascript:alert(1)">click me</a>
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > code frame 1`] = `
|
|
||||||
"<p>Testing code block</p><p></p><p><pre class=\\"code-block\\">import { useMouse, usePreferredDark } from '@vueuse/core'
|
|
||||||
// tracks mouse position
|
|
||||||
const { x, y } = useMouse()
|
|
||||||
// is the user prefers dark theme
|
|
||||||
const isDark = usePreferredDark()</pre></p>"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > code frame 2 1`] = `
|
|
||||||
"<p>
|
|
||||||
<span class=\\"h-card\\"
|
|
||||||
><a
|
|
||||||
class=\\"u-url mention\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
to=\\"/webtoo.ls/@antfu\\"
|
|
||||||
></a
|
|
||||||
></span>
|
|
||||||
Testing<br />
|
|
||||||
<pre class=\\"code-block\\">const a = hello</pre>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > code frame empty 1`] = `"<p><pre class=\\"code-block\\"></pre><br></p>"`;
|
|
||||||
|
|
||||||
exports[`content-rich > code frame no lang 1`] = `"<p><pre class=\\"code-block\\">hello world</pre><br>no lang</p>"`;
|
|
||||||
|
|
||||||
exports[`content-rich > custom emoji 1`] = `
|
|
||||||
"Daniel Roe
|
|
||||||
<picture alt=\\":nuxt:\\" class=\\"custom-emoji\\" data-emoji-id=\\"nuxt\\"
|
|
||||||
><source
|
|
||||||
srcset=\\"
|
|
||||||
https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png
|
|
||||||
\\"
|
|
||||||
media=\\"(prefers-reduced-motion: reduce)\\" />
|
|
||||||
<img
|
|
||||||
src=\\"https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png\\"
|
|
||||||
alt=\\":nuxt:\\"
|
|
||||||
/></picture>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > empty 1`] = `""`;
|
|
||||||
|
|
||||||
exports[`content-rich > group mention > html 1`] = `
|
|
||||||
"<p>
|
|
||||||
<span class=\\"h-card\\"
|
|
||||||
><a
|
|
||||||
class=\\"u-url mention\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
to=\\"//@pilipinas@lemmy.ml\\"
|
|
||||||
></a
|
|
||||||
></span>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > handles formatting from servers 1`] = `
|
|
||||||
"<h1>Fedi HTML Support Survey</h1>
|
|
||||||
<p>Does the following formatting come through accurately for you?</p>
|
|
||||||
<p></p>
|
|
||||||
<ul>
|
|
||||||
<li>This is an indented bulleted list (not just asterisks).</li>
|
|
||||||
<li><strong>This line is bold.</strong></li>
|
|
||||||
<li><em>This line is italic.</em></li>
|
|
||||||
</ul>
|
|
||||||
<ol>
|
|
||||||
<li>This list...</li>
|
|
||||||
<li>...is numbered and indented</li>
|
|
||||||
</ol>
|
|
||||||
<h1>This line is larger.</h1>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > handles html within code blocks 1`] = `
|
|
||||||
"<p>
|
|
||||||
HTML block code:<br />
|
|
||||||
<pre class=\\"code-block\\">
|
|
||||||
<span class="icon--noto icon--noto--1st-place-medal"></span>
|
|
||||||
<span class="icon--noto icon--noto--2nd-place-medal-medal"></span></pre
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > inline code with link 1`] = `
|
|
||||||
"<p>
|
|
||||||
Inline code with link:
|
|
||||||
<code
|
|
||||||
>https://api.iconify.design/noto.css?icons=1st-place-medal,2nd-place-medal</code
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > link + mention 1`] = `
|
|
||||||
"<p>
|
|
||||||
Happy
|
|
||||||
<img
|
|
||||||
src=\\"/emojis/twemoji/1f917.svg\\"
|
|
||||||
alt=\\"🤗\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/>
|
|
||||||
we’re now using
|
|
||||||
<span class=\\"h-card\\"
|
|
||||||
><a
|
|
||||||
class=\\"u-url mention\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
to=\\"/webtoo.ls/@vitest\\"
|
|
||||||
></a
|
|
||||||
></span>
|
|
||||||
(migrated from chai+mocha)
|
|
||||||
<a
|
|
||||||
href=\\"https://github.com/ayoayco/astro-reactive-library/pull/203\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
target=\\"_blank\\"
|
|
||||||
><span class=\\"invisible\\">https://</span
|
|
||||||
><span class=\\"ellipsis\\">github.com/ayoayco/astro-react</span
|
|
||||||
><span class=\\"invisible\\">ive-library/pull/203</span></a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`content-rich > plain text 1`] = `
|
|
||||||
"hello there
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`editor > transform mentions 1`] = `
|
|
||||||
"
|
|
||||||
@elk Hello"
|
|
||||||
`;
|
|
|
@ -1,144 +0,0 @@
|
||||||
// Vitest Snapshot v1
|
|
||||||
|
|
||||||
exports[`html-parse > code frame > html 1`] = `
|
|
||||||
"<p>Testing code block</p><p></p><p><pre><code class=\\"language-ts\\">import { useMouse, usePreferredDark } from '@vueuse/core'
|
|
||||||
// tracks mouse position
|
|
||||||
const { x, y } = useMouse()
|
|
||||||
// is the user prefers dark theme
|
|
||||||
const isDark = usePreferredDark()</code></pre></p>"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > code frame > text 1`] = `
|
|
||||||
"Testing code block
|
|
||||||
|
|
||||||
|
|
||||||
\`\`\`ts
|
|
||||||
import { useMouse, usePreferredDark } from '@vueuse/core'
|
|
||||||
// tracks mouse position
|
|
||||||
const { x, y } = useMouse()
|
|
||||||
// is the user prefers dark theme
|
|
||||||
const isDark = usePreferredDark()
|
|
||||||
\`\`\`"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > code frame 2 > html 1`] = `
|
|
||||||
"<p>
|
|
||||||
<span class=\\"h-card\\"
|
|
||||||
><a
|
|
||||||
href=\\"https://webtoo.ls/@antfu\\"
|
|
||||||
class=\\"u-url mention\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
target=\\"_blank\\"
|
|
||||||
>@<span>antfu</span></a
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
Testing<br />
|
|
||||||
<pre><code class=\\"language-ts\\">const a = hello</code></pre>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > code frame 2 > text 1`] = `
|
|
||||||
"@antfu Testing
|
|
||||||
|
|
||||||
\`\`\`ts
|
|
||||||
const a = hello
|
|
||||||
\`\`\`"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > custom emoji > html 1`] = `
|
|
||||||
"Daniel Roe
|
|
||||||
<picture alt=\\":nuxt:\\" class=\\"custom-emoji\\" data-emoji-id=\\"nuxt\\"
|
|
||||||
><source
|
|
||||||
srcset=\\"
|
|
||||||
https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png
|
|
||||||
\\"
|
|
||||||
media=\\"(prefers-reduced-motion: reduce)\\" />
|
|
||||||
<img
|
|
||||||
src=\\"https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png\\"
|
|
||||||
alt=\\":nuxt:\\"
|
|
||||||
/></picture>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > custom emoji > text 1`] = `"Daniel Roe :nuxt:"`;
|
|
||||||
|
|
||||||
exports[`html-parse > emojis > html 1`] = `
|
|
||||||
"<img
|
|
||||||
src=\\"/emojis/twemoji/1f1eb-1f1f7.svg\\"
|
|
||||||
alt=\\"🇫🇷\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
src=\\"/emojis/twemoji/1f468-200d-1f469-200d-1f466.svg\\"
|
|
||||||
alt=\\"👨👩👦\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
src=\\"/emojis/twemoji/1f469-200d-1f692.svg\\"
|
|
||||||
alt=\\"👩🚒\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/><img
|
|
||||||
src=\\"/emojis/twemoji/1f9d1-1f3fd-200d-1f680.svg\\"
|
|
||||||
alt=\\"🧑🏽🚀\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > emojis > text 1`] = `"🇫🇷 👨👩👦 👩🚒🧑🏽🚀"`;
|
|
||||||
|
|
||||||
exports[`html-parse > empty > html 1`] = `""`;
|
|
||||||
|
|
||||||
exports[`html-parse > empty > text 1`] = `""`;
|
|
||||||
|
|
||||||
exports[`html-parse > html entities > html 1`] = `
|
|
||||||
"<p>Hello <World />.</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > html entities > text 1`] = `"Hello <World />."`;
|
|
||||||
|
|
||||||
exports[`html-parse > inline markdown > html 1`] = `"<p>text <code>code</code> <b>bold</b> <em>italic</em> <del>del</del></p><p></p><p><pre><code class=\\"language-js\\">code block</code></pre></p>"`;
|
|
||||||
|
|
||||||
exports[`html-parse > inline markdown > text 1`] = `
|
|
||||||
"text \`code\` **bold** *italic* ~~del~~
|
|
||||||
|
|
||||||
|
|
||||||
\`\`\`js
|
|
||||||
code block
|
|
||||||
\`\`\`"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > link + mention > html 1`] = `
|
|
||||||
"<p>
|
|
||||||
Happy
|
|
||||||
<img
|
|
||||||
src=\\"/emojis/twemoji/1f917.svg\\"
|
|
||||||
alt=\\"🤗\\"
|
|
||||||
class=\\"iconify-emoji iconify-emoji--twemoji\\"
|
|
||||||
/>
|
|
||||||
we’re now using
|
|
||||||
<span class=\\"h-card\\"
|
|
||||||
><a
|
|
||||||
href=\\"https://webtoo.ls/@vitest\\"
|
|
||||||
class=\\"u-url mention\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
target=\\"_blank\\"
|
|
||||||
>@<span>vitest</span></a
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
(migrated from chai+mocha)
|
|
||||||
<a
|
|
||||||
href=\\"https://github.com/ayoayco/astro-reactive-library/pull/203\\"
|
|
||||||
rel=\\"nofollow noopener noreferrer\\"
|
|
||||||
target=\\"_blank\\"
|
|
||||||
><span class=\\"invisible\\">https://</span
|
|
||||||
><span class=\\"ellipsis\\">github.com/ayoayco/astro-react</span
|
|
||||||
><span class=\\"invisible\\">ive-library/pull/203</span></a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`html-parse > link + mention > text 1`] = `"Happy 🤗 we’re now using @vitest (migrated from chai+mocha) https://github.com/ayoayco/astro-reactive-library/pull/203"`;
|
|
|
@ -287,15 +287,10 @@ vi.mock('shiki-es', async (importOriginal) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
vi.mock('~/components/content/ContentMentionGroup.vue', async () => {
|
mockComponent('ContentMentionGroup', {
|
||||||
const { defineComponent, h } = await import('vue')
|
setup(props, { slots }) {
|
||||||
return {
|
return () => h('mention-group', null, { default: () => slots?.default?.() })
|
||||||
default: defineComponent({
|
},
|
||||||
setup(props, { slots }) {
|
|
||||||
return () => h('mention-group', null, { default: () => slots?.default?.() })
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mockComponent('AccountHoverWrapper', {
|
mockComponent('AccountHoverWrapper', {
|
||||||
|
|
Loading…
Reference in a new issue