feat: add optional slug to public note URLs
This commit is contained in:
@@ -31,7 +31,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () => import("@/views/PublicNoteListByDidView.vue"),
|
component: () => import("@/views/PublicNoteListByDidView.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/pub/:did/:rkey",
|
path: "/pub/:did/:rkey/:slug?",
|
||||||
name: "PublicNoteView",
|
name: "PublicNoteView",
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/views/PublicNoteView.vue"),
|
component: () => import("@/views/PublicNoteView.vue"),
|
||||||
|
|||||||
9
src/utils/slugify.ts
Normal file
9
src/utils/slugify.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export function slugify(text: string): string {
|
||||||
|
return text
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, "-")
|
||||||
|
.replace(/-+/g, "-")
|
||||||
|
.replace(/^-|-$/g, "")
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import BackButton from "@/components/BackButton.vue"
|
import BackButton from "@/components/BackButton.vue"
|
||||||
import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook"
|
import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook"
|
||||||
import { getAuthor } from "@/modules/atproto/getAuthor"
|
import { getAuthor } from "@/modules/atproto/getAuthor"
|
||||||
|
import { slugify } from "@/utils/slugify"
|
||||||
import { computedAsync } from "@vueuse/core"
|
import { computedAsync } from "@vueuse/core"
|
||||||
import { computed } from "vue"
|
import { computed } from "vue"
|
||||||
import { vInfiniteScroll } from "@vueuse/components"
|
import { vInfiniteScroll } from "@vueuse/components"
|
||||||
@@ -31,7 +32,7 @@ const author = computedAsync(async () => getAuthor(did.value))
|
|||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name: 'PublicNoteView',
|
name: 'PublicNoteView',
|
||||||
params: { did: note.did, rkey: note.rkey },
|
params: { did: note.did, rkey: note.rkey, slug: slugify(note.title) },
|
||||||
}"
|
}"
|
||||||
class="btn btn-link"
|
class="btn btn-link"
|
||||||
>{{ note.title }}</router-link
|
>{{ note.title }}</router-link
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import BackButton from "@/components/BackButton.vue"
|
import BackButton from "@/components/BackButton.vue"
|
||||||
import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook"
|
import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook"
|
||||||
|
import { slugify } from "@/utils/slugify"
|
||||||
import { vInfiniteScroll } from "@vueuse/components"
|
import { vInfiniteScroll } from "@vueuse/components"
|
||||||
|
|
||||||
const { notes, isLoading, canLoadMore, onLoadMore, getAuthor } =
|
const { notes, isLoading, canLoadMore, onLoadMore, getAuthor } =
|
||||||
@@ -24,7 +25,7 @@ const { notes, isLoading, canLoadMore, onLoadMore, getAuthor } =
|
|||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name: 'PublicNoteView',
|
name: 'PublicNoteView',
|
||||||
params: { did: note.did, rkey: note.rkey },
|
params: { did: note.did, rkey: note.rkey, slug: slugify(note.title) },
|
||||||
}"
|
}"
|
||||||
class="btn btn-link"
|
class="btn btn-link"
|
||||||
>{{ note.title }}</router-link
|
>{{ note.title }}</router-link
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ import type { PublicNoteRecord } from "@/modules/atproto/publicNote.types"
|
|||||||
import { withATProtoImages } from "@/modules/atproto/withATProtoImages"
|
import { withATProtoImages } from "@/modules/atproto/withATProtoImages"
|
||||||
import { getUrl } from "@/modules/atproto/getUrl"
|
import { getUrl } from "@/modules/atproto/getUrl"
|
||||||
import { downloadFont } from "@/utils/downloadFont"
|
import { downloadFont } from "@/utils/downloadFont"
|
||||||
|
import { slugify } from "@/utils/slugify"
|
||||||
import { computedAsync } from "@vueuse/core"
|
import { computedAsync } from "@vueuse/core"
|
||||||
import { computed, nextTick, watch } from "vue"
|
import { computed, nextTick, watch } from "vue"
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
||||||
import ThemeSwap from "@/components/ThemeSwap.vue"
|
import ThemeSwap from "@/components/ThemeSwap.vue"
|
||||||
|
|
||||||
const props = defineProps<{ did: string; rkey: string }>()
|
const props = defineProps<{ did: string; rkey: string; slug?: string }>()
|
||||||
|
const router = useRouter()
|
||||||
const did = computed(() => props.did)
|
const did = computed(() => props.did)
|
||||||
const rkey = computed(() => props.rkey)
|
const rkey = computed(() => props.rkey)
|
||||||
|
|
||||||
@@ -31,6 +34,15 @@ const noteRecord = computedAsync(async () =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
watch(noteRecord, () => {
|
watch(noteRecord, () => {
|
||||||
|
if (
|
||||||
|
noteRecord.value?.value.title &&
|
||||||
|
props.slug &&
|
||||||
|
props.slug !== slugify(noteRecord.value.value.title)
|
||||||
|
) {
|
||||||
|
router.replace({ name: "SpaceCowboy" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (noteRecord.value?.value.fontFamily) {
|
if (noteRecord.value?.value.fontFamily) {
|
||||||
downloadFont(noteRecord.value.value.fontFamily)
|
downloadFont(noteRecord.value.value.fontFamily)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user