Before, every GitHub call awaited indefinitely with no AbortSignal, so a "lie-fi" connection (technically online, effectively dead) left FluxNote stuck on the skeleton loader and the freshness badge spinning forever. Inject an 8s AbortSignal.timeout via octokit.hook.before (20s override on the recursive tree fetch), classify failures in the userRepo store as auth vs network, and surface a "Couldn't reach GitHub" retry button when no cached content is available.
288 lines
6.3 KiB
Vue
288 lines
6.3 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, onMounted, onUnmounted, toRefs, watch } from "vue"
|
|
|
|
import HeaderNote from "@/components/HeaderNote.vue"
|
|
import SignInGithub from "@/components/SignInGithub.vue"
|
|
import SkeletonLoader from "@/components/SkeletonLoader.vue"
|
|
import StackedNote from "@/components/StackedNote.vue"
|
|
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
|
import { useLinks } from "@/hooks/useLinks.hook"
|
|
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
|
import { useMarkdownPostRender } from "@/hooks/useMarkdownPostRender.hook"
|
|
import { useNoteView } from "@/hooks/useNoteView.hook"
|
|
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
|
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
|
import { useVisitRepo } from "@/modules/history/hooks/useVisitRepo.hook"
|
|
import CacheAllNotes from "@/modules/note/components/CacheAllNote.vue"
|
|
import { useUserRepoStore } from "@/modules/repo/store/userRepo.store"
|
|
import { useUserSettings } from "@/modules/user/hooks/useUserSettings.hook"
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
user: string
|
|
repo: string
|
|
content?: string | null
|
|
parseContent?: boolean
|
|
withContent?: boolean
|
|
withHeader?: boolean
|
|
}>(),
|
|
{
|
|
content: null,
|
|
parseContent: true,
|
|
withContent: true,
|
|
withHeader: true
|
|
}
|
|
)
|
|
|
|
const user = computed(() => props.user)
|
|
const repo = computed(() => props.repo)
|
|
|
|
const refProps = toRefs(props)
|
|
const store = useUserRepoStore()
|
|
useUserSettings()
|
|
const { visitRepo } = useVisitRepo({ user: user, repo: repo })
|
|
const { toHTML } = markdownBuilder(repo)
|
|
const { listenToClick } = useLinks("note-display")
|
|
const { stackedNotes, scrollToFocusedNote } = useRouteQueryStackedNotes()
|
|
|
|
const { titles } = useNoteView()
|
|
const { isLogged } = useGitHubLogin()
|
|
useResizeContainer("note-container", stackedNotes)
|
|
|
|
const renderedContent = computed(() =>
|
|
props.content !== null
|
|
? props.parseContent
|
|
? toHTML(props.content)
|
|
: props.content
|
|
: store.readme
|
|
)
|
|
|
|
const isLoading = computed(() => renderedContent.value === undefined)
|
|
const hasContent = computed(() => !!renderedContent.value)
|
|
|
|
useMarkdownPostRender(renderedContent, () => ".note-display", {
|
|
onReady: () => listenToClick(),
|
|
tikz: true
|
|
})
|
|
|
|
watch(
|
|
[refProps.user, refProps.repo],
|
|
() => {
|
|
store.setUserRepo(props.user, props.repo)
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
const retryLoad = () => {
|
|
store.setUserRepo(props.user, props.repo)
|
|
}
|
|
|
|
onMounted(() => visitRepo())
|
|
|
|
onUnmounted(() => {
|
|
store.resetFiles()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<main class="flux-note repo-note content note-container">
|
|
<div class="note readme">
|
|
<header-note v-if="withHeader" class="header" :user="user" :repo="repo" />
|
|
<div class="repo-title-breadcrumb">
|
|
<a
|
|
class="title-stacked-note-link"
|
|
@click.prevent="scrollToFocusedNote()"
|
|
>{{ repo }}</a
|
|
>
|
|
</div>
|
|
<div class="repo-title">
|
|
<div class="repo-header">
|
|
<h1 class="heading-1">
|
|
{{ repo }}
|
|
</h1>
|
|
{{ user }}
|
|
</div>
|
|
<cache-all-notes />
|
|
</div>
|
|
<slot />
|
|
<skeleton-loader v-if="isLoading" />
|
|
<div
|
|
v-else-if="withContent && !hasContent && store.loadError === 'network'"
|
|
class="repo-network-error"
|
|
>
|
|
<p>Couldn't reach GitHub. Check your connection.</p>
|
|
<button class="btn btn-primary" @click="retryLoad">Retry</button>
|
|
</div>
|
|
<div v-else-if="withContent && !hasContent" class="repo-not-found">
|
|
<template v-if="isLogged">
|
|
<p>This repository is not accessible.</p>
|
|
</template>
|
|
<template v-else>
|
|
<p>This repository is private. Sign in to view it.</p>
|
|
<sign-in-github />
|
|
</template>
|
|
</div>
|
|
<p
|
|
v-else-if="withContent && hasContent"
|
|
class="note-display"
|
|
v-html="renderedContent"
|
|
/>
|
|
</div>
|
|
<stacked-note
|
|
v-for="(stackedNote, index) in stackedNotes"
|
|
:key="stackedNote"
|
|
class="note"
|
|
:index="index"
|
|
:sha="stackedNote"
|
|
:user="user"
|
|
:repo="repo"
|
|
:title="titles[stackedNote]"
|
|
/>
|
|
</main>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
$header-height: 40px;
|
|
|
|
.flux-note {
|
|
display: flex;
|
|
flex: 1;
|
|
|
|
&.content {
|
|
.title,
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6,
|
|
strong {
|
|
color: var(--color-base-content);
|
|
}
|
|
|
|
table {
|
|
color: var(--color-base-content);
|
|
background-color: var(--color-base-100);
|
|
|
|
thead {
|
|
th {
|
|
color: var(--color-base-content);
|
|
}
|
|
}
|
|
}
|
|
|
|
blockquote {
|
|
background-color: var(--color-base-100);
|
|
color: var(--color-base-content);
|
|
}
|
|
}
|
|
|
|
.header {
|
|
height: $header-height;
|
|
}
|
|
|
|
.readme {
|
|
position: sticky;
|
|
left: 0;
|
|
top: 0;
|
|
padding: 0 2rem;
|
|
scrollbar-width: none;
|
|
|
|
.repo-title {
|
|
margin-top: 1rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
.title {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.title,
|
|
.subtitle {
|
|
text-align: center;
|
|
}
|
|
|
|
img {
|
|
vertical-align: middle;
|
|
}
|
|
}
|
|
}
|
|
|
|
.repo-not-found,
|
|
.repo-network-error {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
padding: 2rem 0;
|
|
color: var(--color-base-content);
|
|
}
|
|
|
|
.note-display {
|
|
padding-bottom: 2rem;
|
|
}
|
|
|
|
.note {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: auto;
|
|
|
|
.title {
|
|
text-align: left;
|
|
}
|
|
}
|
|
|
|
@media screen and (min-width: 769px) {
|
|
background-color: var(--note-canvas-bg);
|
|
|
|
.repo-title-breadcrumb {
|
|
padding: 0.5rem 1rem 0;
|
|
transform-origin: 0 0;
|
|
transform: rotate(90deg);
|
|
font-size: 0.8em;
|
|
|
|
a {
|
|
color: var(--color-base-content);
|
|
display: block;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.note {
|
|
min-width: var(--note-width);
|
|
max-width: var(--note-width);
|
|
background-color: var(--color-base-100);
|
|
}
|
|
|
|
.readme {
|
|
box-shadow: var(--note-sheet-shadow);
|
|
}
|
|
}
|
|
}
|
|
|
|
@media print, screen and (max-width: 768px) {
|
|
.flux-note {
|
|
.readme {
|
|
padding: 0 0.75rem;
|
|
position: relative;
|
|
}
|
|
}
|
|
|
|
.flux-note {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.note {
|
|
width: 100vw;
|
|
height: 100svh;
|
|
overflow-y: visible;
|
|
}
|
|
|
|
.repo-title-breadcrumb {
|
|
display: none;
|
|
visibility: hidden;
|
|
}
|
|
}
|
|
</style>
|