Yōten: A social tracker for your language learning journey built on the atproto.
1package partials
2
3import (
4 "fmt"
5 "net/url"
6 "strings"
7
8 "yoten.app/internal/db"
9)
10
11func getResourceIcon(resourceType db.ResourceType) string {
12 switch resourceType {
13 case db.Book:
14 return "book"
15 case db.Video:
16 return "video"
17 case db.Website:
18 return "globe"
19 case db.Course:
20 return "monitor"
21 case db.App:
22 return "smartphone"
23 case db.Podcast:
24 return "podcast"
25 case db.Article:
26 return "file-text"
27 case db.Other:
28 return "file"
29 default:
30 return "file"
31 }
32}
33
34templ studySessionAction(params StudySessionProps) {
35 <div class="flex justify-between sm:justify-normal gap-2 w-auto">
36 <details class="relative inline-block text-left">
37 <summary class="cursor-pointer list-none">
38 <div class="btn btn-muted p-2">
39 <i class="w-4 h-4 flex-shrink-0" data-lucide="ellipsis"></i>
40 </div>
41 </summary>
42 <div
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 >
45 <button id="edit-button" type="button" class="w-full">
46 <a
47 href={ templ.URL(fmt.Sprintf("/session/edit/%s", params.StudySession.Rkey)) }
48 class="text-base text-text flex items-center px-4 py-2 text-sm hover:bg-bg gap-2"
49 >
50 <i class="w-4 h-4" data-lucide="square-pen"></i>
51 Edit
52 </a>
53 </button>
54 <button
55 class="text-base text-red-600 flex items-center px-4 py-2 text-sm hover:bg-bg gap-2 group"
56 type="button"
57 id="delete-button"
58 hx-disabled-elt="#delete-button,#edit-button"
59 hx-delete={ templ.URL(fmt.Sprintf("/session/%s", params.StudySession.Rkey)) }
60 >
61 <i class="w-4 h-4" data-lucide="trash-2"></i>
62 Delete
63 <i class="w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" data-lucide="loader-circle"></i>
64 </button>
65 </div>
66 </details>
67 </div>
68}
69
70templ StudySession(params StudySessionProps) {
71 {{ elementId := SanitiseHtmlId(fmt.Sprintf("study-session-%s-%s", params.StudySession.Did, params.StudySession.Rkey)) }}
72 {{ studySessionUrl := templ.SafeURL("/" + params.StudySession.Did + "/session/" + params.StudySession.Rkey) }}
73 <div id={ elementId } class="card relative" x-data="{ open: false }" :class="{ 'z-20': open }">
74 <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
75 <div class="flex items-center justify-between">
76 <div class="flex items-center gap-3">
77 if params.StudySession.BskyProfile.Avatar == "" {
78 <div class="flex items-center justify-center w-10 h-10 rounded-full bg-primary">
79 <i class="w-7 h-7" data-lucide="user"></i>
80 </div>
81 } else {
82 <img src={ params.StudySession.BskyProfile.Avatar } class="w-10 h-10 rounded-full"/>
83 }
84 <div>
85 <div class="flex items-center gap-2">
86 <a href={ templ.URL(fmt.Sprintf("/%s", params.StudySession.Did)) } class="font-semibold">
87 { params.StudySession.ProfileDisplayName }
88 </a>
89 <p class="pill pill-secondary px-2 py-0.5 h-fit items-center justify-center gap-1 w-fit flex">
90 <i class="w-3.5 h-3.5" data-lucide="star"></i>
91 <span class="text-xs">{ params.StudySession.ProfileLevel }</span>
92 </p>
93 </div>
94 <p class="text-text-muted text-sm">@{ params.StudySession.BskyProfile.Handle }</p>
95 </div>
96 </div>
97 if params.DoesOwn {
98 <div class="block sm:hidden">
99 @studySessionAction(params)
100 </div>
101 }
102 </div>
103 <div class="flex gap-4">
104 <div class="pill pill-primary w-fit">
105 { params.StudySession.Language.Flag }
106 { params.StudySession.Language.Name }
107 if params.StudySession.Language.NativeName != nil {
108 ({ *params.StudySession.Language.NativeName })
109 }
110 </div>
111 if params.DoesOwn {
112 <div class="hidden sm:block">
113 @studySessionAction(params)
114 </div>
115 }
116 </div>
117 </div>
118 <span class="font-medium">{ params.StudySession.StudySession.Activity.Name }</span>
119 if params.StudySession.StudySession.Description != "" {
120 <p class="whitespace-pre-wrap leading-relaxed">
121 { params.StudySession.StudySession.Description }
122 </p>
123 }
124 if params.StudySession.Resource != nil {
125 <a
126 rel="noopener noreferrer"
127 target="_blank"
128 if params.StudySession.Resource.Link != nil {
129 href={ templ.URL(*params.StudySession.Resource.Link) }
130 aria-label={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
131 title={ fmt.Sprintf("Visit %s", *params.StudySession.Resource.Link) }
132 }
133 class="flex flex-col sm:flex-row sm:items-center gap-2 py-2 px-4 bg-gray-light hover:opacity-75 rounded-md"
134 >
135 <div class="flex items-center gap-2 flex-1 min-w-0">
136 <i class="w-4 h-4 flex-shrink-0" data-lucide={ getResourceIcon(params.StudySession.Resource.Type) }></i>
137 <span title={ params.StudySession.Resource.Description } class="font-medium text-sm truncate">{ params.StudySession.Resource.Title }</span>
138 </div>
139 if params.StudySession.Resource.Link != nil {
140 {{ parsedURL, err := url.Parse(*params.StudySession.Resource.Link) }}
141 if err == nil {
142 {{ hostname := strings.TrimPrefix(parsedURL.Hostname(), "www.") }}
143 <div title={ *params.StudySession.Resource.Link }>
144 { hostname }
145 </div>
146 }
147 }
148 </a>
149 }
150 <hr class="border-gray"/>
151 <div class="flex flex-col sm:flex-row justify-between sm:items-center gap-4">
152 <div class="flex items-center gap-1">
153 @NewReactions(NewReactionsProps{
154 User: params.User,
155 SessionDid: params.StudySession.Did,
156 SessionRkey: params.StudySession.Rkey,
157 ReactionEvents: params.StudySession.Reactions,
158 })
159 <a class="flex items-center gap-1" href={ studySessionUrl } title="comments">
160 <i class="w-5 h-5" data-lucide="message-square-share"></i>
161 <span>{ params.StudySession.CommentCount }</span>
162 </a>
163 </div>
164 <div class="flex flex-col sm:flex-row sm:items-center gap-2 text-sm text-text-muted">
165 <div class="flex items-center gap-1">
166 <i class="w-4 h-4" data-lucide="clock"></i>
167 <span>{ FormatDuration(params.StudySession.StudySession.Duration) }</span>
168 </div>
169 <div class="flex items-center gap-1">
170 <i class="w-4 h-4" data-lucide="calendar"></i>
171 <span>{ params.StudySession.StudySession.Date.Format("January 2, 2006") }</span>
172 </div>
173 </div>
174 </div>
175 </div>
176}