1
- import React from "react" ;
1
+ import { formatDistanceToNow } from "date-fns" ;
2
+ import {
3
+ collection ,
4
+ getDocs ,
5
+ getFirestore ,
6
+ query ,
7
+ where ,
8
+ } from "firebase/firestore/lite" ;
9
+ import React , { useContext , useEffect , useState } from "react" ;
2
10
import { Link } from "react-router-dom" ;
3
11
import styled from "styled-components" ;
12
+ import { AppContext } from "../../AppContext" ;
4
13
import { beautifySlug } from "../corpora/utils" ;
5
14
6
15
export const StyledButton = styled . button `
@@ -40,19 +49,37 @@ const StyledButtonLink = styled(Link)`
40
49
41
50
const LandingContainer = styled . div `
42
51
display: flex;
43
- flex-direction: column;
44
- align-items: center;
52
+ flex-direction: row;
45
53
padding: 50px 20px;
46
- margin-top: 30px; /* Add space below the header */
47
- min-height: calc(100vh - 30px); /* Subtract header height */
54
+ margin-top: 30px;
55
+ min-height: calc(100vh - 30px);
48
56
box-sizing: border-box;
49
57
` ;
50
58
59
+ // Two column layout
60
+ const LeftColumn = styled . div `
61
+ flex: 1;
62
+ margin-right: 20px;
63
+ max-width: 350px;
64
+ ` ;
65
+
66
+ const RightColumn = styled . div `
67
+ flex: 2;
68
+ display: flex;
69
+ flex-direction: column;
70
+ align-items: center;
71
+ ` ;
72
+
73
+ // Styling for the user's scores
74
+ const UserScoresContainer = styled . div `
75
+ width: 100%;
76
+ margin-bottom: 30px;
77
+ ` ;
78
+
51
79
// Styled components for the scores list
52
80
const ScoresContainer = styled . div `
53
81
width: 100%;
54
82
max-width: 600px;
55
- margin-top: 30px;
56
83
text-align: left;
57
84
` ;
58
85
@@ -68,22 +95,47 @@ const ScoresList = styled.ul`
68
95
` ;
69
96
70
97
const ScoreItem = styled . li `
71
- margin: 8px 0;
98
+ margin: 12px 0;
72
99
` ;
73
100
74
101
const ScoreLink = styled ( Link ) `
75
102
color: white;
76
103
text-decoration: none;
77
104
font-size: 16px;
78
- padding: 5px 0;
79
105
display: block;
80
106
81
107
&:hover {
82
108
text-decoration: underline;
83
109
}
84
110
` ;
85
111
112
+ const ScoreTimestamp = styled . span `
113
+ color: #888;
114
+ font-size: 12px;
115
+ display: block;
116
+ margin-top: 2px;
117
+ ` ;
118
+
119
+ const VersionBadge = styled . span `
120
+ display: inline-block;
121
+ background-color: transparent;
122
+ color: #888;
123
+ font-size: 11px;
124
+ margin-left: 8px;
125
+ ` ;
126
+
127
+ interface UserScore {
128
+ id : string ;
129
+ title : string ;
130
+ updatedAt : Date ;
131
+ versions : number ;
132
+ }
133
+
86
134
const EditorLandingPage : React . FC = ( ) => {
135
+ const [ userScores , setUserScores ] = useState < UserScore [ ] > ( [ ] ) ;
136
+ const appContext = useContext ( AppContext ) ;
137
+ const user = appContext ?. user ;
138
+
87
139
// Hard-coded list of featured scores
88
140
const featuredScores = [
89
141
"schubert_d365_09" ,
@@ -94,20 +146,84 @@ const EditorLandingPage: React.FC = () => {
94
146
"idea-n.10---gibran-alcocer" ,
95
147
] ;
96
148
149
+ // Fetch user scores when component mounts and user is available
150
+ useEffect ( ( ) => {
151
+ const fetchUserScores = async ( ) => {
152
+ if ( ! user ) {
153
+ setUserScores ( [ ] ) ;
154
+ return ;
155
+ }
156
+
157
+ try {
158
+ const db = getFirestore ( ) ;
159
+ const editsCollection = collection ( db , "edits" ) ;
160
+ const q = query ( editsCollection , where ( "owner" , "==" , user . uid ) ) ;
161
+ const querySnapshot = await getDocs ( q ) ;
162
+
163
+ const scores : UserScore [ ] = [ ] ;
164
+ querySnapshot . forEach ( ( doc ) => {
165
+ const data = doc . data ( ) ;
166
+ scores . push ( {
167
+ id : doc . id ,
168
+ title : data . title || "" ,
169
+ updatedAt : data . updatedAt ?. toDate ( ) || new Date ( ) ,
170
+ versions : data . versions ?. length || 0 ,
171
+ } ) ;
172
+ } ) ;
173
+
174
+ // Sort by most recently updated
175
+ scores . sort ( ( a , b ) => b . updatedAt . getTime ( ) - a . updatedAt . getTime ( ) ) ;
176
+ setUserScores ( scores ) ;
177
+ } catch ( error ) {
178
+ console . error ( "Error fetching user scores:" , error ) ;
179
+ }
180
+ } ;
181
+
182
+ fetchUserScores ( ) ;
183
+ } , [ user ] ) ;
184
+
97
185
return (
98
186
< LandingContainer >
99
- < StyledButtonLink to = "/e/new" > New Score</ StyledButtonLink >
100
-
101
- < ScoresContainer >
102
- < ScoresTitle > Featured Scores</ ScoresTitle >
103
- < ScoresList >
104
- { featuredScores . map ( ( name ) => (
105
- < ScoreItem key = { name } >
106
- < ScoreLink to = { `/e/${ name } ` } > { beautifySlug ( name ) } </ ScoreLink >
107
- </ ScoreItem >
108
- ) ) }
109
- </ ScoresList >
110
- </ ScoresContainer >
187
+ < LeftColumn >
188
+ { user && (
189
+ < UserScoresContainer >
190
+ < ScoresTitle > My Scores</ ScoresTitle >
191
+ < ScoresList >
192
+ { userScores . length > 0 ? (
193
+ userScores . map ( ( score ) => (
194
+ < ScoreItem key = { score . id } >
195
+ < ScoreLink to = { `/ef/${ score . id } /${ score . versions } ` } >
196
+ { score . title || score . id }
197
+ < VersionBadge > v{ score . versions } </ VersionBadge >
198
+ </ ScoreLink >
199
+ < ScoreTimestamp >
200
+ { formatDistanceToNow ( score . updatedAt , {
201
+ addSuffix : true ,
202
+ } ) }
203
+ </ ScoreTimestamp >
204
+ </ ScoreItem >
205
+ ) )
206
+ ) : (
207
+ < ScoreItem > No saved scores yet</ ScoreItem >
208
+ ) }
209
+ </ ScoresList >
210
+ </ UserScoresContainer >
211
+ ) }
212
+ < StyledButtonLink to = "/e/new" > New Score</ StyledButtonLink >
213
+ </ LeftColumn >
214
+
215
+ < RightColumn >
216
+ < ScoresContainer >
217
+ < ScoresTitle > Examples</ ScoresTitle >
218
+ < ScoresList >
219
+ { featuredScores . map ( ( name ) => (
220
+ < ScoreItem key = { name } >
221
+ < ScoreLink to = { `/e/${ name } ` } > { beautifySlug ( name ) } </ ScoreLink >
222
+ </ ScoreItem >
223
+ ) ) }
224
+ </ ScoresList >
225
+ </ ScoresContainer >
226
+ </ RightColumn >
111
227
</ LandingContainer >
112
228
) ;
113
229
} ;
0 commit comments