diff --git a/src/components/MeetingNavbar/InviteButtons.svelte b/src/components/MeetingNavbar/InviteButtons.svelte new file mode 100644 index 0000000..bd4942c --- /dev/null +++ b/src/components/MeetingNavbar/InviteButtons.svelte @@ -0,0 +1,44 @@ + + +
+ + +
diff --git a/src/components/MeetingNavbar/MeetingNavbar.svelte b/src/components/MeetingNavbar/MeetingNavbar.svelte new file mode 100644 index 0000000..864c80d --- /dev/null +++ b/src/components/MeetingNavbar/MeetingNavbar.svelte @@ -0,0 +1,62 @@ + + + diff --git a/src/containers/CodeEditor/editor.ts b/src/containers/CodeEditor/editor.ts index 4e71cf9..71c167b 100644 --- a/src/containers/CodeEditor/editor.ts +++ b/src/containers/CodeEditor/editor.ts @@ -14,7 +14,6 @@ import { IndexeddbPersistence } from 'y-indexeddb'; import { WebrtcProvider } from 'y-webrtc'; import { YJS_WEBRTC_SIGNALLING_SERVERS } from '$lib/constants'; import * as awarenessProtocol from 'y-protocols/awareness.js'; -import { randomColor } from '$lib/util'; type TExtendedSelf = typeof globalThis & { MonacoEnvironment: { @@ -89,6 +88,8 @@ export const initEditorTracking = async ( // A Yjs document holds the shared data const ydoc = new Y.Doc(); const type = ydoc.getText('monaco'); // TODO Understand better... + const undoManager = new Y.UndoManager(type); + console.debug(undoManager); // TODO #22 Handle doc persistence when starting a new webrtc conference. // We persist the document content across sessions diff --git a/src/lib/routes.ts b/src/lib/routes.ts index 538dc8e..79c4dda 100644 --- a/src/lib/routes.ts +++ b/src/lib/routes.ts @@ -2,3 +2,7 @@ const _BASE = ''; export const BASE = `${_BASE}/`; export const ABOUT = `${_BASE}/about`; + +export const MEET = `${_BASE}/meet`; +export const MEET_CREATE = `${MEET}/create`; +export const MEET_SLUG = (s: string): string => `${MEET}/${s}`; diff --git a/src/lib/util/index.ts b/src/lib/util/index.ts index 82989d2..983245a 100644 --- a/src/lib/util/index.ts +++ b/src/lib/util/index.ts @@ -1,3 +1,5 @@ +import { browser } from '$app/env'; + // TODO #23 Pretty colors w/ golden ratio const _USER_COLORS = [ '#30bced', @@ -11,3 +13,68 @@ const _USER_COLORS = [ ]; export const randomColor = () => _USER_COLORS[Math.floor(Math.random() * _USER_COLORS.length)]; + +export function copyToClipboard(text: string): void { + if (!browser) { + console.error('Cannot copy: Not in browser.'); + return; + } + + const textArea = document.createElement('textarea'); + + // + // *** This styling is an extra step which is likely not required. *** + // + // Why is it here? To ensure: + // 1. the element is able to have focus and selection. + // 2. if the element was to flash render it has minimal visual impact. + // 3. less flakyness with selection and copying which **might** occur if + // the textarea element is not visible. + // + // The likelihood is the element won't even render, not even a + // flash, so some of these are just precautions. However in + // Internet Explorer the element is visible whilst the popup + // box asking the user for permission for the web page to + // copy to the clipboard. + // + + // Place in the top-left corner of screen regardless of scroll position. + textArea.style.position = 'fixed'; + textArea.style.top = '0'; + textArea.style.left = '0'; + + // Ensure it has a small width and height. Setting to 1px / 1em + // doesn't work as this gives a negative w/h on some browsers. + textArea.style.width = '2em'; + textArea.style.height = '2em'; + + // We don't need padding, reducing the size if it does flash render. + textArea.style.padding = '0'; + + // Clean up any borders. + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + + // Avoid flash of the white box if rendered for any reason. + textArea.style.background = 'transparent'; + + textArea.value = text; + + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + if (!document.execCommand('copy')) { + throw new Error('Could not copy'); + } + // var msg = successful ? 'successful' : 'unsuccessful'; + // console.log('Copying text command was ' + msg); + } catch (err) { + console.error(`Unable to copy: ${text}`); + alert(`Unable to copy: ${text}`); + } + + document.body.removeChild(textArea); +} diff --git a/src/lib/util/naming.ts b/src/lib/util/naming.ts index 8fa4dfd..b28b9d7 100644 --- a/src/lib/util/naming.ts +++ b/src/lib/util/naming.ts @@ -4,6 +4,9 @@ export const randomAnimal = () => _ANIMALS[Math.floor(Math.random() * _ANIMALS_L export const randomName = (machine = false): string => `${randomDescriptor()} ${randomAnimal()}`.replace(' ', machine ? '-' : ' '); +export const randomMeetingName = () => + `${randomAnimal().toLowerCase().replace(' ', '-')}-${makeid(10)}`; + export const emojiFromName = (name: string): string => { if (_NAME_TO_EMOJI[name]) { return _NAME_TO_EMOJI[name]; @@ -17,6 +20,21 @@ export const emojiFromName = (name: string): string => { return v; }; +/** + * h/t https://stackoverflow.com/a/1349426/3422060 + * @param length Length of ID + * @returns an ID + */ +function makeid(length: number): string { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + const _NAME_TO_EMOJI = { Monkey: '🐒', Gorilla: 'ðŸĶ', Orangutan: 'ðŸĶ§', 'Dog Face': 'ðŸķ', Dog: '🐕', 'Guide Dog': 'ðŸĶŪ', 'Service Dog': '🐕‍ðŸĶš', Poodle: 'ðŸĐ', Wolf: '🐚', Fox: 'ðŸĶŠ', Raccoon: 'ðŸĶ', Cat: '🐈', 'Black Cat': '🐈‍⎛', Lion: 'ðŸĶ', Tiger: '🐅', Leopard: '🐆', Horse: '🐎', Unicorn: 'ðŸĶ„', Zebra: 'ðŸĶ“', Deer: 'ðŸĶŒ', Ox: '🐂', 'Water Buffalo': '🐃', Cow: '🐄', Pig: '🐖', Boar: '🐗', 'Pig Nose': 'ðŸ―', Ram: '🐏', Ewe: '🐑', Goat: '🐐', Camel: '🐊', 'Two-hump Camel': 'ðŸŦ', Llama: 'ðŸĶ™', Giraffe: 'ðŸĶ’', Elephant: '🐘', Mammoth: 'ðŸĶĢ', Rhinoceros: 'ðŸĶ', Hippopotamus: 'ðŸĶ›', Mouse: '🐁', Rat: '🐀', Hamster: 'ðŸđ', Rabbit: '🐇', Chipmunk: 'ðŸŋïļ', Beaver: 'ðŸĶŦ', Hedgehog: 'ðŸĶ”', Bat: 'ðŸĶ‡', Bear: 'ðŸŧ', 'Polar Bear': 'ðŸŧ‍❄ïļ', Koala: 'ðŸĻ', Panda: '🐞', Sloth: 'ðŸĶĨ', Otter: 'ðŸĶĶ', Skunk: 'ðŸĶĻ', Kangaroo: 'ðŸĶ˜', Badger: 'ðŸĶĄ', 'Paw Prints': 'ðŸū', Turkey: 'ðŸĶƒ', Chicken: '🐔', Rooster: '🐓', 'Hatching Chick': 'ðŸĢ', 'Baby Chick': 'ðŸĪ', Bird: 'ðŸĶ', Penguin: '🐧', Dove: '🕊ïļ', Eagle: 'ðŸĶ…', Duck: 'ðŸĶ†', Swan: 'ðŸĶĒ', Owl: 'ðŸĶ‰', Dodo: 'ðŸĶĪ', Feather: 'ðŸŠķ', Flamingo: 'ðŸĶĐ', Peacock: 'ðŸĶš', Parrot: 'ðŸĶœ', Frog: 'ðŸļ', Crocodile: '🐊', Turtle: 'ðŸĒ', Lizard: 'ðŸĶŽ', Snake: '🐍', Dragon: '🐉', Sauropod: 'ðŸĶ•', 'T-Rex': 'ðŸĶ–', Whale: '🐋', Dolphin: '🐎', Seal: 'ðŸĶ­', Fish: '🐟', 'Tropical Fish': '🐠', Blowfish: 'ðŸĄ', Shark: 'ðŸĶˆ', Octopus: '🐙', 'Spiral Shell': '🐚', Coral: 'ðŸŠļ', Snail: '🐌', Butterfly: 'ðŸĶ‹', Bug: '🐛', Ant: '🐜', Honeybee: '🐝', Beetle: 'ðŸŠē', 'Lady Beetle': '🐞', Cricket: 'ðŸĶ—', Cockroach: 'ðŸŠģ', Spider: '🕷ïļ', 'Spider Web': 'ðŸ•ļïļ', Scorpion: 'ðŸĶ‚', Mosquito: 'ðŸĶŸ', Fly: '🊰', Worm: 'ðŸŠą', Microbe: 'ðŸĶ ' } as Record; // prettier-ignore // Not included: // "Tiger Face": "ðŸŊ", "Cat Face": "ðŸą", "Horse Face": "ðŸī", "Bison": "ðŸĶŽ", "Cow Face": "ðŸŪ", "Pig Face": "🐷", "Mouse Face": "🐭", "Rabbit Face": "🐰", "Cat Face": "ðŸą", "Front-facing Baby Chick": "ðŸĨ", "Dragon Face": "ðŸē", "Spouting Whale": "ðŸģ", "Monkey Face": "ðŸĩ", diff --git a/src/routes/__layout-root.svelte b/src/routes/__layout-root.svelte new file mode 100644 index 0000000..647ebbb --- /dev/null +++ b/src/routes/__layout-root.svelte @@ -0,0 +1,12 @@ + + + + {PROJECT_NAME} + + + + diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 82cca1f..81672f4 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -9,8 +9,6 @@ {PROJECT_NAME} -
-
diff --git a/src/routes/meet/[slug]/__layout@root.svelte b/src/routes/meet/[slug]/__layout@root.svelte new file mode 100644 index 0000000..e676aef --- /dev/null +++ b/src/routes/meet/[slug]/__layout@root.svelte @@ -0,0 +1,12 @@ + + + + Meet {$page.params.slug} | {PROJECT_NAME} + + + + diff --git a/src/routes/meet/[slug]/index.svelte b/src/routes/meet/[slug]/index.svelte new file mode 100644 index 0000000..6b1bfa7 --- /dev/null +++ b/src/routes/meet/[slug]/index.svelte @@ -0,0 +1,3 @@ +

Meeting

+ +

TODO: Carry out actual meeting here.

diff --git a/src/routes/meet/create.svelte b/src/routes/meet/create.svelte new file mode 100644 index 0000000..c7b7897 --- /dev/null +++ b/src/routes/meet/create.svelte @@ -0,0 +1,3 @@ +

Create a new meeting

+ +

TODO: Create a new meeting here

diff --git a/src/routes/meet/index.svelte b/src/routes/meet/index.svelte new file mode 100644 index 0000000..aecf56f --- /dev/null +++ b/src/routes/meet/index.svelte @@ -0,0 +1,3 @@ +

Meetings

+ +

TODO: List all previous sessions here.