Skip to content

Commit

Permalink
Add ability to save and recall lower thirds (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanlihou committed Feb 27, 2024
1 parent 86badbd commit 67d4741
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/assets/delete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/load.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/save.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/show.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 15 additions & 3 deletions src/components/Manage/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { route } from 'preact-router';
import { getAuth } from 'firebase/auth';
import styled from 'styled-components';
import {
useContext, useEffect, useMemo, useRef, useState,
useCallback, useContext, useEffect, useMemo, useRef, useState,
} from 'preact/hooks';
import {
ref, getDatabase, onValue, DatabaseReference, set,
ref, getDatabase, onValue, DatabaseReference, update,
} from 'firebase/database';
import AppContext from '@/AppContext';
import { CGConfig } from '@/types';
import ErrorMessage from '../ErrorMessage';
import SavedLowerThirds, { SavedLowerThirdRecord } from './SavedLowerThirds';

const OptionsPage = styled.div`
font-size: 18px;
Expand Down Expand Up @@ -170,7 +171,7 @@ const Options = () => {
if (cgConfigDbRef.current === undefined) {
throw new Error('DB Reference not defined');
}
await set(cgConfigDbRef.current, {
await update(cgConfigDbRef.current, {
pageBg: pageBg ?? null,
showBranding: showBranding ?? null,
showTicker: showTicker ?? null,
Expand All @@ -191,6 +192,11 @@ const Options = () => {
}
};

const onLoadLowerThird = useCallback((rec: SavedLowerThirdRecord) => {
setLowerThirdTitle(rec.title);
setLowerThirdSubtitle(rec.subtitle);
}, []);

const onClickLogout = async () => {
await getAuth().signOut();
route('/login');
Expand Down Expand Up @@ -309,6 +315,12 @@ const Options = () => {
Subtitle
<input id="lowerThirdSubtitle" onChange={(e) => setLowerThirdSubtitle((e.target as HTMLInputElement).value)} value={lowerThirdSubtitle ?? undefined} type="text" />
</label>
<SavedLowerThirds
lowerThirdTitle={lowerThirdTitle}
lowerThirdSubtitle={lowerThirdSubtitle}
cgConfigRef={cgConfigDbRef}
onLoad={onLoadLowerThird}
/>
</OptionsBox>
</main>
<footer>
Expand Down
172 changes: 172 additions & 0 deletions src/components/Manage/SavedLowerThirds.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { h } from 'preact';
import {
MutableRef, useCallback, useMemo, useEffect, useState,
} from 'preact/hooks';
import styled from 'styled-components';
import {
DatabaseReference, child, off, onValue, push, remove, update,
} from 'firebase/database';
import ShowIcon from '@/assets/show.svg';
import LoadIcon from '@/assets/load.svg';
import SaveIcon from '@/assets/save.svg';
import DeleteIcon from '@/assets/delete.svg';
import { CGConfig } from '@/types';

export type SavedLowerThirdRecord = {
title: string | null,
subtitle: string | null,
};

type SavedLowerThirdsProps = {
lowerThirdTitle: string | null,
lowerThirdSubtitle: string | null,
cgConfigRef: MutableRef<DatabaseReference | undefined>,
onLoad: (record: SavedLowerThirdRecord) => void,
};

const Container = styled.div`
table tr>td:last-of-type {
width: 0.1%;
word-wrap: nowrap;
}
.buttons{
display: flex;
gap: .5em;
button {
display: block;
width: 100%;
margin-block: .5em;
padding: .2em;
&.destructive {
background-color: red;
border-color: red;
}
img {
height: 100%;
}
&:hover img {
filter: grayscale(1);
}
}
}
`;

const SavedLowerThirds = ({
lowerThirdTitle, lowerThirdSubtitle, cgConfigRef, onLoad,
}: SavedLowerThirdsProps) => {
const savedRef = useMemo(() => (cgConfigRef.current ? child(cgConfigRef.current, 'lowerThirds') : undefined), [cgConfigRef.current]);
const [saved, setSaved] = useState<{ [_: string]: SavedLowerThirdRecord }>({});

useEffect(() => {
if (savedRef) {
onValue(savedRef, (snap) => {
setSaved(snap.val() ?? {});
console.log(snap.val());
});
}

return () => {
if (savedRef) off(savedRef);
};
}, [savedRef]);

const onClickSave = useCallback(() => {
(async () => {
if (lowerThirdTitle === null && lowerThirdSubtitle === null) return;
if (!savedRef) {
// eslint-disable-next-line no-alert
alert('Unable to connect to database.');
return;
}

console.log({
title: lowerThirdTitle,
subtitle: lowerThirdSubtitle,
});
await push(savedRef, {
title: lowerThirdTitle,
subtitle: lowerThirdSubtitle,
});
})();
}, [lowerThirdTitle, lowerThirdSubtitle, savedRef]);

const onClickLoad = useCallback((record: SavedLowerThirdRecord) => {
onLoad(record);
}, []);

const onClickShow = useCallback((rec: SavedLowerThirdRecord) => {
(async () => {
if (!cgConfigRef.current) {
// eslint-disable-next-line no-alert
alert('Unable to connect to database.');
return;
}

await update(cgConfigRef.current, {
lowerThirdTitle: rec.title,
lowerThirdSubtitle: rec.subtitle,
} as Partial<CGConfig>);
})();
}, [savedRef, saved]);

const onClickDelete = useCallback((key: string) => {
(async () => {
if (!savedRef) {
// eslint-disable-next-line no-alert
alert('Unable to connect to database.');
return;
}

// eslint-disable-next-line no-alert
if (!window.confirm(`Are you sure you want to delete '${saved[key].title}'?`)) return;

await remove(child(savedRef, key));
})();
}, [savedRef, saved]);

return (
<Container>
<div className="buttons">
<button
type="button"
title="Save"
onClick={onClickSave}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '1em',
}}
>
<img src={SaveIcon} alt="Save" /> <span>Save</span>
</button>
</div>
<h3>Saved</h3>
<table>
{Object.keys(saved).length > 0 ? Object.entries(saved).map(([key, rec]) => (
<tr key={key}>
<td>
<div>{rec.title}</div>
<div style={{ marginLeft: '1em' }}>{rec.subtitle}</div>
</td>
<td>
<div className="buttons">
<button type="button" title="Load" onClick={() => onClickLoad(rec)}><img src={LoadIcon} alt="Load" /></button>
<button type="button" title="Immediately Show" onClick={() => onClickShow(rec)}><img src={ShowIcon} alt="Show" /></button>
<button type="button" className="destructive" title="Delete" onClick={() => onClickDelete(key)}>
<img src={DeleteIcon} alt="Delete" />
</button>
</div>
</td>
</tr>
)) : <span>You don&apos;t have any saved lower thirds yet...</span>}
</table>
</Container>
);
};

export default SavedLowerThirds;
2 changes: 1 addition & 1 deletion src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const Routes: Route<any>[] = [
requiresLogin: true,
},
{
name: 'Audience Display',
name: 'Stream Overlay',
url: '/overlay',
linkFactory: (ctx: AppContextType) => `/overlay?key=${ctx.token}`,
component: Overlay,
Expand Down

0 comments on commit 67d4741

Please sign in to comment.