Skip to content

Commit

Permalink
Add beta transcript mode
Browse files Browse the repository at this point in the history
  • Loading branch information
patdx committed Jan 5, 2025
1 parent 8c92172 commit 1d358e6
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 108 deletions.
5 changes: 2 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
- restore favicon, etc
- fix broken timer buttons
- syncing between devices
- use evolu or one(?) that crsqlite related thing
- switch to remix?
- disable unnecessary text highlighting
- eg when clicking the next button
- and double click triggering highlight instead of another click?
Expand All @@ -14,7 +13,6 @@
- track time for each episode separately
- remember last time for each episode
- next episode button
- transcript view
- markup support
- have it fade out after a few seconds
- only show full screen button on platforms that support it (not iOS)
Expand All @@ -23,3 +21,4 @@
- auto shrink large text if it does not fit
- Fix untappable buttons in PwA mode
- fix theme color/background color
- show title of program inside details page
2 changes: 1 addition & 1 deletion app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const EditFilesPage = () => {
<p className="mt-2 text-sm text-gray-600">Play your subtitle files</p>
</div>

<div className="rounded-lg bg-white p-8 shadow-sm">
<div className="rounded-lg bg-white py-8 shadow-sm">
<input
id={`${id}-file-upload`}
className="hidden"
Expand Down
32 changes: 11 additions & 21 deletions app/routes/play.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Page } from 'konsta/react'
import { DateTime } from 'luxon'
import { sortBy } from 'lodash-es'
import { observer } from 'mobx-react-lite'
import { TranscriptDisplay } from '~/shared/transcript-display'

// TODO: update the whole page bg to black when this page is open

export default function PlayPage() {
return (
Expand All @@ -9,45 +13,31 @@ export default function PlayPage() {
)
}

const Play = () => {
const updateElapsedTime = () => {
const timeSinceLastAction = clock.isPlaying
? Math.abs(DateTime.fromJSDate(clock.lastActionAt).diffNow().toMillis()) *
clock.playSpeed
: 0
setTimeElapsed(timeSinceLastAction + clock.lastTimeElapsedMs)
requestAnimationFrame(updateElapsedTime)
}

const Play = observer(() => {
async function loadFile() {
const fileId = new URL(location.href).searchParams.get('id')
if (!fileId) {
console.warn(`No id provided, waiting for file id...`)
return
}
const db = await initAndGetDb()
const lines = await db.getAllFromIndex('lines', 'by-file-id', fileId)
let lines = await db.getAllFromIndex('lines', 'by-file-id', fileId)
console.log(`loaded ${lines.length} lines`)
lines = sortBy(lines, (line) => line.from)
setFile(lines)
}

useEffect(() => {
loadFile()
}, [])

useEffect(() => {
requestAnimationFrame(updateElapsedTime)
}, [])

return (
<>
<div className="relative h-full overflow-hidden bg-black">
<div className="absolute left-0 right-0 top-[-100%] bottom-[-100%]">
<FileDisplay />
</div>

<FileDisplay />
<TranscriptDisplay />
<Controls />
</div>
</>
)
}
})
98 changes: 44 additions & 54 deletions app/shared/controls.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { once } from 'lodash-es'
import {
clock,
getTimeElapsed,
getTimeElapsedAsDuration,
setClock,
setTextSize,
TEXT_SIZES,
} from './utils'
import { observer } from 'mobx-react-lite'
import { Link } from 'react-router'
import {
FullScreenIcon,
GoBackIcon,
Expand All @@ -15,23 +8,18 @@ import {
PauseIcon,
PlayIcon,
RightIcon,
TranscriptIcon,
} from './icons'
import { NumberInput } from './text-input'
import { Link } from 'react-router'
import { observer } from 'mobx-react-lite'

const getNoSleep = once(async () => {
const { default: NoSleep } = await import('nosleep.js')
return new NoSleep()
})

function enableNoSleep() {
getNoSleep().then((ns) => ns.enable())
}

function disableNoSleep() {
getNoSleep().then((ns) => ns.disable())
}
import {
clock,
controlState,
getTimeElapsed,
getTimeElapsedAsDuration,
setClock,
setTextSize,
TEXT_SIZES,
} from './utils'

const IconTextButton = ({ icon, text, onClick }: any) => {
return (
Expand Down Expand Up @@ -60,16 +48,21 @@ const TextButton = ({ children, onClick }: any) => {
)
}

export const Controls = observer(() => {
const [isOpen, setIsOpen] = useSignal(true)
// const controlState = makeAutoObservable({
// isOpen: true,
// toggle() {
// this.isOpen = !this.isOpen
// },
// })

export const Controls = observer(() => {
return (
<>
<div className="absolute left-0 right-0 top-0 bg-gradient-to-b from-black to-transparent pb-8 pl-[env(safe-area-inset-left,0)] pr-[env(safe-area-inset-right,0)]">
{/* padding for iOS */}
<div className="h-[env(safe-area-inset-top,0)]"></div>
<div className="flex">
<Show when={isOpen}>
<Show when={() => controlState.isOpen}>
{/* go back button */}
<Link
to="/"
Expand All @@ -81,7 +74,17 @@ export const Controls = observer(() => {

<div className="flex-1"></div>

<Show when={isOpen}>
<Show when={() => controlState.isOpen}>
{/* transcript button */}
<button
onClick={() => controlState.toggleTranscript()}
className="flex h-10 w-10 flex-none items-center justify-center text-gray-200 hover:text-white active:text-white"
>
<TranscriptIcon />
</button>
</Show>

<Show when={() => controlState.showFullScreenButton}>
{/* full screen button (for Android) */}
<button
onClick={() => {
Expand Down Expand Up @@ -111,15 +114,15 @@ export const Controls = observer(() => {

{/* toggle menu */}
<button
onClick={() => setIsOpen((isOpen) => !isOpen)}
onClick={controlState.toggle}
className="flex h-10 w-10 flex-none items-center justify-center text-gray-200 hover:text-white active:text-white"
>
<MenuIcon />
</button>
</div>
</div>

<Show when={isOpen}>
<Show when={() => controlState.isOpen}>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent pt-16 pl-[env(safe-area-inset-left,0)] pr-[env(safe-area-inset-right,0)]">
<div className="mx-auto flex max-w-sm flex-col flex-wrap items-stretch justify-center gap-2 sm:max-w-none sm:flex-row sm:items-center">
<div className="flex items-center justify-between sm:justify-center">
Expand All @@ -133,7 +136,7 @@ export const Controls = observer(() => {
hours: value,
})
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: duration.toMillis(),
})
}}
Expand All @@ -148,7 +151,7 @@ export const Controls = observer(() => {
minutes: value,
})
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: duration.toMillis(),
})
}}
Expand All @@ -163,7 +166,7 @@ export const Controls = observer(() => {
seconds: value,
})
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: duration.toMillis(),
})
}}
Expand All @@ -179,7 +182,7 @@ export const Controls = observer(() => {
milliseconds: value,
})
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: duration.toMillis(),
})
}}
Expand All @@ -192,7 +195,7 @@ export const Controls = observer(() => {
text={'1s'}
onClick={() => {
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: getTimeElapsed() - 1000,
})
}}
Expand All @@ -203,30 +206,17 @@ export const Controls = observer(() => {
text={'0.1s'}
onClick={() => {
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: getTimeElapsed() - 100,
})
}}
/>

<button
className="flex h-10 w-10 items-center justify-center text-gray-200 hover:text-white active:text-white"
onClick={async () => {
onClick={() => {
const isPlaying = !clock.isPlaying

if (isPlaying) {
enableNoSleep()
} else {
disableNoSleep()
}

setClock({
lastActionAt: new Date(),
// todo: recalculate at time of action
// instead of using Signal
lastTimeElapsedMs: getTimeElapsed(),
isPlaying,
})
clock.toggleIsPlaying(isPlaying)
}}
>
{clock.isPlaying ? <PauseIcon /> : <PlayIcon />}
Expand All @@ -237,7 +227,7 @@ export const Controls = observer(() => {
text={'0.1s'}
onClick={() => {
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: getTimeElapsed() + 100,
})
}}
Expand All @@ -248,7 +238,7 @@ export const Controls = observer(() => {
text={'1s'}
onClick={() => {
setClock({
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: getTimeElapsed() + 1000,
})
}}
Expand All @@ -267,7 +257,7 @@ export const Controls = observer(() => {
if (Number.isFinite(newPlaySpeed)) {
setClock({
playSpeed: newPlaySpeed,
lastActionAt: new Date(),
lastActionAt: Date.now(),
lastTimeElapsedMs: getTimeElapsed(),
})
}
Expand Down
22 changes: 12 additions & 10 deletions app/shared/file-display.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { getActiveNodes, getFile, getTimeElapsed } from './utils'
import { controlState, getActiveNodes, getFile, getTimeElapsed } from './utils'
import { Subtitle } from './subtitle'

export const FileDisplay = () => {
return (
<>
<div className="flex h-full flex-col justify-center pl-[env(safe-area-inset-left,0)] pr-[env(safe-area-inset-right,0)] text-center">
<div className="px-2">
<For each={() => getActiveNodes(getFile(), getTimeElapsed())}>
{(node) => <Subtitle key={node.id} node={node} />}
</For>
<Show when={() => !controlState.showTranscript}>
<div className="absolute left-0 right-0 top-[-100%] bottom-[-100%]">
<div className="flex h-full flex-col justify-center pl-[env(safe-area-inset-left,0)] pr-[env(safe-area-inset-right,0)] text-center">
<div className="px-2">
<For each={() => getActiveNodes(getFile(), getTimeElapsed())}>
{(node) => <Subtitle key={node.id} node={node} />}
</For>
</div>
{/* <div>{props.file?.name ?? 'unknown name'}</div> */}
{/* <div>{nodes()?.length ? `${nodes()?.length} lines` : 'no file'}</div> */}
</div>
{/* <div>{props.file?.name ?? 'unknown name'}</div> */}
{/* <div>{nodes()?.length ? `${nodes()?.length} lines` : 'no file'}</div> */}
</div>
</>
</Show>
)
}
17 changes: 17 additions & 0 deletions app/shared/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,20 @@ export const GoBackIcon = () => (
/>
</svg>
)

export const TranscriptIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
)
Loading

0 comments on commit 1d358e6

Please sign in to comment.