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

Meeting settings: Alternate Welcome page, custom CSS #172

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions client/Header.coffee
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react'
import {Tooltip, OverlayTrigger} from 'react-bootstrap'

import {useMeetingTitle} from './MeetingTitle'
import {useMeetingSetting} from './MeetingSetting'

export Header = React.memo ->
title = useMeetingTitle()
title = useMeetingSetting('title')
<nav>
<OverlayTrigger placement="bottom" overlay={(props) ->
<Tooltip {...props}>Comingle</Tooltip>
Expand Down
17 changes: 17 additions & 0 deletions client/Meeting.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,23 @@ export Meeting = React.memo ->
parts.push room.title if room?.title
document.title = parts.reverse().join ' - '
, []
## Add custom CSS stylesheet
useTracker ->
meeting = Meetings.findOne meetingId
stylePath = meeting?.css
if stylePath and /^https:\/\/[^ "]+$/.exec stylePath
link = document.getElementById "customCss"
if link
link.href = stylePath
else
head = document.head
link = document.createElement "link"
link.type = "text/css"
link.rel = "stylesheet"
link.href = stylePath
link.id = "customCss"
head.appendChild link
, []

factory = (node) ->
updateTab = -> FlexLayout.updateNode model, node.getId()
Expand Down
32 changes: 16 additions & 16 deletions client/MeetingTitle.coffee → client/MeetingSetting.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,48 @@ import {Meetings} from '/lib/meetings'
import {getUpdator} from './lib/presenceId'
import {useDebounce} from './lib/useDebounce'

export useMeetingTitle = ->
export useMeetingSetting = (setting) ->
{meetingId} = useParams()
meeting = useTracker ->
Meetings.findOne meetingId
, [meetingId]
meeting?.title
meeting?[setting]

export MeetingTitle = React.memo ->
export MeetingSetting = React.memo ({setting, alt, placeholder}) ->
{meetingId} = useParams()
meeting = useTracker ->
Meetings.findOne meetingId
, [meetingId]
[title, setTitle] = useState ''
[value, setValue] = useState ''
[changed, setChanged] = useState null
## Synchronize text box to title from database whenever it changes
## Synchronize text box to setting from database whenever it changes
useLayoutEffect ->
return unless meeting?.title?
setTitle meeting.title
return unless meeting?[setting]?
setValue meeting[setting]
setChanged false
, [meeting?.title]
## When text box stabilizes for half a second, update database title
, [meeting?[setting]]
## When text box stabilizes for half a second, update database setting
changedDebounce = useDebounce changed, 500
useLayoutEffect ->
return unless changedDebounce?
unless title == meeting.title
unless value == meeting[setting]
Meteor.call 'meetingEdit',
id: meetingId
title: title
"#{setting}": value
updator: getUpdator()
setChanged null
, [changedDebounce]

<Card>
<Card.Header className="tight">
Meeting Title:
{alt}:
</Card.Header>
<Card.Body>
<Form.Control type="text" placeholder="Comingle Meeting"
value={title} onChange={(e) ->
setTitle e.target.value
<Form.Control type="text" placeholder={placeholder}
value={value} onChange={(e) ->
setValue e.target.value
setChanged e.target.value # ensure `change` different for each update
}/>
</Card.Body>
</Card>
MeetingTitle.displayName = 'MeetingTitle'
MeetingSetting.displayName = 'MeetingSetting'
26 changes: 16 additions & 10 deletions client/Settings.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {useParams} from 'react-router-dom'
import {Card, Form} from 'react-bootstrap'

import {LocalStorageVar, StorageDict} from './lib/useLocalStorage'
import {MeetingTitle} from './MeetingTitle'
import {MeetingSetting} from './MeetingSetting'
import {MeetingSecret, useMeetingAdmin} from './MeetingSecret'

export Settings = React.memo ->
Expand All @@ -18,18 +18,24 @@ export Settings = React.memo ->
</Card.Body>
</Card>
<div className="sidebar">
<MeetingTitle/>
<MeetingSetting setting="title" alt="Meeting Title" placeholder="Comingle Meeting"/>
<MeetingSecret/>
</div>
{if admin
<Card>
<Card.Body>
<Card.Title as="h3">Admin</Card.Title>
<Form>
<AdminVisit/>
</Form>
</Card.Body>
</Card>
<>
<Card>
<Card.Body>
<Card.Title as="h3">Admin</Card.Title>
<Form>
<AdminVisit/>
</Form>
</Card.Body>
</Card>
<div className="sidebar">
<MeetingSetting setting="welcome" alt="Welcome URL (https only)" placeholder="(default)"/>
<MeetingSetting setting="css" alt="Custom CSS (https only)" placeholder="(none)"/>
</div>
</>
}
</>
Settings.displayName = 'Settings'
Expand Down
97 changes: 51 additions & 46 deletions client/Welcome.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,61 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {clipboardLink} from './icons/clipboardLink'

import {useMeetingSecret} from './MeetingSecret'
import {useMeetingSetting} from './MeetingSetting'
import {homepage, repository} from '/package.json'

export Welcome = ->
{meetingId} = useParams()
meetingUrl = Meteor.absoluteUrl "/m/#{meetingId}"
meetingSecret = useMeetingSecret()
meetingWelcome = useMeetingSetting('welcome')

<Card>
<Card.Body>
<Card.Title as="h3">Welcome to Comingle!</Card.Title>
<p>
<b>Comingle</b> is an <a href={repository.url}>open-source</a> online
meeting tool whose goal is to approximate the advantages of
in-person meetings.
It integrates web tools in an open multiroom environment.
</p>
<h5>Getting Started:</h5>
<ul>
<li>First, <b>enter your name</b> (first and last) in the left panel (top text box).</li>
<li>To <b>join a room</b>, click on a room (such as &ldquo;Main Room&rdquo;) in the room list on the left.</li>
<li>When you click a second room, you'll be offered to &ldquo;<b>Switch to Room</b>&rdquo; which leaves the current room and any video calls (shortcut: hold <kbd>Shift</kbd> while clicking).</li>
<li>Each room contains one or more <b>tabs</b>: video call, whiteboard, etc.
You can drag these tabs to re-arrange them however you like!</li>
<li><b>Star</b> rooms to (publicly) indicate your interest in that topic. To focus on just starred rooms, unfold the &ldquo;<b>Your Starred Rooms</b>&rdquo; section.</li>
<li><a href={homepage}>Read the documentation</a> for more information.</li>
</ul>
<h5>
<span className="mr-2">Meeting&nbsp;Link:</span>
{' '}
<code className="text-break user-select-all">{meetingUrl}</code>
<OverlayTrigger placement="top" overlay={(props) ->
<Tooltip {...props}>Copy meeting link to clipboard</Tooltip>
}>
<div aria-label="Copy meeting link to clipboard"
onClick={-> navigator.clipboard.writeText meetingUrl}
className="flexlayout__tab_button_trailing">
<FontAwesomeIcon icon={clipboardLink}/>
</div>
</OverlayTrigger>
</h5>
<p className="ml-4">
Send this link to other people to let them join the meeting.
</p>
{if meetingSecret
<>
<h5>Administrative Access</h5>
<p className="ml-4">
You have administrative access to this meeting because you either created it or entered the <b>Meeting Secret</b> (under Settings). You should record the secret (for gaining access on other machines/browsers) and give it to anyone you want to have administrative access.
</p>
</>
}
</Card.Body>
</Card>
if meetingWelcome and /^https:\/\/[^ "]+$/.exec meetingWelcome
<iframe src={meetingWelcome}> </iframe>
else
<Card>
<Card.Body>
<Card.Title as="h3">Welcome to Comingle!</Card.Title>
<p>
<b>Comingle</b> is an <a href={repository.url}>open-source</a> online
meeting tool whose goal is to approximate the advantages of
in-person meetings.
It integrates web tools in an open multiroom environment.
</p>
<h5>Getting Started:</h5>
<ul>
<li>First, <b>enter your name</b> (first and last) in the left panel (top text box).</li>
<li>To <b>join a room</b>, click on a room (such as &ldquo;Main Room&rdquo;) in the room list on the left.</li>
<li>When you click a second room, you'll be offered to &ldquo;<b>Switch to Room</b>&rdquo; which leaves the current room and any video calls (shortcut: hold <kbd>Shift</kbd> while clicking).</li>
<li>Each room contains one or more <b>tabs</b>: video call, whiteboard, etc.
You can drag these tabs to re-arrange them however you like!</li>
<li><b>Star</b> rooms to (publicly) indicate your interest in that topic. To focus on just starred rooms, unfold the &ldquo;<b>Your Starred Rooms</b>&rdquo; section.</li>
<li><a href={homepage}>Read the documentation</a> for more information.</li>
</ul>
<h5>
<span className="mr-2">Meeting&nbsp;Link:</span>
{' '}
<code className="text-break user-select-all">{meetingUrl}</code>
<OverlayTrigger placement="top" overlay={(props) ->
<Tooltip {...props}>Copy meeting link to clipboard</Tooltip>
}>
<div aria-label="Copy meeting link to clipboard"
onClick={-> navigator.clipboard.writeText meetingUrl}
className="flexlayout__tab_button_trailing">
<FontAwesomeIcon icon={clipboardLink}/>
</div>
</OverlayTrigger>
</h5>
<p className="ml-4">
Send this link to other people to let them join the meeting.
</p>
{if meetingSecret
<>
<h5>Administrative Access</h5>
<p className="ml-4">
You have administrative access to this meeting because you either created it or entered the <b>Meeting Secret</b> (under Settings). You should record the secret (for gaining access on other machines/browsers) and give it to anyone you want to have administrative access.
</p>
</>
}
</Card.Body>
</Card>
2 changes: 2 additions & 0 deletions lib/meetings.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Meteor.methods
check diff,
id: String
title: Match.Optional String
welcome: Match.Optional String
css: Match.Optional String
updator: updatorPattern
secret: Match.Optional String
unless @isSimulation
Expand Down