Yōten: A social tracker for your language learning journey built on the atproto.
at master 176 lines 6.3 kB view raw
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">&commat;{ 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}