11import { useAtomSet , useAtomValue } from "@effect/atom-react"
2- import { Option } from "effect"
3- import { AsyncResult } from "effect/unstable/reactivity"
2+ import { Match } from "effect"
43import { useCallback , useEffect , useState } from "react"
54import { ExamplePage } from "../../components/ExamplePage.tsx"
6- import { mediaPlayerAtom } from "./atoms.ts"
7- import { initialAudioSettings , initialPlaybackData , MediaPlayerEvent , MediaPlayerStates } from "./schemas.ts"
5+ import { mediaPlayerAtom , mediaPlayerViewAtom , registerMediaPlayerElement } from "./atoms.ts"
6+ import { MediaPlayerEvent } from "./schemas.ts"
87
98interface AudioSource {
109 readonly name : string
@@ -20,17 +19,18 @@ const formatTime = (seconds: number): string => {
2019}
2120
2221export function MediaPlayerPage ( ) {
23- const snapshotResult = useAtomValue ( mediaPlayerAtom . snapshot )
22+ const viewResult = useAtomValue ( mediaPlayerViewAtom )
2423 const send = useAtomSet ( mediaPlayerAtom . send )
24+ const register = useAtomSet ( registerMediaPlayerElement )
2525 const [ source , setSource ] = useState < AudioSource > ( )
2626
2727 const registerAudioElement = useCallback (
2828 ( audioRef : HTMLAudioElement | null ) => {
2929 if ( audioRef !== null ) {
30- send ( MediaPlayerEvent . cases . AudioElementMounted . make ( { audioRef } ) )
30+ register ( audioRef )
3131 }
3232 } ,
33- [ send ]
33+ [ register ]
3434 )
3535
3636 useEffect (
@@ -46,67 +46,13 @@ export function MediaPlayerPage() {
4646 summary = "Coordinate a compound transport lifecycle and independent sound modes inside a parallel Effect statechart."
4747 machineFile = "src/examples/media-player/machine.ts"
4848 >
49- { AsyncResult . isInitial ( snapshotResult ) ?
50- < div className = "media-player-message" > Starting the media player machine…</ div > :
51- AsyncResult . isFailure ( snapshotResult ) ?
52- < div className = "media-player-message is-error" > The media player machine failed to start.</ div > :
53- (
54- ( ( ) => {
55- const runtimeSnapshot = snapshotResult . value
56- const snapshot = runtimeSnapshot . state
57- const paused = Option . getOrUndefined (
58- MediaPlayerStates . get ( snapshot , "Player.transport.Ready.Paused" )
59- )
60- const playing = Option . getOrUndefined (
61- MediaPlayerStates . get ( snapshot , "Player.transport.Ready.Playing" )
62- )
63- const buffering = Option . getOrUndefined (
64- MediaPlayerStates . get ( snapshot , "Player.transport.Ready.Buffering" )
65- )
66- const restarting = Option . getOrUndefined (
67- MediaPlayerStates . get ( snapshot , "Player.transport.Ready.Restarting" )
68- )
69- const ended = Option . getOrUndefined (
70- MediaPlayerStates . get ( snapshot , "Player.transport.Ready.Ended" )
71- )
72- const failed = Option . getOrUndefined (
73- MediaPlayerStates . get ( snapshot , "Player.transport.Failed" )
74- )
75- const audible = Option . getOrUndefined (
76- MediaPlayerStates . get ( snapshot , "Player.settings.Audible" )
77- )
78- const muted = Option . getOrUndefined (
79- MediaPlayerStates . get ( snapshot , "Player.settings.Muted" )
80- )
81- const playback = paused ?? playing ?? buffering ?? restarting ?? ended
82- const playbackData = playback ?? initialPlaybackData
83- const settings = audible ?? muted ?? initialAudioSettings
84- const isMuted = muted !== undefined
85- const isPaused = paused !== undefined
86- const isPlaying = playing !== undefined
87- const isBuffering = buffering !== undefined
88- const isEnded = ended !== undefined
89- const canPlay = isPaused || isEnded
90- const canPause = isPlaying || isBuffering
91- const canRestart = canPlay || canPause
92- const transportPath = isPlaying ?
93- "Player.transport.Ready.Playing"
94- : isBuffering ?
95- "Player.transport.Ready.Buffering"
96- : restarting !== undefined ?
97- "Player.transport.Ready.Restarting"
98- : isEnded ?
99- "Player.transport.Ready.Ended"
100- : isPaused ?
101- "Player.transport.Ready.Paused"
102- : failed !== undefined ?
103- "Player.transport.Failed"
104- : MediaPlayerStates . matches ( snapshot , "Player.transport.Loading" ) ?
105- "Player.transport.Loading"
106- : "Player.transport.Empty"
107- const stateName = transportPath . slice ( transportPath . lastIndexOf ( "." ) + 1 )
108- const settingsPath = isMuted ? "Player.settings.Muted" : "Player.settings.Audible"
109- const loudness = playing ?. loudness ?? null
49+ { Match . value ( viewResult ) . pipe (
50+ Match . tagsExhaustive ( {
51+ Initial : ( ) => < div className = "media-player-message" > Starting the media player machine…</ div > ,
52+ Failure : ( ) => < div className = "media-player-message is-error" > The media player machine failed to start.</ div > ,
53+ Success : ( { value : view } ) => {
54+ const { settings, status, transport } = view
55+ const { canPause, canPlay, canRestart, isPlaying, loudness, playback } = transport
11056 const loudnessLevel = Math . min ( 100 , Math . round ( ( loudness ?. rms ?? 0 ) * 220 ) )
11157 const peakLevel = Math . min ( 100 , Math . round ( ( loudness ?. peak ?? 0 ) * 100 ) )
11258
@@ -118,7 +64,7 @@ export function MediaPlayerPage() {
11864 < h2 > { source ?. name ?? "Choose a local audio track" } </ h2 >
11965 </ div >
12066 < div className = "media-player-toolbar-actions" >
121- < span className = { `media-state-chip is-${ stateName . toLowerCase ( ) } ` } > { stateName } </ span >
67+ < span className = { `media-state-chip is-${ transport . name . toLowerCase ( ) } ` } > { transport . name } </ span >
12268 < label className = "media-file-button" >
12369 < span > { source === undefined ? "Choose audio" : "Replace audio" } </ span >
12470 < input
@@ -163,7 +109,7 @@ export function MediaPlayerPage() {
163109
164110 < div className = "media-time-row" >
165111 < span > Current time</ span >
166- < strong > { formatTime ( playbackData . currentTime ) } </ strong >
112+ < strong > { formatTime ( playback . currentTime ) } </ strong >
167113 </ div >
168114
169115 < div className = "media-controls" >
@@ -207,7 +153,7 @@ export function MediaPlayerPage() {
207153 < section className = "media-settings" >
208154 < div className = "media-panel-heading" >
209155 < span > Audio settings</ span >
210- < strong > { isMuted ? "Muted" : "Audible" } · { Math . round ( settings . volume * 100 ) } %</ strong >
156+ < strong > { settings . muted ? "Muted" : "Audible" } · { Math . round ( settings . volume * 100 ) } %</ strong >
211157 </ div >
212158 < label >
213159 < span > Volume</ span >
@@ -227,7 +173,7 @@ export function MediaPlayerPage() {
227173 < span > Muted</ span >
228174 < input
229175 type = "checkbox"
230- checked = { isMuted }
176+ checked = { settings . muted }
231177 onChange = { ( { currentTarget } ) =>
232178 send (
233179 currentTarget . checked
@@ -257,30 +203,31 @@ export function MediaPlayerPage() {
257203 < section className = "media-state-preview" >
258204 < div className = "media-panel-heading" >
259205 < span > Machine snapshot</ span >
260- < strong > { runtimeSnapshot . status } </ strong >
206+ < strong > { status } </ strong >
261207 </ div >
262208 < pre >
263209 { JSON . stringify ( {
264- status : runtimeSnapshot . status ,
210+ status,
265211 states : {
266- transport : transportPath ,
267- settings : settingsPath
212+ transport : transport . path ,
213+ settings : settings . path
268214 } ,
269215 data : {
270- transport : playback ?? failed ?? null ,
271- settings
216+ transport : transport . value ,
217+ settings : settings . value
272218 }
273219 } , null , 2 ) }
274220 </ pre >
275221 </ section >
276222
277- { failed !== undefined && < p className = "media-error-message" role = "alert" > { failed . message } </ p > }
223+ { transport . error !== null && < p className = "media-error-message" role = "alert" > { transport . error } </ p > }
278224 </ aside >
279225 </ div >
280226 </ div >
281227 )
282- } ) ( )
283- ) }
228+ }
229+ } )
230+ ) }
284231 </ ExamplePage >
285232 )
286233}
0 commit comments