progress for file update inapp

This commit is contained in:
Julien Calixte
2023-08-20 23:55:25 +02:00
parent 767e238848
commit ccb486a0b6
6 changed files with 118 additions and 24 deletions

View File

@@ -2,7 +2,7 @@
const GITHUB_URL = 'https://github.com/login/oauth/authorize' const GITHUB_URL = 'https://github.com/login/oauth/authorize'
const CLIENT_ID = 'Iv1.12dc43d013ce3623' const CLIENT_ID = 'Iv1.12dc43d013ce3623'
const SCOPE = 'repo' const SCOPE = 'repo%20workflow'
const REDIRECT_URI = window.location.origin const REDIRECT_URI = window.location.origin
const url = new URL(GITHUB_URL) const url = new URL(GITHUB_URL)

View File

@@ -1,7 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue' import {
computed,
defineAsyncComponent,
nextTick,
onMounted,
ref,
watch
} from 'vue'
import { useFile } from '@/hooks/useFile.hook' import { useFile } from '@/hooks/useFile.hook'
import { useGitHubUpdate } from '@/hooks/useGitHubUpdate.hook'
import { useImages } from '@/hooks/useImages.hook' import { useImages } from '@/hooks/useImages.hook'
import { useLinks } from '@/hooks/useLinks.hook' import { useLinks } from '@/hooks/useLinks.hook'
import { useNoteOverlay } from '@/hooks/useNoteOverlay.hook' import { useNoteOverlay } from '@/hooks/useNoteOverlay.hook'
@@ -27,13 +35,15 @@ const props = defineProps<{
sha: string sha: string
}>() }>()
const user = computed(() => props.user)
const repo = computed(() => props.repo)
const sha = computed(() => props.sha) const sha = computed(() => props.sha)
const index = computed(() => props.index) const index = computed(() => props.index)
const repo = computed(() => props.repo)
const { scrollToFocusedNote } = useRouteQueryStackedNotes() const { scrollToFocusedNote } = useRouteQueryStackedNotes()
const { content, rawContent } = useFile(sha) const { path, content, rawContent, getRawContent } = useFile(sha)
const initialRawContent = ref<string | null>(null)
const className = computed(() => `stacked-note-${props.index}`) const className = computed(() => `stacked-note-${props.index}`)
const { listenToClick } = useLinks(className.value, sha) const { listenToClick } = useLinks(className.value, sha)
const titleClassName = computed(() => `title-${className.value}`) const titleClassName = computed(() => `title-${className.value}`)
@@ -45,11 +55,21 @@ const hasBacklinks = computed(() => store.userSettings?.backlink)
const { displayNoteOverlay } = useNoteOverlay(className.value, index) const { displayNoteOverlay } = useNoteOverlay(className.value, index)
const displayedTitle = computed(() => filenameToNoteTitle(props.title)) const displayedTitle = computed(() => filenameToNoteTitle(props.title))
const { updateFile } = useGitHubUpdate({
user: user.value,
repo: repo.value,
sha: sha.value
})
const mode = ref<'read' | 'edit'>('read') const mode = ref<'read' | 'edit'>('read')
const toggleMode = () => { const toggleMode = () => {
mode.value = mode.value === 'read' ? 'edit' : 'read' mode.value = mode.value === 'read' ? 'edit' : 'read'
} }
onMounted(async () => {
initialRawContent.value = await getRawContent()
})
watch([content, mode], () => { watch([content, mode], () => {
if (!content.value) { if (!content.value) {
return return
@@ -61,6 +81,12 @@ watch([content, mode], () => {
generateTweets() generateTweets()
}) })
}) })
watch(mode, (newMode) => {
if (newMode === 'read' && rawContent.value !== initialRawContent.value) {
updateFile({ content: rawContent.value, path: path.value })
}
})
</script> </script>
<template> <template>
@@ -96,7 +122,7 @@ watch([content, mode], () => {
<img src="@/assets/icons/share.svg" alt="share" /> <img src="@/assets/icons/share.svg" alt="share" />
</router-link> </router-link>
<div v-if="mode === 'edit'" class="edit"> <div v-if="mode === 'edit'" class="edit">
<edit-note :sha="sha" :initial-content="rawContent" /> <edit-note v-model="rawContent" />
</div> </div>
<div v-if="mode === 'read'" class="note-content" v-html="content"></div> <div v-if="mode === 'read'" class="note-content" v-html="content"></div>
</section> </section>

View File

@@ -1,4 +1,4 @@
import { Ref, ref, toValue } from 'vue' import { computed, Ref, ref, toValue } from 'vue'
import { useMarkdown } from '@/hooks/useMarkdown.hook' import { useMarkdown } from '@/hooks/useMarkdown.hook'
import { prepareNoteCache } from '@/modules/note/cache/prepareNoteCache' import { prepareNoteCache } from '@/modules/note/cache/prepareNoteCache'
@@ -6,10 +6,16 @@ import { getFileContent } from '@/modules/repo/services/repo'
import { useUserRepoStore } from '@/modules/repo/store/userRepo.store' import { useUserRepoStore } from '@/modules/repo/store/userRepo.store'
export const useFile = (sha: Ref<string> | string, retrieveContent = true) => { export const useFile = (sha: Ref<string> | string, retrieveContent = true) => {
const store = useUserRepoStore()
const path = computed(() => {
const file = store.files.find((file) => file.sha === toValue(sha))
return file?.path ?? ''
})
const { render, getRawContent: getRawContentFromFile } = useMarkdown( const { render, getRawContent: getRawContentFromFile } = useMarkdown(
toValue(sha) toValue(sha)
) )
const store = useUserRepoStore()
const { getCachedNote, saveCacheNote } = prepareNoteCache(toValue(sha)) const { getCachedNote, saveCacheNote } = prepareNoteCache(toValue(sha))
const fromCache = ref(false) const fromCache = ref(false)
@@ -41,28 +47,37 @@ export const useFile = (sha: Ref<string> | string, retrieveContent = true) => {
const getRawContent = async () => { const getRawContent = async () => {
const fileContent = await getCachedFileContent() const fileContent = await getCachedFileContent()
if (!fileContent) { if (!fileContent) {
return return null
} }
rawContent.value = getRawContentFromFile(fileContent)
return rawContent.value return getRawContentFromFile(fileContent)
} }
const getContent = async () => { const getContent = async () => {
const fileContent = await getCachedFileContent() const fileContent = await getCachedFileContent()
if (!fileContent) { if (!fileContent) {
return return null
} }
content.value = render(fileContent)
return content.value return render(fileContent)
} }
if (retrieveContent) { if (retrieveContent) {
getContent() getCachedFileContent().then((fileContent) => {
getRawContent() if (!fileContent) {
return
}
rawContent.value = getRawContentFromFile(fileContent)
content.value = render(fileContent)
})
} }
return { return {
path,
content, content,
rawContent, rawContent,
getRawContent, getRawContent,

View File

@@ -0,0 +1,49 @@
import { getOctokit } from '@/modules/repo/services/octo'
import { encodeUTF8ToBase64 } from '@/utils/decodeBase64ToUTF8'
import { confirmMessage, errorMessage } from '@/utils/notif'
export const useGitHubUpdate = ({
user,
repo,
sha
}: {
user: string
repo: string
sha: string
}) => {
const updateFile = async ({
content,
path
}: {
content: string
path: string
}) => {
try {
const octokit = await getOctokit()
const response = await octokit.request(
`PUT /repos/{owner}/{repo}/contents/{path}`,
{
owner: user,
repo,
path,
message: `Updating ${path} from Lite Note`,
content: encodeUTF8ToBase64(content),
sha
}
)
confirmMessage('file saved on GitHub')
return response?.data.commit.sha ?? null
} catch (error) {
errorMessage('File could not be saved')
}
return null
}
return {
updateFile
}
}

View File

@@ -1,24 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' defineProps<{
modelValue: string
}>()
const props = defineProps<{ sha: string; initialContent: string }>() const emits = defineEmits<{
const content = ref('') (event: 'update:modelValue', payload: string): void
}>()
onMounted(() => {
content.value = props.initialContent
})
const handleInput = (input: string) => { const handleInput = (input: string) => {
content.value = input emits('update:modelValue', input)
} }
</script> </script>
<template> <template>
<div> <div>
<pre <pre
v-once
contenteditable contenteditable
@input="(e) => handleInput((e.target as any)?.innerText ?? '')" @input="(e) => handleInput((e.target as any)?.innerText ?? '')"
>{{ initialContent }}</pre >{{ modelValue }}</pre
> >
</div> </div>
</template> </template>

View File

@@ -6,3 +6,7 @@ export const decodeBase64ToUTF8 = (content: string): string => {
.join('') .join('')
) )
} }
export const encodeUTF8ToBase64 = (content: string): string => {
const utf8Bytes: Uint8Array = new TextEncoder().encode(content)
return btoa(String.fromCharCode(...utf8Bytes))
}