profile.svelte (13406B)
1 <script lang="ts"> 2 import { ImagePath, NavigationTabs, SelectMenu } from "$lib"; 3 import ButtonRoundNav from "$lib/components/button/button-round-nav.svelte"; 4 import FloatPage from "$lib/components/lib/float-page.svelte"; 5 import ImageUploadPhotoAdd from "$lib/components/media/image-upload-photo-add.svelte"; 6 import type { LibContext } from "$lib/types/context"; 7 import type { IViewBasis } from "$lib/types/views"; 8 import type { 9 IViewProfileData, 10 ViewProfileEditFieldKey, 11 } from "$lib/types/views/profile"; 12 import { idb_kv_init_page } from "$lib/utils/keyval"; 13 import { 14 get_context, 15 Glyph, 16 SYMBOLS, 17 type IViewOnDestroy, 18 } from "@radroots/apps-lib"; 19 import { 20 handle_err, 21 type CallbackPromise, 22 type CallbackPromiseGeneric, 23 } from "@radroots/utils"; 24 import { onDestroy, onMount } from "svelte"; 25 26 const { ls } = get_context<LibContext>(`lib`); 27 28 let { 29 basis, 30 photo_path = $bindable(``), 31 }: { 32 basis: IViewBasis<{ 33 data?: IViewProfileData; 34 loading_photo_upload: boolean; 35 loading_photo_upload_open: boolean; 36 on_handle_back: CallbackPromiseGeneric<{ 37 is_photo_existing: boolean; 38 }>; 39 on_handle_photo_options: CallbackPromise; 40 on_handle_edit_profile_field: CallbackPromiseGeneric<{ 41 field: ViewProfileEditFieldKey; 42 }>; 43 }> & 44 IViewOnDestroy<{ public_key: string }>; 45 photo_path: string; 46 } = $props(); 47 48 type ViewDisplay = `photos` | `following` | `followers`; 49 let view_display: ViewDisplay = $state(`photos`); 50 51 let val_sel_options_button = $state(``); 52 53 $effect(() => { 54 console.log(JSON.stringify(basis.data, null, 4), `data`); 55 }); 56 57 onMount(async () => { 58 try { 59 if (!basis.kv_init_prevent) await idb_kv_init_page(); 60 } catch (e) { 61 handle_err(e, `on_mount`); 62 } 63 }); 64 65 onDestroy(async () => { 66 try { 67 if (basis.data?.profile.public_key) 68 await basis.on_destroy({ 69 public_key: basis.data.profile.public_key, 70 }); 71 } catch (e) { 72 handle_err(e, `on_destroy`); 73 } 74 }); 75 76 const photo_overlay_visible = $derived( 77 !!(basis.data?.profile.picture || photo_path), 78 ); 79 80 const classes_photo_overlay_glyph = $derived( 81 photo_overlay_visible ? `text-white` : `text-ly0-gl`, 82 ); 83 84 const classes_photo_overlay_glyph_opt = $derived( 85 photo_overlay_visible ? `text-gray-300` : `text-ly0-gl`, 86 ); 87 88 const classes_photo_overlay_glyph_opt_selected = $derived( 89 photo_overlay_visible ? `text-white` : `text-ly1-gl_d`, 90 ); 91 </script> 92 93 {#if basis.data} 94 <div 95 class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-ly2 fade-in`} 96 > 97 <FloatPage 98 basis={{ 99 posx: `left`, 100 }} 101 > 102 <ButtonRoundNav 103 basis={{ 104 glyph: `arrow-left`, 105 loading: basis.loading_photo_upload, 106 callback: async () => { 107 await basis.on_handle_back({ 108 is_photo_existing: photo_overlay_visible, 109 }); 110 }, 111 }} 112 /> 113 </FloatPage> 114 {#if photo_path} 115 <button 116 class={`absolute top-12 flex flex-row h-7 px-6 justify-center items-center bg-ly1 active:bg-ly1-a rounded-full`} 117 onclick={async () => {}} 118 > 119 <p class={`font-sans font-[400] text-sm text-ly0-gl`}> 120 {`Post Photo`} 121 </p> 122 </button> 123 {/if} 124 <FloatPage 125 basis={{ 126 posx: `right`, 127 }} 128 > 129 <SelectMenu 130 bind:value={val_sel_options_button} 131 basis={{ 132 layer: 0, 133 options: [ 134 { 135 entries: [ 136 { 137 value: `*add-new`, 138 label: `Add new photo`, 139 }, 140 ], 141 }, 142 ], 143 }} 144 > 145 <ButtonRoundNav 146 basis={{ 147 glyph: `images-square`, 148 callback: basis.on_handle_photo_options, 149 }} 150 /> 151 </SelectMenu> 152 </FloatPage> 153 {#if basis.data.profile.picture || photo_path} 154 {@const img_path = photo_path || basis.data.profile.picture || ``} 155 <ImagePath basis={{ path: img_path }} /> 156 {:else} 157 <div 158 class={`flex flex-row justify-start items-center -translate-y-8`} 159 > 160 <ImageUploadPhotoAdd 161 bind:photo_path 162 basis={{ 163 loading: basis.loading_photo_upload_open, 164 }} 165 /> 166 </div> 167 {/if} 168 <div 169 class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`} 170 > 171 <div 172 class={`flex flex-col w-full gap-[2px] justify-center items-center`} 173 > 174 <div 175 class={`flex flex-row h-10 w-full justify-start items-center`} 176 > 177 <button 178 class={`group flex flex-row justify-center items-center`} 179 onclick={async () => { 180 await basis.on_handle_edit_profile_field({ 181 field: `display_name`, 182 }); 183 }} 184 > 185 <p 186 class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${ 187 basis.data?.profile.name 188 ? `` 189 : `capitalize opacity-active` 190 } el-re`} 191 > 192 {#if basis.data?.profile.display_name} 193 {`${basis.data?.profile.display_name}`} 194 {:else if basis.data?.profile.name} 195 {`${ 196 basis.data?.profile.display_name || 197 basis.data?.profile.name || 198 `` 199 }`} 200 {:else} 201 {`+ ${`${$ls(`icu.add_*`, { 202 value: `${$ls(`common.profile_name`)}`, 203 })}`}`} 204 {/if} 205 </p> 206 </button> 207 </div> 208 <div 209 class={`flex flex-row w-full gap-[6px] justify-start items-center`} 210 > 211 <button 212 class={`group flex flex-row justify-center items-center`} 213 onclick={async () => { 214 await basis.on_handle_edit_profile_field({ 215 field: `name`, 216 }); 217 }} 218 > 219 <p 220 class={`font-sansd font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${ 221 basis.data?.profile.name 222 ? `` 223 : `capitalize opacity-active` 224 } el-re`} 225 > 226 {#if basis.data?.profile.name} 227 {`@${basis.data.profile.name}`} 228 {:else} 229 {`+ ${`${$ls(`icu.add_*`, { 230 value: `${$ls(`common.username`)}`, 231 })}`}`} 232 {/if} 233 </p> 234 </button> 235 <p 236 class={`font-sans font-[400] ${classes_photo_overlay_glyph}`} 237 > 238 {SYMBOLS.bullet} 239 </p> 240 <button 241 class={`flex flex-row justify-center items-center`} 242 onclick={async () => { 243 alert(`@todo!`); 244 }} 245 > 246 <Glyph 247 basis={{ 248 classes: `${classes_photo_overlay_glyph}`, 249 dim: `xs`, 250 251 key: `link-simple`, 252 }} 253 /> 254 </button> 255 </div> 256 <div class={`flex flex-row w-full justify-start items-center`}> 257 <button 258 class={`group flex flex-row justify-center items-center`} 259 onclick={async () => { 260 await basis.on_handle_edit_profile_field({ 261 field: `about`, 262 }); 263 }} 264 > 265 <p 266 class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${ 267 basis.data?.profile.about 268 ? `` 269 : `capitalize opacity-active` 270 }`} 271 > 272 {#if basis.data?.profile.about} 273 {`${basis.data.profile.about}`} 274 {:else} 275 {`+ ${`${$ls(`icu.add_*`, { 276 value: `${$ls(`common.bio`)}`, 277 })}`}`} 278 {/if} 279 </p> 280 </button> 281 </div> 282 </div> 283 <div 284 class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`} 285 > 286 <button 287 class={`flex flex-row justify-center items-center`} 288 onclick={async () => { 289 view_display = `photos`; 290 }} 291 > 292 <p 293 class={`font-sans text-[1.1rem] font-[600] capitalize ${ 294 view_display === `photos` 295 ? classes_photo_overlay_glyph_opt_selected 296 : classes_photo_overlay_glyph_opt 297 } el-re`} 298 > 299 {`${$ls(`common.photos`)}`} 300 </p> 301 </button> 302 <button 303 class={`flex flex-row justify-center items-center`} 304 onclick={async () => { 305 view_display = `following`; 306 }} 307 > 308 <p 309 class={`font-sans text-[1.1rem] font-[600] capitalize ${ 310 view_display === `following` 311 ? classes_photo_overlay_glyph_opt_selected 312 : classes_photo_overlay_glyph_opt 313 } el-re`} 314 > 315 {`${$ls(`common.following`)}`} 316 </p> 317 </button> 318 <button 319 class={`flex flex-row justify-center items-center`} 320 onclick={async () => { 321 view_display = `followers`; 322 }} 323 > 324 <p 325 class={`font-sans text-[1.1rem] font-[600] capitalize ${ 326 view_display === `followers` 327 ? classes_photo_overlay_glyph_opt_selected 328 : classes_photo_overlay_glyph_opt 329 } el-re`} 330 > 331 {`${$ls(`common.followers`)}`} 332 </p> 333 </button> 334 </div> 335 </div> 336 </div> 337 <div 338 class={`flex flex-col w-full min-h-[500px] justify-start items-center`} 339 > 340 {#if view_display === `photos`} 341 <p class={`font-sans font-[400] text-ly0-gl`}> 342 {view_display} 343 </p> 344 {:else if view_display === `following`} 345 <p class={`font-sans font-[400] text-ly0-gl`}> 346 {view_display} 347 </p> 348 {:else if view_display === `followers`} 349 <p class={`font-sans font-[400] text-ly0-gl`}> 350 {view_display} 351 </p> 352 {/if} 353 </div> 354 <NavigationTabs /> 355 {/if}