tangled
alpha
login
or
join now
yoten.app
/
yoten
17
fork
atom
Yōten: A social tracker for your language learning journey built on the atproto.
17
fork
atom
overview
issues
pulls
pipelines
feat: redesign study session component
brookjeynes.dev
6 months ago
c2713e96
f4dc1962
verified
This commit was signed with the committer's
known signature
.
brookjeynes.dev
SSH Key Fingerprint:
SHA256:N3n3PCBSiXfS6EHlmGdx+LMEruJMj6FS2hqaXyfsw0s=
+107
-109
3 changed files
expand all
collapse all
unified
split
input.css
internal
server
views
partials
reactions.templ
study-session.templ
+3
input.css
···
55
55
}
56
56
57
57
@utility pill {
58
58
+
display: flex;
59
59
+
justify-content: center;
60
60
+
align-items: center;
58
61
padding: theme(spacing.1) theme(spacing.4);
59
62
border-radius: theme(borderRadius.full);
60
63
font-size: theme(text.sm);
+2
-7
internal/server/views/partials/reactions.templ
···
42
42
}
43
43
<div class="inline-block text-left w-fit">
44
44
<button @click="open = !open" id="reaction-button" type="button" class="btn rounded-full hover:bg-bg py-1 px-2">
45
45
-
if !params.HasReactions {
46
46
-
<i class="w-4 h-4" data-lucide="plus"></i>
47
47
-
} else {
48
48
-
<i class="w-4 h-4" data-lucide="heart"></i>
49
49
-
<span class="font-normal">Be the first to react</span>
50
50
-
}
45
45
+
<i class="w-5 h-5" data-lucide="smile-plus"></i>
51
46
</button>
52
47
<div
53
48
x-show="open"
···
66
61
class="btn flex-col gap-1 hover:bg-primary-surface rounded-md"
67
62
hx-vals={ templ.JSONString(ReactionHxVals{ReactionID: reaction.ID}) }
68
63
hx-disabled-elt="#reaction-button"
64
64
+
title={ reaction.Label }
69
65
>
70
66
<span>{ reaction.Emoji }</span>
71
71
-
<span class="text-xs text-text-muted">{ reaction.Label }</span>
72
67
</button>
73
68
}
74
69
</div>
+102
-102
internal/server/views/partials/study-session.templ
···
31
31
}
32
32
}
33
33
34
34
+
templ studySessionAction(params StudySessionProps) {
35
35
+
<div class="flex justify-between sm:justify-normal gap-2 w-auto">
36
36
+
<details class="relative inline-block text-left">
37
37
+
<summary class="cursor-pointer list-none">
38
38
+
<div class="btn btn-muted p-2">
39
39
+
<i class="w-4 h-4 flex-shrink-0" data-lucide="ellipsis"></i>
40
40
+
</div>
41
41
+
</summary>
42
42
+
<div
43
43
+
class="absolute flex flex-col right-0 mt-2 p-1 gap-1 rounded w-32 bg-bg-light border border-bg-dark"
44
44
+
>
45
45
+
<button id="edit-button" type="button" class="w-full">
46
46
+
<a
47
47
+
href={ templ.URL(fmt.Sprintf("/session/edit/%s", params.StudySession.Rkey)) }
48
48
+
class="text-base text-text flex items-center px-4 py-2 text-sm hover:bg-bg gap-2"
49
49
+
>
50
50
+
<i class="w-4 h-4" data-lucide="square-pen"></i>
51
51
+
Edit
52
52
+
</a>
53
53
+
</button>
54
54
+
<button
55
55
+
class="text-base text-red-600 flex items-center px-4 py-2 text-sm hover:bg-bg gap-2 group"
56
56
+
type="button"
57
57
+
id="delete-button"
58
58
+
hx-disabled-elt="delete-button,#edit-button"
59
59
+
hx-delete={ templ.URL(fmt.Sprintf("/session/%s", params.StudySession.Rkey)) }
60
60
+
>
61
61
+
<i class="w-4 h-4" data-lucide="trash-2"></i>
62
62
+
Delete
63
63
+
<i class="w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" data-lucide="loader-circle"></i>
64
64
+
</button>
65
65
+
</div>
66
66
+
</details>
67
67
+
</div>
68
68
+
}
69
69
+
34
70
templ StudySession(params StudySessionProps) {
35
71
{{ elementId := SanitiseHtmlId(fmt.Sprintf("study-session-%s-%s", params.StudySession.Did, params.StudySession.Rkey)) }}
36
72
<div id={ elementId } class="card relative" x-data="{ open: false }" :class="{ 'z-20': open }">
37
73
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
38
38
-
<div class="flex items-center gap-3">
39
39
-
if params.StudySession.BskyProfile.Avatar == "" {
40
40
-
<div class="flex items-center justify-center w-10 h-10 rounded-full bg-primary">
41
41
-
<i class="w-7 h-7" data-lucide="user"></i>
42
42
-
</div>
43
43
-
} else {
44
44
-
<img src={ params.StudySession.BskyProfile.Avatar } class="w-10 h-10 rounded-full"/>
45
45
-
}
46
46
-
<div>
47
47
-
<div class="flex items-center gap-2">
48
48
-
<a href={ templ.URL(fmt.Sprintf("/@%s", params.StudySession.Did)) } class="font-semibold">
49
49
-
{ params.StudySession.ProfileDisplayName }
50
50
-
</a>
51
51
-
<p class="pill pill-secondary h-fit items-center justify-center gap-1 w-fit hidden sm:flex">
52
52
-
<i class="w-3.5 h-3.5" data-lucide="star"></i>
53
53
-
<span class="text-xs">Level { params.StudySession.ProfileLevel }</span>
54
54
-
</p>
74
74
+
<div class="flex items-center justify-between">
75
75
+
<div class="flex items-center gap-3">
76
76
+
if params.StudySession.BskyProfile.Avatar == "" {
77
77
+
<div class="flex items-center justify-center w-10 h-10 rounded-full bg-primary">
78
78
+
<i class="w-7 h-7" data-lucide="user"></i>
79
79
+
</div>
80
80
+
} else {
81
81
+
<img src={ params.StudySession.BskyProfile.Avatar } class="w-10 h-10 rounded-full"/>
82
82
+
}
83
83
+
<div>
84
84
+
<div class="flex items-center gap-2">
85
85
+
<a href={ templ.URL(fmt.Sprintf("/@%s", params.StudySession.Did)) } class="font-semibold">
86
86
+
{ params.StudySession.ProfileDisplayName }
87
87
+
</a>
88
88
+
<p class="pill pill-secondary px-2 py-0.5 h-fit items-center justify-center gap-1 w-fit flex">
89
89
+
<i class="w-3.5 h-3.5" data-lucide="star"></i>
90
90
+
<span class="text-xs">{ params.StudySession.ProfileLevel }</span>
91
91
+
</p>
92
92
+
</div>
93
93
+
<p class="text-text-muted text-sm">@{ params.StudySession.BskyProfile.Handle }</p>
55
94
</div>
56
56
-
<p class="text-text-muted text-sm">@{ params.StudySession.BskyProfile.Handle }</p>
57
95
</div>
58
58
-
</div>
59
59
-
if params.DoesOwn {
60
60
-
<div class="flex justify-between sm:justify-normal gap-2 w-auto">
61
61
-
<div class="pill pill-secondary flex items-center gap-1 mr-2 w-fit">
62
62
-
<i class="w-4 h-4" data-lucide="zap"></i>
63
63
-
<span class="font-medium text-sm">+{ params.StudySession.XpGained }</span>
96
96
+
if params.DoesOwn {
97
97
+
<div class="block sm:hidden">
98
98
+
@studySessionAction(params)
64
99
</div>
65
65
-
<details class="relative inline-block text-left">
66
66
-
<summary class="cursor-pointer list-none">
67
67
-
<div class="btn btn-muted p-2">
68
68
-
<i class="w-4 h-4 flex-shrink-0" data-lucide="ellipsis"></i>
69
69
-
</div>
70
70
-
</summary>
71
71
-
<div
72
72
-
class="absolute flex flex-col right-0 mt-2 p-1 gap-1 rounded w-32 bg-bg-light border border-bg-dark"
73
73
-
>
74
74
-
<button id="edit-button" type="button" class="w-full">
75
75
-
<a
76
76
-
href={ templ.URL(fmt.Sprintf("/session/edit/%s", params.StudySession.Rkey)) }
77
77
-
class="text-base text-text flex items-center px-4 py-2 text-sm hover:bg-bg gap-2"
78
78
-
>
79
79
-
<i class="w-4 h-4" data-lucide="square-pen"></i>
80
80
-
Edit
81
81
-
</a>
82
82
-
</button>
83
83
-
<button
84
84
-
class="text-base text-red-600 flex items-center px-4 py-2 text-sm hover:bg-bg gap-2 group"
85
85
-
type="button"
86
86
-
id="delete-button"
87
87
-
hx-disabled-elt="delete-button,#edit-button"
88
88
-
hx-delete={ templ.URL(fmt.Sprintf("/session/%s", params.StudySession.Rkey)) }
89
89
-
>
90
90
-
<i class="w-4 h-4" data-lucide="trash-2"></i>
91
91
-
Delete
92
92
-
<i class="w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" data-lucide="loader-circle"></i>
93
93
-
</button>
94
94
-
</div>
95
95
-
</details>
100
100
+
}
101
101
+
</div>
102
102
+
<div class="flex gap-4">
103
103
+
<div class="pill pill-primary w-fit">
104
104
+
{ params.StudySession.Language.Flag }
105
105
+
{ params.StudySession.Language.Name }
106
106
+
if params.StudySession.Language.NativeName != nil {
107
107
+
({ *params.StudySession.Language.NativeName })
108
108
+
}
96
109
</div>
97
97
-
}
98
98
-
</div>
99
99
-
<p class="pill pill-secondary h-fit flex items-center justify-center w-fit gap-1 sm:hidden">
100
100
-
<i class="w-3.5 h-3.5" data-lucide="star"></i>
101
101
-
<span class="text-xs">Level { params.StudySession.ProfileLevel }</span>
102
102
-
</p>
103
103
-
<div class="flex sm:items-center flex-col sm:flex-row gap-2">
104
104
-
<div class="pill pill-primary w-fit">
105
105
-
{ params.StudySession.Language.Flag }
106
106
-
{ params.StudySession.Language.Name }
107
107
-
if params.StudySession.Language.NativeName != nil {
108
108
-
({ *params.StudySession.Language.NativeName })
110
110
+
if params.DoesOwn {
111
111
+
<div class="hidden sm:block">
112
112
+
@studySessionAction(params)
113
113
+
</div>
109
114
}
110
115
</div>
111
111
-
<span class="text-text-muted hidden sm:block">•</span>
112
112
-
<span class="font-medium">{ params.StudySession.StudySession.Activity.Name }</span>
113
116
</div>
114
114
-
<div class="flex items-center gap-4 text-sm text-text-muted">
115
115
-
<div class="flex items-center gap-1">
116
116
-
<i class="w-4 h-4" data-lucide="clock"></i>
117
117
-
<span>{ params.StudySession.StudySession.Duration.String() }</span>
118
118
-
</div>
119
119
-
<div class="flex items-center gap-1">
120
120
-
<i class="w-4 h-4" data-lucide="calendar"></i>
121
121
-
<span>{ params.StudySession.StudySession.Date.Format("January 2, 2006") }</span>
122
122
-
</div>
123
123
-
</div>
117
117
+
<span class="font-medium">{ params.StudySession.StudySession.Activity.Name }</span>
124
118
if params.StudySession.StudySession.Description != "" {
125
119
<p class="whitespace-pre-wrap leading-relaxed">
126
120
{ params.StudySession.StudySession.Description }
127
121
</p>
128
122
}
129
123
if params.StudySession.Resource != nil {
130
130
-
<hr class="border-gray"/>
131
131
-
<div class="flex flex-col sm:flex-row sm:items-center gap-2 p-2 bg-gray-light rounded-md">
124
124
+
<a
125
125
+
rel="noopener noreferrer"
126
126
+
target="_blank"
127
127
+
href={ templ.URL(*params.StudySession.Resource.Link) }
128
128
+
aria-label={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
129
129
+
title={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
130
130
+
class="flex flex-col sm:flex-row sm:items-center gap-2 py-2 px-4 bg-gray-light hover:opacity-75 rounded-md"
131
131
+
>
132
132
<div class="flex items-center gap-2 flex-1 min-w-0">
133
133
<i class="w-4 h-4 flex-shrink-0" data-lucide={ getResourceIcon(params.StudySession.Resource.Type) }></i>
134
134
<span title={ params.StudySession.Resource.Description } class="font-medium text-sm truncate">{ params.StudySession.Resource.Title }</span>
···
137
137
{{ parsedURL, err := url.Parse(*params.StudySession.Resource.Link) }}
138
138
if err == nil {
139
139
{{ hostname := strings.TrimPrefix(parsedURL.Hostname(), "www.") }}
140
140
-
<div class="flex justify-between sm:items-center">
141
141
-
<div title={ *params.StudySession.Resource.Link } class="pill pill-primary">
142
142
-
{ hostname }
143
143
-
</div>
144
144
-
<a
145
145
-
class="btn w-fit"
146
146
-
rel="noopener noreferrer"
147
147
-
target="_blank"
148
148
-
href={ templ.URL(*params.StudySession.Resource.Link) }
149
149
-
aria-label={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
150
150
-
title={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
151
151
-
>
152
152
-
<i class="w-4 h-4" data-lucide="square-arrow-out-up-right"></i>
153
153
-
</a>
140
140
+
<div title={ *params.StudySession.Resource.Link }>
141
141
+
{ hostname }
154
142
</div>
155
143
}
156
144
}
157
157
-
</div>
145
145
+
</a>
158
146
}
159
147
<hr class="border-gray"/>
160
160
-
@NewReactions(NewReactionsProps{
161
161
-
User: params.User,
162
162
-
SessionDid: params.StudySession.Did,
163
163
-
SessionRkey: params.StudySession.Rkey,
164
164
-
ReactionEvents: params.StudySession.Reactions,
165
165
-
})
148
148
+
<div class="flex flex-col sm:flex-row justify-between sm:items-center gap-4">
149
149
+
@NewReactions(NewReactionsProps{
150
150
+
User: params.User,
151
151
+
SessionDid: params.StudySession.Did,
152
152
+
SessionRkey: params.StudySession.Rkey,
153
153
+
ReactionEvents: params.StudySession.Reactions,
154
154
+
})
155
155
+
<div class="flex flex-col sm:flex-row sm:items-center gap-2 text-sm text-text-muted">
156
156
+
<div class="flex items-center gap-1">
157
157
+
<i class="w-4 h-4" data-lucide="clock"></i>
158
158
+
<span>{ params.StudySession.StudySession.Duration.String() }</span>
159
159
+
</div>
160
160
+
<div class="flex items-center gap-1">
161
161
+
<i class="w-4 h-4" data-lucide="calendar"></i>
162
162
+
<span>{ params.StudySession.StudySession.Date.Format("January 2, 2006") }</span>
163
163
+
</div>
164
164
+
</div>
165
165
+
</div>
166
166
</div>
167
167
}