@@ -36,24 +36,45 @@ interface Repls {
36
36
list : APIRepl [ ] ;
37
37
}
38
38
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
+
39
64
export const Home = ( ) => {
40
65
const params = useParams ( ) ;
41
66
const context = useAppContext ( ) ! ;
42
67
const navigate = useNavigate ( ) ;
43
68
const location = useLocation ( ) ;
44
69
45
70
createEffect ( ( ) => {
46
- if ( ! location . hash && ( context . token || localStorage . getItem ( 'scratchpad' ) ) ) return ;
71
+ if ( ! location . hash && context . token ) return ;
47
72
48
73
const initialTabs = parseHash ( location . hash . slice ( 1 ) , defaultTabs ) ;
49
74
50
75
localStorage . setItem (
51
76
'scratchpad' ,
52
77
JSON . stringify ( {
53
- title : 'Scratchpad' ,
54
- public : true ,
55
- labels : [ ] ,
56
- version : '1.0' ,
57
78
files : initialTabs . map ( ( x ) => ( {
58
79
name : x . name + ( ( x as any ) . type ? `.${ ( x as any ) . type } ` : '' ) ,
59
80
content : x . source . split ( '\n' ) ,
@@ -92,37 +113,45 @@ export const Home = () => {
92
113
} }
93
114
/>
94
115
< 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 >
118
147
119
148
< 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" >
121
150
< thead >
122
151
< 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 >
126
155
</ tr >
127
156
</ thead >
128
157
< tbody >
@@ -153,8 +182,8 @@ export const Home = () => {
153
182
< td >
154
183
< a href = { `${ params . user || context . user ( ) ?. display } /${ repl . id } ` } > { repl . title } </ a >
155
184
</ 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" >
158
187
< Icon
159
188
path = { repl . public ? eye : eyeOff }
160
189
class = "w-6 inline m-2 ml-0 cursor-pointer"
0 commit comments