@@ -36,24 +36,45 @@ interface Repls {
3636 list : APIRepl [ ] ;
3737}
3838
39+ const formatter = new Intl . RelativeTimeFormat ( 'en' ) ;
40+ const timeAgo = ( ms : number ) : string => {
41+ const sec = Math . round ( ms / 1000 ) ;
42+ const min = Math . round ( sec / 60 ) ;
43+ const hr = Math . round ( min / 60 ) ;
44+ const day = Math . round ( hr / 24 ) ;
45+ const month = Math . round ( day / 30 ) ;
46+ const year = Math . round ( month / 12 ) ;
47+ if ( sec < 10 ) {
48+ return 'just now' ;
49+ } else if ( sec < 45 ) {
50+ return formatter . format ( - sec , 'second' ) ;
51+ } else if ( sec < 90 || min < 45 ) {
52+ return formatter . format ( - min , 'minute' ) ;
53+ } else if ( min < 90 || hr < 24 ) {
54+ return formatter . format ( - hr , 'hour' ) ;
55+ } else if ( hr < 36 || day < 30 ) {
56+ return formatter . format ( - day , 'day' ) ;
57+ } else if ( month < 18 ) {
58+ return formatter . format ( - month , 'month' ) ;
59+ } else {
60+ return formatter . format ( - year , 'year' ) ;
61+ }
62+ } ;
63+
3964export const Home = ( ) => {
4065 const params = useParams ( ) ;
4166 const context = useAppContext ( ) ! ;
4267 const navigate = useNavigate ( ) ;
4368 const location = useLocation ( ) ;
4469
4570 createEffect ( ( ) => {
46- if ( ! location . hash && ( context . token || localStorage . getItem ( 'scratchpad' ) ) ) return ;
71+ if ( ! location . hash && context . token ) return ;
4772
4873 const initialTabs = parseHash ( location . hash . slice ( 1 ) , defaultTabs ) ;
4974
5075 localStorage . setItem (
5176 'scratchpad' ,
5277 JSON . stringify ( {
53- title : 'Scratchpad' ,
54- public : true ,
55- labels : [ ] ,
56- version : '1.0' ,
5778 files : initialTabs . map ( ( x ) => ( {
5879 name : x . name + ( ( x as any ) . type ? `.${ ( x as any ) . type } ` : '' ) ,
5980 content : x . source . split ( '\n' ) ,
@@ -92,37 +113,45 @@ export const Home = () => {
92113 } }
93114 />
94115 < div class = "m-8" >
95- < button
96- class = "bg-solid-lightgray shadow-md dark:bg-solid-darkLighterBg rounded-xl p-4 text-3xl flex mx-auto"
97- onClick = { async ( ) => {
98- const result = await fetch ( `${ API } /repl` , {
99- method : 'POST' ,
100- headers : {
101- 'authorization' : `Bearer ${ context . token } ` ,
102- 'Content-Type' : 'application/json' ,
103- } ,
104- body : JSON . stringify ( {
105- title : 'Counter Example' ,
106- public : true ,
107- labels : [ ] ,
108- version : '1.0' ,
109- files : defaultTabs . map ( ( x ) => ( { name : x . name , content : x . source . split ( '\n' ) } ) ) ,
110- } ) ,
111- } ) ;
112- const { id } = await result . json ( ) ;
113- navigate ( `/${ context . user ( ) ?. display } /${ id } ` ) ;
114- } }
115- >
116- < Icon path = { plus } class = "w-8" /> Create new REPL
117- </ button >
116+ < div class = "flex flex-col align-middle" >
117+ < button
118+ class = "bg-solid-lightgray shadow-md dark:bg-solid-darkLighterBg rounded-xl p-4 text-3xl flex mx-auto"
119+ onClick = { async ( ) => {
120+ const result = await fetch ( `${ API } /repl` , {
121+ method : 'POST' ,
122+ headers : {
123+ 'authorization' : `Bearer ${ context . token } ` ,
124+ 'Content-Type' : 'application/json' ,
125+ } ,
126+ body : JSON . stringify ( {
127+ title : 'Counter Example' ,
128+ public : true ,
129+ labels : [ ] ,
130+ version : '1.0' ,
131+ files : defaultTabs . map ( ( x ) => ( { name : x . name , content : x . source . split ( '\n' ) } ) ) ,
132+ } ) ,
133+ } ) ;
134+ const { id } = await result . json ( ) ;
135+ navigate ( `/${ context . user ( ) ?. display } /${ id } ` ) ;
136+ } }
137+ >
138+ < Icon path = { plus } class = "w-8" /> Create new REPL
139+ </ button >
140+ < p class = "text-center text-gray-300 text-sm" >
141+ Or{ ' ' }
142+ < a href = "/scratchpad" class = "hover:underline" >
143+ open my scratchpad
144+ </ a >
145+ </ p >
146+ </ div >
118147
119148 < h1 class = "text-center text-3xl mb-4 mt-16" > { params . user || 'My' } Repls</ h1 >
120- < table class = "w-128 mx-auto" >
149+ < table class = "w-200 max-w-full mx-auto" >
121150 < thead >
122151 < tr class = "border-b border-neutral-600" >
123- < td > Title</ td >
124- < td > Edited</ td >
125- < td > Options</ td >
152+ < td class = "w-6/10" > Title</ td >
153+ < td class = "w-32" > Edited</ td >
154+ < td class = "text-right w-20" > Options</ td >
126155 </ tr >
127156 </ thead >
128157 < tbody >
@@ -153,8 +182,8 @@ export const Home = () => {
153182 < td >
154183 < a href = { `${ params . user || context . user ( ) ?. display } /${ repl . id } ` } > { repl . title } </ a >
155184 </ td >
156- < td > { new Date ( repl . created_at ) . toLocaleString ( ) } </ td >
157- < td >
185+ < td > { timeAgo ( Date . now ( ) - new Date ( repl . updated_at || repl . created_at ) . getTime ( ) ) } </ td >
186+ < td class = "text-right" >
158187 < Icon
159188 path = { repl . public ? eye : eyeOff }
160189 class = "w-6 inline m-2 ml-0 cursor-pointer"
0 commit comments