wip bsky client for the web & android bbell.vt3e.cat
at main 217 lines 4.6 kB view raw
1<script lang="ts" setup> 2import { computed, onMounted, ref } from 'vue' 3 4import { useAuthStore } from '@/stores/auth' 5import { getProviderList, type HydratedProvider } from '@/utils/pds' 6 7import Button from '../../UI/BaseButton.vue' 8import Toggle from '../../UI/BaseCheckbox.vue' 9import Modal from '../../UI/BaseModal.vue' 10import PdsList from '../../PdsSelector.vue' 11 12const auth = useAuthStore() 13 14const providers = ref<HydratedProvider[]>([]) 15const selectedProvider = ref<HydratedProvider>() 16 17const selectedHasHandlePolicy = computed(() => !!selectedProvider.value?.handlePolicy) 18const hasAgreedToHandlePolicy = ref(false) 19 20const canProgress = computed(() => { 21 const selected = selectedProvider.value 22 if (!selected) return false 23 if (selectedHasHandlePolicy.value && hasAgreedToHandlePolicy.value) return true 24 return !selected.handlePolicy 25}) 26 27const pdsError = ref('') 28const pdsInput = ref('') 29 30async function submitPds() { 31 const val = 32 pdsInput.value.trim() === '' ? selectedProvider.value?.url.toString() : pdsInput.value.trim() 33 if (!val) { 34 pdsError.value = 'Enter a PDS URL or handle' 35 return 36 } 37 await auth.login(val) 38} 39 40onMounted(async () => { 41 providers.value = await getProviderList() 42 if (providers.value.length) { 43 selectedProvider.value = providers.value[0] 44 } else { 45 pdsError.value = 'No providers available' 46 } 47}) 48 49function selectProvider(provider: HydratedProvider) { 50 hasAgreedToHandlePolicy.value = false 51 selectedProvider.value = provider 52} 53</script> 54 55<template> 56 <Modal title="Create an account" width="600px"> 57 <div class="modal-body"> 58 <p> 59 Choose a provider to host your account. You can migrate to a different provider later if 60 needed. 61 </p> 62 63 <PdsList @select="selectProvider" /> 64 65 <div v-if="selectedProvider" class="provider-profile"> 66 <div class="profile-main"> 67 <div class="pds-info"> 68 <p class="pds-name">{{ selectedProvider.name }}</p> 69 <p class="pds-location">{{ selectedProvider.location }}</p> 70 </div> 71 </div> 72 73 <div 74 class="policy-transition-wrapper" 75 :class="{ open: selectedHasHandlePolicy }" 76 :inert="!selectedHasHandlePolicy" 77 > 78 <div class="policy-inner"> 79 <div class="policy-section"> 80 <div class="policy-notice"> 81 <p> 82 This provider has restrictions on who can use what handles. 83 <a :href="selectedProvider.handlePolicy?.toString() || '#'" class="policy-link"> 84 View terms here 85 </a> 86 </p> 87 </div> 88 <label class="policy-checkbox" :class="{ checked: hasAgreedToHandlePolicy }"> 89 <Toggle v-model="hasAgreedToHandlePolicy" id="policy-agree" /> 90 <span>I've read and accept these terms</span> 91 </label> 92 </div> 93 </div> 94 </div> 95 </div> 96 </div> 97 98 <template #footer> 99 <Button variant="ghost" type="button" @click="$emit('close')">Cancel</Button> 100 <Button 101 variant="primary" 102 :loading="auth.isLoading" 103 @click="submitPds" 104 type="button" 105 :disabled="!canProgress" 106 > 107 Create account 108 </Button> 109 </template> 110 </Modal> 111</template> 112 113<style lang="scss" scoped> 114.modal-body { 115 display: flex; 116 flex-direction: column; 117 gap: 1.5rem; 118} 119 120.provider-list { 121 margin-top: -0.75rem; 122} 123 124.provider-profile { 125 background: hsla(var(--surface0) / 0.4); 126 margin-left: -0.75rem; 127 width: calc(100% + 1.5rem); 128 border-radius: 1rem; 129 padding: 1.25rem; 130 display: flex; 131 flex-direction: column; 132 gap: 0; 133 134 .profile-main { 135 display: flex; 136 align-items: center; 137 gap: 1rem; 138 139 .pds-info { 140 display: flex; 141 flex-direction: row; 142 align-items: baseline; 143 gap: 0.5rem; 144 145 .pds-name { 146 font-weight: 600; 147 font-size: 1rem; 148 color: hsla(var(--text)); 149 display: block; 150 } 151 152 .pds-location { 153 font-size: 0.8rem; 154 color: hsla(var(--text) / 0.6); 155 display: block; 156 } 157 } 158 } 159 160 .policy-transition-wrapper { 161 display: grid; 162 grid-template-rows: 0fr; 163 opacity: 0; 164 165 &.open { 166 grid-template-rows: 1fr; 167 opacity: 1; 168 } 169 } 170 171 .policy-inner { 172 overflow: hidden; 173 min-height: 0; 174 } 175 176 .policy-section { 177 border-top: 1px dashed hsla(var(--accent) / 0.5); 178 margin-top: 0.5rem; 179 padding-top: 0.5rem; 180 181 display: flex; 182 flex-direction: column; 183 gap: 0.25rem; 184 185 .policy-notice { 186 font-size: 0.85rem; 187 color: hsl(var(--subtext1)); 188 189 .policy-link { 190 color: hsla(var(--accent)); 191 text-decoration: none; 192 font-weight: 700; 193 194 &:hover { 195 text-decoration: underline; 196 } 197 } 198 } 199 200 .policy-checkbox { 201 display: flex; 202 align-items: center; 203 gap: 0.75rem; 204 205 span { 206 font-size: 0.85rem; 207 user-select: none; 208 } 209 } 210 } 211} 212 213.fade-enter-from, 214.fade-leave-to { 215 opacity: 0; 216} 217</style>