Skip to content

Commit

Permalink
Add /icebreakers path to integrate with Icebreakers (#123)
Browse files Browse the repository at this point in the history
* Add topic path to local dev server

* Add topic page

* Fix threadComments when allComments is undefined

* Update layout

* Fix the create topic flow

* Change /topic to /icebreakers

* Add 'cancel' and 'load...' functions

* Add Icebreakers styling

* Add icebreakers functionality
  • Loading branch information
rendall authored Nov 17, 2023
1 parent 3cbdcb1 commit dd362e3
Show file tree
Hide file tree
Showing 10 changed files with 420 additions and 26 deletions.
31 changes: 23 additions & 8 deletions src/components/DiscussionDisplay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
export let discussionId: string
export let title: string = ""
export let currentUser: User | undefined
const emptyTopicMessages = [
"Looks like this topic is waiting for its first thoughts. Be the pioneer and start the conversation!",
"This space is all yours! Kick off the discussion with your insights.",
"No comments yet. Share your perspective and spark the dialogue!",
"It's quiet here... Why not break the ice with your viewpoint?",
"A fresh topic awaits your input. Lead the way with your comment!",
]
let repliesFlatArray: (Comment & { isNew?: true })[] = []
let discussion: Discussion = {
Expand Down Expand Up @@ -49,9 +56,9 @@
const updateDiscussionDisplay = (
topic: Discussion,
topicReplies: Comment[]
topicReplies: Comment[] | undefined
) => {
repliesFlatArray = topicReplies
repliesFlatArray = topicReplies ?? []
discussion = threadComments(
topic,
topicReplies,
Expand All @@ -76,12 +83,13 @@
const { status, statusText, ok, body } = error as ServerResponse
if (ok) console.warn("Error handler caught an OK response", error)
if (
status === 404 &&
statusText === "Not Found" &&
body.startsWith("Topic")
) {
send("CREATE")
const isUnknownTopic =
status === 404 && statusText === "Not Found" && body.startsWith("Topic")
if (isUnknownTopic) {
setTimeout(() => {
send("CREATE")
}, 1)
return
}
Expand Down Expand Up @@ -183,6 +191,13 @@
</script>

<section class="simple-comment-discussion">
{#if !discussion?.replies}
<p class="instructions">
{emptyTopicMessages[
Math.floor(Math.random() * emptyTopicMessages.length)
]}
</p>
{/if}
{#if showReply === discussionId}
<CommentInput
commentId={discussionId}
Expand Down
3 changes: 2 additions & 1 deletion src/frontend-utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ type MinComment = {
*/
export const threadComments = <T extends MinComment, U extends MinComment>(
comment: T,
allComments: U[],
allComments: U[] | undefined,
sort: (a: U, b: U) => number = (a, b) => (b.id < a.id ? 0 : 1)
): T => {
if (!allComments) return { ...comment, replies: [] }
// Make this robust but warn
allComments = allComments.filter(reply => {
if (reply && reply.id) return true
Expand Down
2 changes: 1 addition & 1 deletion src/lib/discussion.xstate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const discussionMachine = createMachine<
DiscussionTypestate
>(
{
/** @xstate-layout N4IgpgJg5mDOIC5QQJawMYFdaxQewDsBaAMwBs8B3AOhQjLAGIBtABgF1FQAHPXAF3wEuIAB6IiAFgCcADmrSA7AFZFrSZNbTWi6coA0IAJ6IAjK1PVTANknKZagEzKlpgMwBfD4dQZsuQlIKGgoAQ1QCKEYAZQBVAGF4gFFo6LZOJBBeASERcQQiN1N5a0dbFxtpW2s3QxMER0US61ZZc2trPWLZRS8fNCwcISCqajCIqKSAJSmAeSn0kWyUQUI8iTdG6kVHWWULN1kepVrjREli6kk3SV1lTpt3U2U+kF9BgOJyUfQAJzBQoJIjEEslUotMstVsJMvlpJpqI5WG5WEjlLJHG4OpI6mZJI4FNIiXoUS5JEdrK93v5ht8aH8AUDJjN5hCeHwVrlYYh4axEcjUax0ZjsbiGrJpNR7o4ZG45Bcdo4qQMaYE6dQGYDICwOEsOdD1gVHGUpbdNJ09tcyWLTETqBLbmpZJ19sbZMq-EM1cExnhwtr4lMkgBBAAqSTZWX1XNA+Q0fJ02hRdnschlYqKBJ66k2jmKlVMHo+tJ9YF+vzwv0YQdDUwAmpGoTGxGZTIpEYpnnJpOZzLJWmL4dRrDY9hjTM9bPii6qvqXy5XGIGQ+HG9G1tyEOYdMOepo3PYdodTvVMZLWKUZLINLID4peq8CHgIHARNSvXOqHqchvYxJ7m41BuFieiKCOMhaDiZwFKUUoWLaeyND2SKUt4bwqh+Iw0HQDDfpyv4tgUyjPEBIFYmByJtKYYoyoSRysOoxqtA4M6Yeq4woJEeEGpuRDEcopEumBpgQfCNrSIBqLoioEEKqh-Sep8WEav8gKcVA3HNvkbRZtc1iqNojg9sBGYWAo1h7hKyL8SorFKeqmr8JAmkEfkE4XIiV63kUF6KIcNotMOSg9DKrTKAe8J2SWozjM5kLrjCf4FBo2wouo5pCsUEkGNBKKWFUllJjZUXeqMZYVr8LmJYRImAX2HQ6BZejSGKdgEneNSPK0VmeF4HhAA */
/** @xstate-layout N4IgpgJg5mDOIC5QQJawMYFdaxQewDsBaAMwBs8B3AOhQjLAGIBtABgF1FQAHPXAF3wEuIAB6IALAE4AHNQkB2AIwTWKpawUBWCToA0IAJ6IATKwDM1TUq0yAbOYkmtCu7IC+7g6gzZchUgoaCgBDVAIoRgBlAFUAYTiAUSiotk4kEF4BIRFxBGk5RRU1CQ1tXQkDYwQTaWoZVxMZEyVzOzclGXNPbzQsHCFAqmpQ8MjEgCUJgHkJtJEslEFCXMQFR3qW83N7CS67bSrEGwVqFy0Tc0uGuxUpOx6QH37-YnJh9AAnMBDBCOj4kkUvMMotlsIMnkTJdqCYpAopI4FHDdBolEcEOZ1vUpKw8dCruZWDIpI9nn5Bu8aF8fn9xlNZiCeHwljlIZItEpYV0SeYVBIsQiMbpTiTrIpbHZat0vE8+hSAlTqDTfpAWBwFizwat8rJ5MpVOpNDp9EZEOZOdQ7HjlBc7IoZKwpBIyfKBoqgiM8GE1QAZaYAQQAIkzMlq2aA8gV9cUjeVTdU4VIrTJUyoTMo1LdXb53W9PWBPp88J9GBNEgAVCYATVDYIjYkQMi0WmoUiUiKKOxMDkqZsxrBMbZaEntWjxTk0OZelILRZLjDi5YDFcSdfDK3ZCFxooUqjxWjsDQRWgx22o63tLWbUlvzqUnllBDwEDgInJeaGlE12U3kcQRB6koCItPcUrKPadgYkQWiWJK458k0Ap4lIWjTgq+bDHQDA-qyf6NpiyjyFIBLrGBNgmBiZhcsi7bIessGqA8sofq8X5emEKARLh2pble8hKAcZgZvaFpSFR8JnGojilDe9wPixbpsUqKp0jxDZ5DBrDUMBJEdu0GaCaOwpStQOx7g47Ydm07ToZ+KnfKqEDqfheRYnYxHXrYCJgQm5rjrCthNESFpqLeMq9LmymeqMkAuRC-6EVy0ikeY5GwRJNFHgc0gSHlzoyHZ0XDIWxafPFOrwh5uIkmYDQ6PlZ54rC2yocFpSsIej7uEAA */
id: "discussion-flow",
initial: "idle",
context: {},
Expand Down
26 changes: 26 additions & 0 deletions src/scss/simple-comment-style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -563,3 +563,29 @@ $highlight-text-color: color.scale(
background-color: inherit;
}
}

.icebreakers {
header {
max-width: 62rem;
align-items: flex-start;
margin: 3rem auto;

.headlines {
margin-left: 3rem;
h1,
h2 {
margin: 0;
padding: 0;
}

h2.icebreakers-logotype {
font-size: 2.2rem;
margin-bottom: 2rem;
}
}

p.instructions {
margin-bottom: 2rem;
}
}
}
78 changes: 78 additions & 0 deletions src/simple-comment-icebreakers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
declare global {
interface Window {
getQuestion: (slug: string) => Promise<string>
}
}
const storageKey = "icebreakerQuestions"
const timeStampKey = "icebreakerQuestionsTimeStamp"

const toSlug = (str: string): string =>
str
.toLowerCase()
.replace(/ /g, "-")
.replace(/[^\w-]+/g, "")
.replace(/_/g, "")
.replace(/-{2,}/g, "-")
.replace(/-$/, "")
.replace(/^-/, "")

const isSlugMatch = (slug: string, question: string) =>
slug === toSlug(question)

const reverseSlug = (slug: string, questions: string[]) =>
questions.find(question => isSlugMatch(slug, question))

const fetchAndStoreQuestions = () =>
new Promise<string[]>((resolve, reject) => {
const currentTimestamp =
document?.getElementById("questions-time-stamp")?.innerText || "0"
const storedTimestamp = localStorage.getItem(timeStampKey)

const isStoredQuestionsValid =
storedTimestamp && parseInt(storedTimestamp) >= parseInt(currentTimestamp)

const storedQuestions = localStorage.getItem(storageKey)
if (isStoredQuestionsValid && storedQuestions) {
resolve(JSON.parse(storedQuestions))
return
}

fetchQuestions(currentTimestamp, resolve, reject)
})

const fetchQuestions = async (currentTimestamp, resolve, reject) => {
try {
const questionFile = await fetch(
"https://raw.githubusercontent.com/rendall/icebreakers/master/QUESTIONS.md"
)
const questionsText = await questionFile.text()
const questionLines = questionsText.split("\n")
const questions = questionLines
.filter(line => /^ {2}\* [A-Z]/.test(line))
.map(line => line.slice(4))

localStorage.setItem(storageKey, JSON.stringify(questions))
localStorage.setItem(timeStampKey, currentTimestamp)
resolve(questions)
} catch (error) {
reject(error)
}
}

const getQuestion = (slug: string) =>
new Promise<string>((resolve, reject) => {
fetchAndStoreQuestions()
.then(questions => {
const question = reverseSlug(slug, questions)
if (question) {
resolve(question)
} else {
reject("No question found")
}
})
.catch(reject)
})

window.getQuestion = getQuestion

export default getQuestion
21 changes: 17 additions & 4 deletions src/simple-comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import SimpleComment from "./components/SimpleComment.svelte"

declare global {
interface Window {
setSimpleCommentOptions: (setupOptions: { [key: string]: unknown }) => void
loadSimpleComment: (options: Options) => void
setSimpleCommentDiscussion: (discussionId: string) => void
setSimpleCommentOptions: (setupOptions: { [key: string]: unknown }) => void
}
}

let simpleComment

let options = {
cancel: false,
discussionId: getDefaultDiscussionId(),
title: document.title,
target: document.getElementById("simple-comment") ?? document.body,
title: document.title,
}

type Options = typeof options

/**
* Sets the options for the SimpleComment component.
* This function merges the provided options with the default options.
Expand Down Expand Up @@ -51,16 +55,25 @@ window.setSimpleCommentOptions = setupOptions =>
window.setSimpleCommentDiscussion = (discussionId: string) =>
window.setSimpleCommentOptions({ discussionId })

// Wait for DOMContentLoaded event before initializing SimpleComment
document.addEventListener("DOMContentLoaded", () => {
const loadSimpleComment = (setupOptions: Options) => {
options = { ...options, ...setupOptions }
simpleComment = new SimpleComment({
target: options.target,
props: { discussionId: options.discussionId, title: options.title },
})
}

window.loadSimpleComment = loadSimpleComment

// Wait for DOMContentLoaded event before initializing SimpleComment
document.addEventListener("DOMContentLoaded", () => {
if (options.cancel) return
else loadSimpleComment(options)
})

export default {
simpleComment,
setSimpleCommentOptions: window.setSimpleCommentOptions,
setSimpleCommentDiscussion: window.setSimpleCommentDiscussion,
loadSimpleComment: window.loadSimpleComment,
}
2 changes: 1 addition & 1 deletion src/static/_redirects
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/topic/* /topic/index.html 200
/icebreakers/* /icebreakers/index.html 200
Loading

0 comments on commit dd362e3

Please sign in to comment.