Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add skeleton/loading components #90

Merged
merged 1 commit into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ yarn-error.log
stats.json
globalConfig.json
debug.log
/scripts/cypress/
/node_modules
/lib
/functions
Expand All @@ -18,6 +19,6 @@ debug.log
.env
*/*.js
*.map
!/dist/css/simple-comment-style.css
!/docs/**/*.js
!/dist/css/simple-comment-style.css
!/cypress/**/*.js
1 change: 1 addition & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default defineConfig({
},
baseUrl: "http://localhost:7070",
excludeSpecPattern: "**/examples/*.spec.js",
experimentalRunAllSpecs: true,
},

component: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"lint": "eslint .",
"posttest": "rimraf ./globalConfig.json",
"prebuild": "rimraf ./functions && rimraf ./dist/js && mkdir -p ./dist/js",
"start:db": "sudo systemctl start mongod",
"start:backend": "netlify dev --port 7070",
"start:frontend": "webpack-dev-server --config ./webpack.frontend.js",
"start": "NODE_ENV=\"development\" NODE_OPTIONS=\"--max-old-space-size=4096\" netlify dev --port 7070 --command \"yarn run start:frontend\"",
Expand Down
13 changes: 10 additions & 3 deletions src/components/CommentDisplay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,17 @@
>
{#if comment.dateDeleted}
<header class="comment-header">
<div class="user-avatar" />
<div class="user-avatar">
<div class="gray-block" />
</div>
<div class="comment-info">
<p class="user-name" />
<p class="comment-date" />
<div class="user-name">
<div class="gray-block" />
<div class="gray-block" />
</div>
<div class="comment-date">
<div class="gray-block" />
</div>
</div>
</header>
<article class="comment-body">
Expand Down
43 changes: 35 additions & 8 deletions src/components/CommentInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
ServerResponse,
User,
} from "../lib/simple-comment-types"
import Login from "./Login.svelte"
import SkeletonCommentInput from "./low-level/SkeletonCommentInput.svelte"
import { StateValue } from "xstate"
import { commentPostMachine } from "../lib/commentPost.xstate"
import { createEventDispatcher, onDestroy } from "svelte"
import { isResponseOk } from "../frontend-utilities"
import { createEventDispatcher, onDestroy, onMount } from "svelte"
import { dispatchableStore, loginStateStore } from "../lib/svelte-stores"
import { isResponseOk } from "../frontend-utilities"
import { postComment } from "../apiClient"
import { useMachine } from "@xstate/svelte"
import Login from "./Login.svelte"
export let currentUser: User | undefined
export let commentId: CommentId
export let onCancel = null
Expand All @@ -20,6 +22,9 @@

let commentText = ""
let loginStateValue
let textareaRef
let textAreaWidth = "100%"
let textAreaHeight = "7rem"

const { state, send } = useMachine(commentPostMachine)
const dispatch = createEventDispatcher()
Expand Down Expand Up @@ -119,6 +124,20 @@
send({ type: "RESET" })
}

const resizeObserver = new ResizeObserver(([textArea]) => {
if (isProcessing) return
const { inlineSize, blockSize } = textArea.borderBoxSize[0] ?? {
inlineSize: "100%",
blockSize: "7rem",
}
textAreaWidth = `${inlineSize}px`
textAreaHeight = `${blockSize}px`
})

onMount(() => {
resizeObserver.observe(textareaRef)
})

onDestroy(() => {
unsubscribeLoginState()
})
Expand All @@ -137,20 +156,28 @@
if ($state.value === stateValue) stateHandler()
})
}

$: isProcessing = (
["validating", "loggingIn", "posting", "deleting"] as StateValue[]
).includes($state.value)
</script>

<form class="comment-form" on:submit={onSubmit}>
<SkeletonCommentInput
width={textAreaWidth}
height={textAreaHeight}
isHidden={!isProcessing}
/>
<form class="comment-form" class:is-hidden={isProcessing} on:submit={onSubmit}>
<!-- svelte-ignore a11y-autofocus -->
<textarea
class="comment-field"
{placeholder}
bind:this={textareaRef}
bind:value={commentText}
required
{autofocus}
bind:value={commentText}
{placeholder}
/>
<!-- {#if !currentUser} -->
<Login {currentUser} />
<!-- {/if} -->
<div class="button-row">
{#if onCancel !== null}
<button class="comment-cancel-button" on:click={onCancel}>Cancel</button>
Expand Down
10 changes: 8 additions & 2 deletions src/components/DiscussionDisplay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { discussionMachine } from "../lib/discussion.xstate"
import { isResponseOk, threadComments } from "../frontend-utilities"
import CommentInput from "./CommentInput.svelte"
import SkeletonComment from "./low-level/SkeletonComment.svelte"

export let discussionId: string
export let title: string
Expand Down Expand Up @@ -115,7 +116,7 @@
updateStatusDisplay("loaded")
}

// Update the single source of truth
// Update the single source of truth without roundtrip to the server
const onCommentPosted = commentPostedEvent => {
const { comment } = commentPostedEvent.detail
repliesFlatArray = [{ ...comment, new: true }, ...repliesFlatArray]
Expand All @@ -131,7 +132,8 @@
event.preventDefault()
showReply = discussionId
}
// Update the single source of truth

// Update the single source of truth without roundtrip to the server
const onCommentDeleted = commentDeletedEvent => {
const { commentId } = commentDeletedEvent.detail
const comment = repliesFlatArray.find(comment => comment.id === commentId)
Expand Down Expand Up @@ -198,4 +200,8 @@
on:posted={onCommentPosted}
/>
{/if}
{#if $state.value === "loading"}
<SkeletonComment />
<div style="margin-bottom:10rem" />
{/if}
</section>
83 changes: 58 additions & 25 deletions src/components/SelfDisplay.svelte
Original file line number Diff line number Diff line change
@@ -1,44 +1,77 @@
<script lang="ts">
import { idIconDataUrl } from "../frontend-utilities"
import SkeletonText from "./low-level/SkeletonText.svelte"
import type { User } from "../lib/simple-comment-types"
import { dispatchableStore, loginStateStore } from "../lib/svelte-stores"
import { fade } from "svelte/transition"
import { idIconDataUrl } from "../frontend-utilities"

export let currentUser: User | undefined = undefined

export let currentUser: User | undefined
let loginStateValue
let loginStateNextEvents
let isProcessing = true

loginStateStore.subscribe(state => {
const { value, nextEvents } = state
loginStateValue = value
loginStateNextEvents = nextEvents
})

$: {
isProcessing =
loginStateValue === "verifying" ||
loginStateValue === "loggingIn" ||
loginStateValue === "loggingOut"
}
const onLogoutClick = (e: Event) => {
e.preventDefault()
dispatchableStore.dispatch("logoutIntent")
}
</script>

{#if loginStateValue === "verifying" || loginStateValue === "loggingIn" || loginStateValue === "loggingOut"}
<section class="self-display">
<div class="self-avatar skeleton" />
<div class="self-info skeleton" />
</section>
{:else if currentUser}
<section class="self-display" id="self-display">
<div class="self-avatar">
<img src={idIconDataUrl(currentUser.id)} alt="" />
</div>
<div class="self-info">
<h2 id="self-user-name">{currentUser.name}</h2>
<p id="self-name">
@{currentUser.id}
{currentUser.isAdmin ? "(admin)" : ""}
</p>
<p id="self-email">{currentUser.email}</p>
</div>
{#if loginStateNextEvents.includes("LOGOUT")}
<button id="log-out-button" on:click={onLogoutClick}>Log out</button>
{/if}
</section>
{/if}
<div class="self-display-container">
{#if isProcessing}
<section class="skeleton self-display" transition:fade>
<div class="self-avatar skeleton"><SkeletonText avatar /></div>
<div class="self-info">
<h2><SkeletonText width="30%" height="1.2rem" /></h2>
<p><SkeletonText width="50%" /></p>
<p><SkeletonText width="45%" /></p>
</div>
<div><SkeletonText width="7rem" height="2rem" /></div>
</section>
{/if}

{#if currentUser}
<section class="self-display" id="self-display">
<div class="self-avatar">
<img src={idIconDataUrl(currentUser.id)} alt="" />
</div>
<div class="self-info">
<h2 id="self-user-name">{currentUser.name}</h2>
<p id="self-name">
@{currentUser.id}
{currentUser.isAdmin ? "(admin)" : ""}
</p>
<p id="self-email">{currentUser.email}</p>
</div>
{#if loginStateNextEvents.includes("LOGOUT")}
<button id="log-out-button" on:click={onLogoutClick}>Log out</button>
{/if}
</section>
{/if}
</div>

<style lang="scss">
.self-display-container {
position: relative;
width: 100%;
display: block;
height: 10rem;

.self-display.skeleton {
position: absolute;
width: 100%;
}
}
</style>
57 changes: 57 additions & 0 deletions src/components/low-level/SkeletonComment.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import SkeletonText from "./SkeletonText.svelte"
export let isHidden = false
</script>

<div class="skeleton-comment" class:is-hidden={isHidden}>
<header class="comment-header">
<div class="user-avatar"><SkeletonText avatar /></div>
<div class="comment-info">
<p class="user-name"><SkeletonText width="8rem" /></p>
<p class="comment-date"><SkeletonText width="12rem" /></p>
</div>
</header>
<article class="comment-body">
<SkeletonText paragraph />
</article>
</div>

<style lang="scss">
.comment-header {
align-items: center;
display: flex;
flex-direction: row;
margin-bottom: 0.25rem;
min-height: unset;

p {
margin: 0;
padding: 0;
}

.user-avatar {
flex: 0 0 3rem;
margin-right: 0.9rem;
display: inline-flex;
max-width: 2.5rem;
border-radius: 50%;
overflow: hidden;
}

.comment-info {
display: flex;
flex-direction: row;
justify-content: space-between;
width: calc(100% - 3rem - 1.3rem);

.user-name {
margin-right: 1rem;
}
}
}

.comment-body {
margin-left: 0.5rem;
padding-left: 2rem;
}
</style>
11 changes: 11 additions & 0 deletions src/components/low-level/SkeletonCommentInput.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import SkeletonText from "./SkeletonText.svelte"
export let isHidden = false
export let width = "100%"
export let height = "7rem"
</script>

<div class="skeleton-comment-input" class:is-hidden={isHidden}>
<SkeletonText {width} {height} />
<div class="button-row"><SkeletonText width="10rem" height="2rem" /></div>
</div>
Loading