1- import { wrap } from "@suspensive/react" ;
2- import React from "react" ;
3- import Markdown from "react-markdown" ;
4- import * as R from "remeda" ;
5-
6- import { SloganShort } from "assets/icons" ;
7- import { FallbackImg } from "components/common/FallbackImg" ;
8- import Page from "components/common/Page" ;
9- import { APIPretalxSessions } from "models/api/session" ;
10- import { useNavigate , useParams } from "react-router" ;
11- import styled from "styled-components" ;
12- import { useRetrieveSessionQuery } from "utils/hooks/useAPI" ;
13- import useTranslation from "utils/hooks/useTranslation" ;
1+ import { wrap } from "@suspensive/react"
2+ import React from "react"
3+ import Markdown from "react-markdown"
4+ import * as R from "remeda"
5+
6+ import { SloganShort } from "assets/icons"
7+ import { FallbackImg } from "components/common/FallbackImg"
8+ import Page from "components/common/Page"
9+ import { APIPretalxSessions } from "models/api/session"
10+ import { useNavigate , useParams } from "react-router"
11+ import styled from "styled-components"
12+ import { useRetrieveSessionQuery } from "utils/hooks/useAPI"
13+ import useTranslation from "utils/hooks/useTranslation"
1414
1515const SessionSpeakerItem : React . FC < { speaker : APIPretalxSessions [ 0 ] [ "speakers" ] [ 0 ] } > = ( {
1616 speaker,
@@ -29,44 +29,52 @@ const SessionSpeakerItem: React.FC<{ speaker: APIPretalxSessions[0]["speakers"][
2929 < Markdown > { speaker . biography } </ Markdown >
3030 </ div >
3131 </ SessionSpeakerItemStyled >
32- ) ;
33- } ;
32+ )
33+ }
3434
3535const SessionDetail : React . FC < { session : APIPretalxSessions [ 0 ] } > = ( { session } ) => {
36- const t = useTranslation ( ) ;
36+ const t = useTranslation ( )
3737
38- let locale = "알 수 없음" ;
38+ let locale = "알 수 없음"
3939 switch ( session . content_locale ) {
4040 case "ko" :
41- locale = "한국어" ;
42- break ;
41+ locale = "한국어"
42+ break
4343 case "en" :
44- locale = "영어" ;
45- break ;
44+ locale = "영어"
45+ break
4646 case "ja" :
47- locale = "일본어" ;
48- break ;
47+ locale = "일본어"
48+ break
4949 default :
50- locale = "알 수 없음" ;
50+ locale = "알 수 없음"
51+ }
52+
53+ let datetime = t ( "알 수 없음" )
54+ let duration = session . duration || 0
55+ let room = t ( "알 수 없음" )
56+
57+ if ( R . isObjectType ( session . slot ) && R . isString ( session . slot . start ) && R . isString ( session . slot . end ) ) {
58+ const startTime = new Date ( session . slot . start )
59+ const endTime = new Date ( session . slot . end )
60+ const timeFormat = t ( "ko-KR" )
61+ const dateOptions = { day : "numeric" as const , month : "short" as const }
62+ const timeOptions = { hour : "numeric" as const , minute : "numeric" as const }
63+
64+ datetime = `${ startTime . toLocaleString ( timeFormat , { ...dateOptions , ...timeOptions } ) } ~ ${ endTime . toLocaleTimeString ( timeFormat , timeOptions ) } `
65+ duration = ( new Date ( session . slot . end ) . getTime ( ) - new Date ( session . slot . start ) . getTime ( ) ) / 1000 / 60
66+ room = t ( session . slot . room [ Object . keys ( session . slot . room ) [ 0 ] ] )
5167 }
5268
5369 return (
5470 < SessionDetailStyled >
5571 < h1 > { session . title } </ h1 >
56- < h4 > { session . abstract } </ h4 >
72+ < h4 > < Markdown > { session . abstract } </ Markdown > </ h4 >
5773 < hr />
5874 < SessionInfoContainerStyled >
59- < p >
60- { t ( "언어" ) } : { t ( locale ) }
61- </ p >
62- < p >
63- { t ( "발표 시간" ) } : { session . duration }
64- { t ( "분" ) }
65- </ p >
66- < p >
67- { t ( "발표 장소" ) } :{ " " }
68- { t ( session . slot ?. room [ Object . keys ( session . slot ?. room ?? { } ) [ 0 ] ] ?? "알 수 없음" ) }
69- </ p >
75+ < p > { `${ t ( "언어" ) } : ${ t ( locale ) } ` } </ p >
76+ < p > { `${ t ( "발표 시각" ) } : ${ datetime } (${ duration } ${ t ( "분" ) } )` } </ p >
77+ < p > { `${ t ( "발표 장소" ) } : ${ room } ` } </ p >
7078 < p >
7179 < TagContainer >
7280 < div style = { { margin : 0 } } > { t ( "태그" ) } :</ div >
@@ -87,29 +95,29 @@ const SessionDetail: React.FC<{ session: APIPretalxSessions[0] }> = ({ session }
8795 ) ) }
8896 < hr />
8997 </ SessionDetailStyled >
90- ) ;
91- } ;
98+ )
99+ }
92100
93101export const SessionDetailPage : React . FC = ( ) => {
94- const t = useTranslation ( ) ;
95- const { code } = useParams < { code : string } > ( ) ;
96- const navigate = useNavigate ( ) ;
102+ const t = useTranslation ( )
103+ const { code } = useParams < { code : string } > ( )
104+ const navigate = useNavigate ( )
97105
98- React . useEffect ( ( ) => window . scrollTo ( 0 , 0 ) , [ ] ) ;
106+ React . useEffect ( ( ) => window . scrollTo ( 0 , 0 ) , [ ] )
99107
100108 if ( ! ( R . isString ( code ) && ! R . isEmpty ( code ) ) ) {
101- navigate ( "/session" ) ;
102- return null ;
109+ navigate ( "/session" )
110+ return null
103111 }
104112
105113 const SessionDetailWrapper = wrap
106114 . ErrorBoundary ( { fallback : < h4 > { t ( "세션 정보를 불러오는 중 에러가 발생했습니다." ) } </ h4 > } )
107115 . Suspense ( { fallback : < h4 > { t ( "세션 정보를 불러오는 중 입니다." ) } </ h4 > } )
108116 . on ( ( ) => {
109117 // eslint-disable-next-line react-hooks/rules-of-hooks
110- const { data } = useRetrieveSessionQuery ( code ) ;
111- return < SessionDetail session = { data } /> ;
112- } ) ;
118+ const { data } = useRetrieveSessionQuery ( code )
119+ return < SessionDetail session = { data } />
120+ } )
113121
114122 return (
115123 < Page >
@@ -118,13 +126,13 @@ export const SessionDetailPage: React.FC = () => {
118126 </ ReturnToSessionList >
119127 < SessionDetailWrapper />
120128 </ Page >
121- ) ;
122- } ;
129+ )
130+ }
123131
124132const ReturnToSessionList = styled . small `
125133 color: rgba(255, 255, 255, 0.4);
126134 cursor: pointer;
127- ` ;
135+ `
128136
129137const SessionDetailStyled = styled . div `
130138 h1 {
@@ -146,13 +154,13 @@ const SessionDetailStyled = styled.div`
146154 font-size: 1rem;
147155 }
148156 }
149- ` ;
157+ `
150158
151159const SessionInfoContainerStyled = styled . div `
152160 * {
153161 margin: 0.5rem 0;
154162 }
155- ` ;
163+ `
156164
157165const SessionSpeakerItemStyled = styled . div `
158166 display: flex;
@@ -165,7 +173,7 @@ const SessionSpeakerItemStyled = styled.div`
165173 margin-top: 0;
166174 color: #fff;
167175 }
168- ` ;
176+ `
169177
170178const SessionSpeakerImageContainerStyled = styled . div `
171179 flex: 0 0 auto;
@@ -190,18 +198,18 @@ const SessionSpeakerImageContainerStyled = styled.div`
190198
191199 border-radius: 50%;
192200 }
193- ` ;
201+ `
194202
195203const TagContainer = styled . div `
196204 display: flex;
197205 align-items: center;
198206 justify-content: flex-start;
199207 gap: 0.5rem;
200- ` ;
208+ `
201209
202210const Tag = styled . kbd `
203211 margin: 0;
204212 background-color: #b0a8fe;
205213 font-family: var(--pico-font-family);
206214 font-size: 0.6rem;
207- ` ;
215+ `
0 commit comments