feat(notes): share an always-latest link, not just a snapshot
Some checks failed
CI / verify (push) Has been cancelled

The Share button now offers two links: the pinned snapshot (default) and
an "always latest" link that carries note paths in `?liveNotes` instead of
blob shas. On open they resolve to the current shas against the HEAD file
list and the URL is rewritten to the ordinary pinned form, so freshness,
editing and the snapshot banner keep working unchanged.
This commit is contained in:
Julien Calixte
2026-07-11 11:11:51 +02:00
parent 70fbe5156f
commit a88d0dbac7
6 changed files with 325 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
import { watch } from "vue"
import { useRoute, useRouter } from "vue-router"
import { resolveLivePathsToShas } from "@/modules/note/liveNotes"
import { useUserRepoStore } from "@/modules/repo/store/userRepo.store"
// A living link (`?liveNotes=path&liveNotes=path`) carries file paths instead of
// pinned blob shas. On open we resolve each path to the latest sha against the
// HEAD file list and rewrite the URL to the ordinary pinned `stackedNotes` form
// — so the recipient lands on the current version, and from there everything
// (freshness, editing, the snapshot banner) behaves exactly as a shared snapshot
// would. The rewrite is a `replace`, so no history entry and no view transition.
export const useResolveLiveNotes = (onResolved?: () => void) => {
const route = useRoute()
const router = useRouter()
const store = useUserRepoStore()
let resolved = false
const tryResolve = () => {
if (resolved) return
const raw = route.query.liveNotes
if (!raw) {
resolved = true
return
}
// Wait for the HEAD file list before resolving paths to shas.
if (store.files.length === 0) return
resolved = true
const entries = (Array.isArray(raw) ? raw : [raw]).filter(
(entry): entry is string => typeof entry === "string"
)
const shas = resolveLivePathsToShas(entries, store.files)
const query = { ...route.query }
delete query.liveNotes
if (shas.length) {
query.stackedNotes = shas
} else {
delete query.stackedNotes
}
router.replace({ path: route.path, query }).then(() => onResolved?.())
}
watch(() => store.files.length, tryResolve, { immediate: true })
}