progress for file update inapp
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
49
src/hooks/useGitHubUpdate.hook.ts
Normal file
49
src/hooks/useGitHubUpdate.hook.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user