wip bsky client for the web & android bbell.vt3e.cat
at main 265 lines 5.4 kB view raw
1<script setup lang="ts"> 2import { onMounted, onUnmounted, ref } from 'vue' 3 4import ScreenLayout from '@/components/Layout/ScreenLayout.vue' 5import SVG from '@/components/UI/SVG.vue' 6import BluebellLogo from '@/assets/icons/bluebell.svg?raw' 7import { tap } from '@/utils/haptics' 8import { useAuthStore } from '@/stores/auth' 9 10const auth = useAuthStore() 11const messages = [ 12 "they're calling it the coilest client", 13 'thank you for using bell >.<', 14 'awawawwaawawa', 15 'bwaaaaaaa', 16 "remember that you're cute (or whatever your preferred compliment is)!", 17 '@xan.lol im coming for ur crown', 18 'the logo is NOT a wilted rose oh my GOD', 19 'isabluebel roses', 20 'trans rights', 21 'do you have your coil habdel?', 22 'CHOCCY MILK IS SHOOO GOOD', 23 'dhis is habdelslop , you only liek it bcuz its coil', 24 25 // ty pupcloud @upcloud.com 26 'use code "CLICKER" and test upcloud.com with €50 in free credit!', 27 28 // ty hoi.tgirl.gay! 29 'shooo eepy', 30 'blahaj approved', 31 "you're so valid bloomf...", 32 'sho gleepy', 33 34 // ty @coolhands.nekoweb.org! 35 ',,i like ,,..ice cweam .,', 36 'get that handle ready', 37 38 // ty @bot-tan.suibari.com! 39 'And thanks a bunch for liking my post! It makes me so happy!', 40] 41const setMessage = () => { 42 let newIndex 43 do { 44 newIndex = Math.floor(Math.random() * messages.length) 45 } while (messages[newIndex] === message.value && messages.length > 1) 46 47 message.value = messages[newIndex] 48 tap() 49} 50const message = ref() 51setMessage() 52 53onMounted(() => { 54 document.addEventListener('pointerdown', setMessage) 55}) 56onUnmounted(() => { 57 document.removeEventListener('pointerdown', setMessage) 58}) 59</script> 60 61<template> 62 <ScreenLayout bgImage="/images/bluebell.webp" :contentPosition="'center'" maxWidth="640px"> 63 <div class="splash-content"> 64 <div class="app-hero"> 65 <div class="logo-wrapper" aria-hidden="true"> 66 <SVG :icon="BluebellLogo" class="logo" /> 67 </div> 68 <h1 class="title">Bluebell</h1> 69 <p class="subtitle" role="status" aria-live="polite">{{ message }}</p> 70 </div> 71 72 <Teleport to="body"> 73 <!-- me fr --> 74 <div class="bottom"> 75 <Transition name="pop-up"> 76 <div v-if="auth.profile" class="hi-user-pill"> 77 <img 78 :src="auth.profile.avatar" 79 :alt="`Avatar for ${auth.profile.handle}`" 80 class="avatar" 81 /> 82 <p class="hello"> 83 hi, <span class="name">{{ auth.profile.displayName || auth.profile.handle }}</span 84 >! 85 </p> 86 </div> 87 </Transition> 88 89 <div class="loader-track" aria-hidden="true"> 90 <div class="loader-bar"></div> 91 </div> 92 </div> 93 </Teleport> 94 </div> 95 </ScreenLayout> 96</template> 97 98<style scoped lang="scss"> 99.pop-up-enter-active { 100 animation-delay: 2s; 101 animation: contentIn 0.5s cubic-bezier(0.2, 0.9, 0.2, 1) both; 102} 103 104@keyframes popUp { 105} 106 107.splash-content { 108 position: relative; 109 z-index: 10; 110 display: flex; 111 flex-direction: column; 112 align-items: center; 113 114 gap: 1.5rem; 115 padding: 2rem; 116 width: 100%; 117 max-width: 640px; 118 margin: 0 auto; 119 text-align: center; 120 transform-origin: center; 121 animation: contentIn 0.7s cubic-bezier(0.2, 0.9, 0.2, 1) both; 122} 123 124.bottom { 125 position: fixed; 126 bottom: calc(2rem + var(--inset-bottom, 0)); 127 left: 50%; 128 transform: translateX(-50%); 129 width: 100%; 130 131 z-index: 2000; 132 display: flex; 133 flex-direction: column; 134 justify-content: center; 135 align-items: center; 136} 137 138.hi-user-pill { 139 display: flex; 140 align-items: center; 141 gap: 0.75em; 142 padding: 0.5rem; 143 padding-right: 1.25rem; 144 background-color: hsla(var(--base) / 1); 145 border: 1px solid hsla(var(--surface2) / 0.3); 146 border-radius: 10rem; 147 margin-bottom: 1.5rem; 148 max-width: 40vw; 149 150 .avatar { 151 width: 2rem; 152 height: 2rem; 153 border-radius: 50%; 154 object-fit: cover; 155 border: 1.5px solid hsl(var(--surface2)); 156 157 @media (min-width: 512px) { 158 width: 2.5rem; 159 height: 2.5rem; 160 } 161 } 162 163 .hello { 164 font-size: 0.875rem; 165 color: hsl(var(--text)); 166 167 .name { 168 font-weight: 600; 169 color: hsl(var(--accent)); 170 } 171 172 @media (min-width: 512px) { 173 font-size: 1rem; 174 } 175 } 176} 177 178.app-hero { 179 display: flex; 180 flex-direction: column; 181 align-items: center; 182 gap: 0.75rem; 183 184 .logo-wrapper { 185 width: 6rem; 186 height: 6rem; 187 color: hsl(var(--accent)); 188 189 :deep(svg) { 190 width: 100%; 191 height: 100%; 192 } 193 } 194 195 .title { 196 font-size: 2.25rem; 197 font-weight: 800; 198 letter-spacing: -0.02em; 199 margin: 0; 200 background: linear-gradient(135deg, hsl(var(--text)) 0%, hsl(var(--subtext0)) 100%); 201 -webkit-background-clip: text; 202 background-clip: text; 203 -webkit-text-fill-color: transparent; 204 } 205 .subtitle { 206 font-size: 1.25rem; 207 font-weight: 400; 208 margin: 0; 209 width: 100%; 210 color: hsl(var(--subtext1)); 211 user-select: none; 212 } 213} 214 215.loader-track { 216 width: 10rem; 217 height: 0.5rem; 218 background-color: hsla(var(--surface2) / 0.3); 219 border-radius: 5rem; 220 overflow: hidden; 221 222 .loader-bar { 223 position: relative; 224 top: 0; 225 left: 0; 226 height: 100%; 227 width: 100%; 228 background-color: hsl(var(--accent)); 229 border-radius: 99px; 230 transform-origin: left; 231 animation: loading 1.5s cubic-bezier(0.65, 0, 0.35, 1) infinite; 232 } 233} 234 235@keyframes loading { 236 0% { 237 transform: translateX(-100%); 238 } 239 50% { 240 transform: translateX(0); 241 } 242 100% { 243 transform: translateX(100%); 244 } 245} 246 247@keyframes contentIn { 248 from { 249 opacity: 0; 250 transform: translateY(3rem) scale(0.75); 251 filter: blur(8px); 252 } 253 to { 254 opacity: 1; 255 transform: translateY(0) scale(1); 256 filter: blur(0); 257 } 258} 259 260@media (min-width: 512px) { 261 .title { 262 font-size: 3rem; 263 } 264} 265</style>