-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat, fix: migrate editor to /, fix file nav breaking
- Loading branch information
Showing
9 changed files
with
289 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,292 @@ | ||
<script lang="ts"> | ||
import SideBar from './nav/SideBar.svelte'; | ||
import FileNavigation from './nav/FileNavigation.svelte'; | ||
import TopBar from './nav/TopBar.svelte'; | ||
import ChangeDialogue from './ChangeDialogue.svelte'; | ||
import { renderMarkdown } from '$lib/render'; | ||
import { cache } from '$lib/cache'; | ||
import { apiAddress } from '$lib/net'; | ||
import LoadingIcon from './LoadingIcon.svelte'; | ||
import { ToastType, addToast } from '$lib/toast'; | ||
import Toasts from './Toasts.svelte'; | ||
import { currentFile } from '$lib/main'; | ||
import { get } from 'svelte/store'; | ||
import { onMount } from 'svelte'; | ||
import { dev } from '$app/environment'; | ||
/** The text currently displayed in the editing window */ | ||
let editorText = ''; | ||
/** A reference to the div where markdown is rendered to */ | ||
let previewWindow: HTMLElement; | ||
/** The width of the sidebar */ | ||
export let sidebarWidth = '14rem'; | ||
$: sidebarWidth; | ||
/** | ||
* The time in milliseconds that must pass after a keypress | ||
* before markdown is rendered | ||
*/ | ||
const DEBOUNCE_TIME: number = 500; | ||
let lastKeyPressedTime = Date.now(); | ||
/** The base directory for filesystem navigation */ | ||
let rootNode = { | ||
name: '', | ||
children: [] | ||
}; | ||
$: rootNode; | ||
onMount(async () => { | ||
const response = await fetch(`${apiAddress}/api/tree`); | ||
rootNode = await response.json(); | ||
}); | ||
/** | ||
* This function is called whenever a key is pressed. | ||
* | ||
* @see https://svelte.dev/repl/162005fa12cc4feb9f668e09260595a7?version=3.24.1 | ||
*/ | ||
async function onKeyDown() { | ||
lastKeyPressedTime = Date.now(); | ||
setTimeout(() => { | ||
if (lastKeyPressedTime + DEBOUNCE_TIME >= Date.now()) { | ||
renderMarkdown(editorText, previewWindow); | ||
} | ||
}, DEBOUNCE_TIME); | ||
} | ||
let showChangeDialogue: boolean; | ||
let showLoadingIcon: boolean; | ||
async function fileSelectionHandler(e: CustomEvent) { | ||
console.log("himom!") | ||
// If the file in cache doesn't differ from the editor or no file is selected, there are no unsaved changes | ||
if ((await cache.get(get(currentFile))) === editorText || get(currentFile) === '') { | ||
currentFile.set(e.detail.path); | ||
editorText = | ||
(await cache.get(e.detail.path)) ?? | ||
'Something went wrong, the file tree reported by the backend references a nonexistent file.'; | ||
renderMarkdown(editorText, previewWindow); | ||
} else if (e.detail.path === currentFile) { | ||
// Do nothing | ||
} else { | ||
// Unsaved changes | ||
showChangeDialogue = true; | ||
} | ||
} | ||
async function saveChangesHandler() { | ||
showLoadingIcon = true; | ||
let response = await fetch(`${apiAddress}/api/doc`, { | ||
method: 'PUT', | ||
credentials: 'include', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
contents: editorText, | ||
path: currentFile | ||
}) | ||
}); | ||
showLoadingIcon = false; | ||
switch (response.status) { | ||
case 201: | ||
// TODO: Show created message, flush cache | ||
addToast({ | ||
message: 'Changes synced successfully.', | ||
type: ToastType.Success, | ||
dismissible: true, | ||
timeout: 3000 | ||
}); | ||
break; | ||
default: | ||
addToast({ | ||
message: `An error was encountered syncing changes, please report to the developer (Code ${response.status}: "${response.statusText}").`, | ||
type: ToastType.Error, | ||
dismissible: true | ||
}); | ||
} | ||
} | ||
onMount(() => { | ||
// TODO: when /users/@me or whatever exists, redirect if the user doesn't have the manage doc permission | ||
// Check to see if the username cookie exists, it's got the same expiration time as the auth token but is visible to the frontend | ||
if (!document.cookie.includes('username')) { | ||
addToast({ | ||
message: 'You need to be logged in to access this page, redirecting...', | ||
type: ToastType.Error, | ||
dismissible: false | ||
}); | ||
setTimeout(() => { | ||
// TODO: When .html stripping middleware is complete, change this to always redirect to /login` | ||
if (dev) { | ||
window.location.href = '/login'; | ||
} else { | ||
window.location.href = '/login.html'; | ||
} | ||
}, 800); | ||
} | ||
}); | ||
</script> | ||
|
||
<h1>Welcome to SvelteKit</h1> | ||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p> | ||
<div style="--sidebar-width: {sidebarWidth}" class="container"> | ||
<Toasts /> | ||
<SideBar bind:sidebarWidth> | ||
<div class="directory-nav"> | ||
<FileNavigation on:fileselect={fileSelectionHandler} {...rootNode} /> | ||
</div> | ||
</SideBar> | ||
<div style="display: flex; flex-direction: column; height: 100vh;"> | ||
<TopBar /> | ||
<div class="editor-controls"> | ||
<!-- Cancel --> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
height="40px" | ||
viewBox="0 -960 960 960" | ||
width="40px" | ||
class="cancel" | ||
> | ||
<title>Cancel Changes</title> | ||
<path | ||
d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" | ||
/> | ||
</svg> | ||
<!-- Save --> | ||
<!-- svelte-ignore a11y-click-events-have-key-events --> | ||
<svg | ||
on:click={saveChangesHandler} | ||
role="button" | ||
tabindex="0" | ||
xmlns="http://www.w3.org/2000/svg" | ||
height="40px" | ||
viewBox="0 -960 960 960" | ||
width="40px" | ||
class="publish" | ||
> | ||
<title>Publish Changes</title> | ||
<path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z" /> | ||
</svg> | ||
</div> | ||
<div class="editor-panes"> | ||
<textarea bind:value={editorText} class="editor-pane"></textarea> | ||
<div bind:this={previewWindow} class="preview-pane"></div> | ||
</div> | ||
</div> | ||
<LoadingIcon bind:visible={showLoadingIcon} /> | ||
<ChangeDialogue bind:visible={showChangeDialogue} /> | ||
</div> | ||
|
||
<svelte:window on:keydown={onKeyDown} /> | ||
|
||
<style> | ||
.container { | ||
background-color: var(--background-0); | ||
display: flex; | ||
} | ||
.editor-controls { | ||
border-radius: 5%; | ||
padding-right: 0.5rem; | ||
margin-top: 0.2rem; | ||
border-bottom: 0.07rem solid; | ||
border-color: var(--foreground-4); | ||
} | ||
.editor-controls * { | ||
border-radius: 5%; | ||
fill: var(--foreground-4); | ||
float: right; | ||
flex-direction: vertical; | ||
margin: 0.3rem; | ||
cursor: pointer; | ||
} | ||
.publish:hover { | ||
background-color: var(--background-0); | ||
box-sizing: border-box; | ||
border: 0.2rem var(--green) solid; | ||
transition: all 0.05s ease-out; | ||
} | ||
.publish:hover > * { | ||
fill: var(--green); | ||
} | ||
.publish:active { | ||
border-radius: 5%; | ||
background-color: var(--green); | ||
} | ||
.publish:active > * { | ||
fill: var(--background-0); | ||
} | ||
.cancel:hover { | ||
background-color: var(--background-0); | ||
box-sizing: border-box; | ||
border: 0.2rem var(--red) solid; | ||
transition: all 0.05s ease-out; | ||
} | ||
.cancel:hover > * { | ||
fill: var(--red); | ||
} | ||
.cancel:active { | ||
background-color: var(--red); | ||
} | ||
.cancel:active > * { | ||
fill: var(--background-0); | ||
} | ||
/* div containing both the preview pane and the editor pane */ | ||
.editor-panes { | ||
height: 100%; | ||
overflow-y: hidden; | ||
} | ||
.editor-pane { | ||
resize: none; | ||
float: left; | ||
box-sizing: border-box; | ||
width: calc((100vw - var(--sidebar-width)) / 2); | ||
height: 98%; | ||
padding: 1rem; | ||
border: none; | ||
font-size: larger; | ||
background-color: var(--background-0); | ||
color: var(--foreground-0); | ||
} | ||
.editor-pane:focus { | ||
outline-width: 0; | ||
} | ||
.preview-pane { | ||
/* sizing and spacing */ | ||
float: left; | ||
box-sizing: border-box; | ||
width: calc((100vw - var(--sidebar-width)) / 2); | ||
height: 100%; | ||
padding-left: 1rem; | ||
padding-right: 1rem; | ||
border-left: 0.07rem solid var(--foreground-5); | ||
/* styling of rendered text */ | ||
color: var(--foreground-0); | ||
font-family: var(--font-family); | ||
overflow-y: scroll; | ||
} | ||
.preview-pane :global(*) { | ||
word-break: normal; | ||
} | ||
.preview-pane :global(a) { | ||
color: var(--foreground-0); | ||
} | ||
.preview-pane :global(img) { | ||
width: 90%; | ||
} | ||
</style> |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.