diff --git a/.gitignore b/.gitignore
index c4c69b12..72b234fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ dist
.idea/
.vite-inspect
.netlify/
+.eslintcache
public/shiki
public/emojis
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6c710a8c..f1855fcb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,27 +1,29 @@
{
- "prettier.enable": false,
- "editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
- },
- "files.associations": {
- "*.css": "postcss"
- },
- "editor.formatOnSave": false,
"cSpell.words": [
"masto",
"Nuxtodon",
"unmute",
"unstorage"
],
- "i18n-ally.localesPaths": [
- "locales"
- ],
- "i18n-ally.keystyle": "nested",
- "i18n-ally.sourceLanguage": "en-US",
- "i18n-ally.preferredDelimiter": "_",
- "i18n-ally.sortKeys": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": true
+ },
+ "editor.formatOnSave": false,
+ "files.associations": {
+ "*.css": "postcss"
+ },
"i18n-ally.keysInUse": [
"time_ago_options.*",
"visibility.*"
- ]
+ ],
+ "i18n-ally.keystyle": "nested",
+ "i18n-ally.localesPaths": [
+ "locales"
+ ],
+ "i18n-ally.preferredDelimiter": "_",
+ "i18n-ally.sortKeys": true,
+ "i18n-ally.sourceLanguage": "en-US",
+ "prettier.enable": false,
+ "volar.completion.preferredTagNameCase": "pascal",
+ "volar.completion.preferredAttrNameCase": "kebab"
}
diff --git a/app.vue b/app.vue
index e7e62612..838c6019 100644
--- a/app.vue
+++ b/app.vue
@@ -12,4 +12,13 @@ const key = computed(() => getUniqueUserId(currentUser.value))
+
+
+
+
+
+
+
+
+
diff --git a/components/account/AccountAvatar.vue b/components/account/AccountAvatar.vue
index 44db7eb8..e12e658e 100644
--- a/components/account/AccountAvatar.vue
+++ b/components/account/AccountAvatar.vue
@@ -3,6 +3,7 @@ import type { Account } from 'masto'
defineProps<{
account: Account
+ square?: boolean
}>()
const loaded = $ref(false)
@@ -17,8 +18,8 @@ const error = $ref(false)
:src="error ? 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' : account.avatar"
:alt="$t('account.avatar_description', [account.username])"
loading="lazy"
- rounded-full
- :class="loaded ? 'bg-base' : 'bg-gray:10'"
+ :class="(loaded ? 'bg-base' : 'bg-gray:10') + (square ? ' ' : ' rounded-full')"
+ :style="{ 'clip-path': square ? `url(#avatar-mask)` : 'none' }"
v-bind="$attrs"
@load="loaded = true"
@error="error = true"
diff --git a/components/account/AccountBigAvatar.vue b/components/account/AccountBigAvatar.vue
index ecc0b3b0..afe705de 100644
--- a/components/account/AccountBigAvatar.vue
+++ b/components/account/AccountBigAvatar.vue
@@ -6,11 +6,12 @@ import type { Account } from 'masto'
defineProps<{
account: Account
+ square?: boolean
}>()
diff --git a/components/account/AccountBigCard.vue b/components/account/AccountBigCard.vue
index fb7f852e..caaf3ba0 100644
--- a/components/account/AccountBigCard.vue
+++ b/components/account/AccountBigCard.vue
@@ -32,13 +32,7 @@ defineOptions({
diff --git a/components/account/AccountDisplayName.vue b/components/account/AccountDisplayName.vue
new file mode 100644
index 00000000..a8b8befc
--- /dev/null
+++ b/components/account/AccountDisplayName.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/components/account/AccountHeader.vue b/components/account/AccountHeader.vue
index 17c21f36..23bbd821 100644
--- a/components/account/AccountHeader.vue
+++ b/components/account/AccountHeader.vue
@@ -1,6 +1,5 @@
+
+
+
+
+
diff --git a/components/common/CommonCheckbox.vue b/components/common/CommonCheckbox.vue
index a7ebd035..11150bbc 100644
--- a/components/common/CommonCheckbox.vue
+++ b/components/common/CommonCheckbox.vue
@@ -11,11 +11,13 @@ const { modelValue } = defineModel<{
+ {{ label }}
- {{ label }}
diff --git a/components/common/CommonCropImage.vue b/components/common/CommonCropImage.vue
index adee86d6..a1eebba4 100644
--- a/components/common/CommonCropImage.vue
+++ b/components/common/CommonCropImage.vue
@@ -4,8 +4,6 @@ import { Cropper } from 'vue-advanced-cropper'
import 'vue-advanced-cropper/dist/style.css'
export interface Props {
- /** Images to be cropped */
- modelValue?: File
/** Crop frame aspect ratio (width/height), default 1/1 */
stencilAspectRatio?: number
/** The ratio of the longest edge of the cut box to the length of the cut screen, default 0.9, not more than 1 */
@@ -16,12 +14,11 @@ const props = withDefaults(defineProps(), {
stencilSizePercentage: 0.9,
})
-const emit = defineEmits<{
- (event: 'update:modelValue', value: File): void
+const { modelValue: file } = defineModel<{
+ /** Images to be cropped */
+ modelValue: File | null
}>()
-const vmFile = useVModel(props, 'modelValue', emit, { passive: true })
-
const cropperDialog = ref(false)
const cropper = ref>()
@@ -40,7 +37,7 @@ const stencilSize = ({ boundaries }: { boundaries: Boundaries }) => {
}
}
-watch(vmFile, (file, _, onCleanup) => {
+watch(file, (file, _, onCleanup) => {
let expired = false
onCleanup(() => expired = true)
@@ -59,12 +56,12 @@ watch(vmFile, (file, _, onCleanup) => {
})
const cropImage = () => {
- if (cropper.value && vmFile.value) {
+ if (cropper.value && file.value) {
cropperFlag.value = true
cropperDialog.value = false
const { canvas } = cropper.value.getResult()
canvas?.toBlob((blob) => {
- vmFile.value = new File([blob as any], `cropped${vmFile.value?.name}` as string, { type: blob?.type })
+ file.value = new File([blob as any], `cropped${file.value?.name}` as string, { type: blob?.type })
}, cropperImage.type)
}
}
diff --git a/components/common/CommonInputImage.vue b/components/common/CommonInputImage.vue
index 49a51db2..595d5786 100644
--- a/components/common/CommonInputImage.vue
+++ b/components/common/CommonInputImage.vue
@@ -3,7 +3,6 @@ import { fileOpen } from 'browser-fs-access'
import type { FileWithHandle } from 'browser-fs-access'
const props = withDefaults(defineProps<{
- modelValue?: FileWithHandle
/** The image src before change */
original?: string
/** Allowed file types */
@@ -19,12 +18,13 @@ const props = withDefaults(defineProps<{
allowedFileSize: 1024 * 1024 * 5, // 5 MB
})
const emit = defineEmits<{
- (event: 'update:modelValue', value: FileWithHandle): void
(event: 'pick', value: FileWithHandle): void
(event: 'error', code: number, message: string): void
}>()
-const file = useVModel(props, 'modelValue', emit, { passive: true })
+const { modelValue: file } = defineModel<{
+ modelValue: FileWithHandle | null
+}>()
const { t } = useI18n()
diff --git a/components/common/CommonPaginator.vue b/components/common/CommonPaginator.vue
index abb0f9f4..b739a4d5 100644
--- a/components/common/CommonPaginator.vue
+++ b/components/common/CommonPaginator.vue
@@ -2,7 +2,7 @@
// @ts-expect-error missing types
import { DynamicScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
-import type { Account, Paginator, WsEvents } from 'masto'
+import type { Paginator, WsEvents } from 'masto'
const {
paginator,
@@ -11,7 +11,6 @@ const {
virtualScroller = false,
eventType = 'update',
preprocess,
- isAccountTimeline,
} = defineProps<{
paginator: Paginator
keyProp?: string
@@ -19,7 +18,6 @@ const {
stream?: Promise
eventType?: 'notification' | 'update'
preprocess?: (items: any[]) => any[]
- isAccountTimeline?: boolean
}>()
defineSlots<{
@@ -34,18 +32,9 @@ defineSlots<{
update: () => void
}
loading: {}
+ done: {}
}>()
-let account: Account | null = null
-
-const { params } = useRoute()
-
-if (isAccountTimeline) {
- const handle = $(computedEager(() => params.account as string))
-
- account = await fetchAccountByHandle(handle)
-}
-
const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, stream, eventType, preprocess)
@@ -84,15 +73,11 @@ const { items, prevItems, update, state, endAnchor, error } = usePaginator(pagin
-
+
{{ $t('common.error') }}: {{ error }}
diff --git a/components/common/CommonRadio.vue b/components/common/CommonRadio.vue
index 05b0dd57..5f0c175a 100644
--- a/components/common/CommonRadio.vue
+++ b/components/common/CommonRadio.vue
@@ -12,9 +12,10 @@ const { modelValue } = defineModel<{
+ {{ label }}
- {{ label }}
diff --git a/components/common/CommonRouteTabs.vue b/components/common/CommonRouteTabs.vue
index a356e364..75ca06e6 100644
--- a/components/common/CommonRouteTabs.vue
+++ b/components/common/CommonRouteTabs.vue
@@ -43,9 +43,9 @@ useCommands(() => command
exact-active-class="children:(text-secondary !border-primary !op100 !text-base)"
@click="!preventScrollTop && $scrollToTop()"
>
- {{ option.display }}
+ {{ option.display }}
-
+
{{ option.display }}
diff --git a/components/common/CommonTrendingCharts.vue b/components/common/CommonTrendingCharts.vue
index 43fbf05b..b579dfee 100644
--- a/components/common/CommonTrendingCharts.vue
+++ b/components/common/CommonTrendingCharts.vue
@@ -4,8 +4,12 @@ import sparkline from '@fnando/sparkline'
const {
history,
+ width = 60,
+ height = 40,
} = $defineProps<{
history?: History[]
+ width?: number
+ height?: number
}>()
const historyNum = $computed(() => {
@@ -24,5 +28,5 @@ watch([$$(historyNum), $$(sparklineEl)], ([historyNum, sparklineEl]) => {
-
+
diff --git a/components/help/HelpPreview.vue b/components/help/HelpPreview.vue
index a664b500..b7d31232 100644
--- a/components/help/HelpPreview.vue
+++ b/components/help/HelpPreview.vue
@@ -32,7 +32,7 @@ const emit = defineEmits<{
-
+
diff --git a/components/main/MainContent.vue b/components/main/MainContent.vue
index 4dfedc73..f67c7c2e 100644
--- a/components/main/MainContent.vue
+++ b/components/main/MainContent.vue
@@ -14,11 +14,12 @@ defineProps<{
pt="[env(safe-area-inset-top,0)]"
border="b base" bg="[rgba(var(--c-bg-base-rgb),0.7)]"
>
-
+
@@ -37,6 +38,7 @@ defineProps<{
+
diff --git a/components/modal/ModalContainer.vue b/components/modal/ModalContainer.vue
index 2ef41456..31ba7cb3 100644
--- a/components/modal/ModalContainer.vue
+++ b/components/modal/ModalContainer.vue
@@ -53,6 +53,7 @@ const handlePublishClose = () => {
>
@@ -65,7 +66,7 @@ const handlePublishClose = () => {
-
+
diff --git a/components/modal/ModalDialog.vue b/components/modal/ModalDialog.vue
index c20696d2..af51f5dc 100644
--- a/components/modal/ModalDialog.vue
+++ b/components/modal/ModalDialog.vue
@@ -2,9 +2,6 @@
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
export interface Props {
- /** v-model dislog visibility */
- modelValue: boolean
-
/**
* level of depth
*
@@ -48,11 +45,13 @@ const props = withDefaults(defineProps(), {
const emit = defineEmits<{
/** v-model dialog visibility */
- (event: 'update:modelValue', value: boolean): void
(event: 'close',): void
}>()
-const visible = useVModel(props, 'modelValue', emit, { passive: true })
+const { modelValue: visible } = defineModel<{
+ /** v-model dislog visibility */
+ modelValue: boolean
+}>()
const deactivated = useDeactivated()
const route = useRoute()
diff --git a/components/modal/ModalMediaPreviewCarousel.vue b/components/modal/ModalMediaPreviewCarousel.vue
index edad636c..b2e76a5f 100644
--- a/components/modal/ModalMediaPreviewCarousel.vue
+++ b/components/modal/ModalMediaPreviewCarousel.vue
@@ -3,19 +3,20 @@ import { SwipeDirection } from '@vueuse/core'
import { useReducedMotion } from '@vueuse/motion'
import type { Attachment } from 'masto'
-const props = withDefaults(defineProps<{ media: Attachment[]; threshold?: number; modelValue: number }>(), {
- media: [] as any,
- threshold: 20,
- modelValue: 0,
-})
+const { media = [], threshold = 20 } = defineProps<{
+ media?: Attachment[]
+ threshold?: number
+}>()
const emit = defineEmits<{
- (e: 'update:modelValue', v: boolean): void
(event: 'close'): void
}>()
+const { modelValue } = defineModel<{
+ modelValue: number
+}>()
+
const target = ref()
-const index = useVModel(props, 'modelValue', emit)
const animateTimeout = useTimeout(10)
const reduceMotion = useReducedMotion()
@@ -28,15 +29,15 @@ const { isSwiping, lengthX, lengthY, direction } = useSwipe(target, {
passive: false,
onSwipeEnd(e, direction) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
- if (direction === SwipeDirection.RIGHT && Math.abs(distanceX.value) > props.threshold)
- index.value = Math.max(0, index.value - 1)
+ if (direction === SwipeDirection.RIGHT && Math.abs(distanceX.value) > threshold)
+ modelValue.value = Math.max(0, modelValue.value - 1)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
- if (direction === SwipeDirection.LEFT && Math.abs(distanceX.value) > props.threshold)
- index.value = Math.min(props.media.length - 1, index.value + 1)
+ if (direction === SwipeDirection.LEFT && Math.abs(distanceX.value) > threshold)
+ modelValue.value = Math.min(media.length - 1, modelValue.value + 1)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
- if (direction === SwipeDirection.UP && Math.abs(distanceY.value) > props.threshold)
+ if (direction === SwipeDirection.UP && Math.abs(distanceY.value) > threshold)
emit('close')
},
})
@@ -46,9 +47,9 @@ const distanceX = computed(() => {
return 0
if (!isSwiping.value || (direction.value !== SwipeDirection.LEFT && direction.value !== SwipeDirection.RIGHT))
- return index.value * 100 * -1
+ return modelValue.value * 100 * -1
- return (lengthX.value / width.value) * 100 * -1 + (index.value * 100) * -1
+ return (lengthX.value / width.value) * 100 * -1 + (modelValue.value * 100) * -1
})
const distanceY = computed(() => {
diff --git a/components/nav/NavBottom.vue b/components/nav/NavBottom.vue
index 24b4543a..8bf68cf5 100644
--- a/components/nav/NavBottom.vue
+++ b/components/nav/NavBottom.vue
@@ -38,12 +38,12 @@ const moreMenuVisible = ref(false)
-
+
-
+
diff --git a/components/nav/NavBottomMoreMenu.vue b/components/nav/NavBottomMoreMenu.vue
index 6dd117c9..45799cbc 100644
--- a/components/nav/NavBottomMoreMenu.vue
+++ b/components/nav/NavBottomMoreMenu.vue
@@ -1,24 +1,20 @@
-
+
+
+
+
+
+
@@ -18,16 +23,17 @@ const { notifications } = useNotifications()
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/components/nav/NavSideItem.vue b/components/nav/NavSideItem.vue
index ed6a0a93..c5eb9a03 100644
--- a/components/nav/NavSideItem.vue
+++ b/components/nav/NavSideItem.vue
@@ -30,13 +30,11 @@ useCommand({
let activeClass = $ref('text-primary')
onMastoInit(async () => {
- if (!props.userOnly) {
- // TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
- // we don't have currentServer defined until later
- activeClass = ''
- await nextTick()
- activeClass = 'text-primary'
- }
+ // TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
+ // we don't have currentServer defined until later
+ activeClass = ''
+ await nextTick()
+ activeClass = 'text-primary'
})
// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
@@ -58,11 +56,11 @@ const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly &
diff --git a/components/nav/NavTitle.vue b/components/nav/NavTitle.vue
index f4f9524c..6a75f50d 100644
--- a/components/nav/NavTitle.vue
+++ b/components/nav/NavTitle.vue
@@ -6,19 +6,27 @@ const { env } = buildInfo
-
-
-
- {{ $t('app_name') }}
{{ env === 'release' ? 'alpha' : env }}
+
+
+
+
+ {{ $t('app_name') }} {{ env === 'release' ? 'alpha' : env }}
+
+
+
-
+
diff --git a/components/nav/NavUser.vue b/components/nav/NavUser.vue
index 76353153..7f31797f 100644
--- a/components/nav/NavUser.vue
+++ b/components/nav/NavUser.vue
@@ -8,6 +8,7 @@
h-8
w-8
:draggable="false"
+ square
/>
G
@@ -18,7 +19,7 @@
-
+
{{ $t('action.sign_in') }}
diff --git a/components/notification/NotificationCard.vue b/components/notification/NotificationCard.vue
index 00f3e815..1c5d540e 100644
--- a/components/notification/NotificationCard.vue
+++ b/components/notification/NotificationCard.vue
@@ -18,11 +18,7 @@ const { notification } = defineProps<{
:lang="notification.status?.language ?? undefined"
>
-
+
{{ $t('notification.followed_you') }}
@@ -36,10 +32,9 @@ const { notification } = defineProps<{
-
{{ $t("notification.signed_up") }}
diff --git a/components/notification/NotificationEnablePushNotification.client.vue b/components/notification/NotificationEnablePushNotification.client.vue
index 097957f3..188e8d89 100644
--- a/components/notification/NotificationEnablePushNotification.client.vue
+++ b/components/notification/NotificationEnablePushNotification.client.vue
@@ -1,7 +1,6 @@
-