mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 17:23:25 +00:00
ENH - merge Feather app overhaul into dev
This commit is contained in:
@@ -89,6 +89,10 @@ type ComposerContext = {
|
|||||||
photos: SelectedPhoto[]
|
photos: SelectedPhoto[]
|
||||||
replyTo?: FeatherPost
|
replyTo?: FeatherPost
|
||||||
}
|
}
|
||||||
|
type ProfileMediaContext = {
|
||||||
|
editing: { bio: string; displayName: string }
|
||||||
|
selectedPhoto: SelectedPhoto | null
|
||||||
|
}
|
||||||
|
|
||||||
const phone = usePhoneStore()
|
const phone = usePhoneStore()
|
||||||
const account = useAccountStore()
|
const account = useAccountStore()
|
||||||
@@ -122,12 +126,18 @@ const reportDetails = ref('')
|
|||||||
const mediaPreview = ref<{ index: number; items: FeatherMedia[] } | null>(null)
|
const mediaPreview = ref<{ index: number; items: FeatherMedia[] } | null>(null)
|
||||||
const onboarding = ref({ bio: '', displayName: '', handle: '' })
|
const onboarding = ref({ bio: '', displayName: '', handle: '' })
|
||||||
const editing = ref({ bio: '', displayName: '' })
|
const editing = ref({ bio: '', displayName: '' })
|
||||||
|
const selectedProfilePhoto = ref<SelectedPhoto | null>(null)
|
||||||
const authMode = ref<'login' | 'register'>('login')
|
const authMode = ref<'login' | 'register'>('login')
|
||||||
const authForm = ref({ confirm: '', email: '', password: '' })
|
const authForm = ref({ confirm: '', email: '', password: '' })
|
||||||
const authBusy = ref(false)
|
const authBusy = ref(false)
|
||||||
const authAttempted = ref(false)
|
const authAttempted = ref(false)
|
||||||
const authError = ref('')
|
const authError = ref('')
|
||||||
const authPasswordVisible = ref(false)
|
const authPasswordVisible = ref(false)
|
||||||
|
const composeFabColors = {
|
||||||
|
activeBgIos: 'active:!bg-[#2778dc] dark:active:!bg-[#2778dc]',
|
||||||
|
bgIos: '!bg-[#58a6ff] dark:!bg-[#58a6ff]',
|
||||||
|
textIos: '!text-white dark:!text-white',
|
||||||
|
}
|
||||||
let exploreSearchTimer: number | undefined
|
let exploreSearchTimer: number | undefined
|
||||||
let networkSearchTimer: number | undefined
|
let networkSearchTimer: number | undefined
|
||||||
|
|
||||||
@@ -204,6 +214,9 @@ const canSaveProfile = computed(
|
|||||||
editing.value.displayName.length <= 50 &&
|
editing.value.displayName.length <= 50 &&
|
||||||
editing.value.bio.length <= 160,
|
editing.value.bio.length <= 160,
|
||||||
)
|
)
|
||||||
|
const editProfileAvatarUrl = computed(
|
||||||
|
() => selectedProfilePhoto.value?.url ?? feather.profile?.avatar_url,
|
||||||
|
)
|
||||||
const canCreateProfile = computed(
|
const canCreateProfile = computed(
|
||||||
() =>
|
() =>
|
||||||
!busy.value &&
|
!busy.value &&
|
||||||
@@ -595,19 +608,49 @@ function openEdit(): void {
|
|||||||
bio: feather.profile.bio,
|
bio: feather.profile.bio,
|
||||||
displayName: feather.profile.display_name,
|
displayName: feather.profile.display_name,
|
||||||
}
|
}
|
||||||
|
selectedProfilePhoto.value = null
|
||||||
screen.value = 'edit'
|
screen.value = 'edit'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openProfileMedia(app: 'camera' | 'photos'): void {
|
||||||
|
messageMedia.begin(
|
||||||
|
'feather:profile-avatar',
|
||||||
|
'photo',
|
||||||
|
'/apps/feather?profileEdit=1',
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
editing: { ...editing.value },
|
||||||
|
selectedPhoto: selectedProfilePhoto.value,
|
||||||
|
} satisfies ProfileMediaContext,
|
||||||
|
)
|
||||||
|
void router.push({
|
||||||
|
path: `/apps/${app}`,
|
||||||
|
query: { mediaAttachment: 'photo' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeProfileEdit(): void {
|
||||||
|
tab.value = 'profile'
|
||||||
|
screen.value = 'main'
|
||||||
|
feather.viewedProfile = null
|
||||||
|
selectedProfilePhoto.value = null
|
||||||
|
if (route.query.profileEdit === '1')
|
||||||
|
void router.replace({ path: '/apps/feather' })
|
||||||
|
}
|
||||||
|
|
||||||
async function saveProfile(): Promise<void> {
|
async function saveProfile(): Promise<void> {
|
||||||
busy.value = true
|
busy.value = true
|
||||||
const response = await feather.updateProfile(editing.value)
|
const response = await feather.updateProfile({
|
||||||
|
...editing.value,
|
||||||
|
avatarId: selectedProfilePhoto.value?.id,
|
||||||
|
})
|
||||||
busy.value = false
|
busy.value = false
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
errorToast(response.error)
|
errorToast(response.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (feather.profile) await feather.loadProfile(feather.profile.id)
|
if (feather.profile) await feather.loadProfile(feather.profile.id)
|
||||||
screen.value = 'profile'
|
closeProfileEdit()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deletePost(): Promise<void> {
|
async function deletePost(): Promise<void> {
|
||||||
@@ -646,7 +689,7 @@ function goBack(): void {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (screen.value === 'edit') {
|
if (screen.value === 'edit') {
|
||||||
screen.value = 'profile'
|
closeProfileEdit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (screen.value === 'thread' || screen.value === 'profile') {
|
if (screen.value === 'thread' || screen.value === 'profile') {
|
||||||
@@ -695,6 +738,8 @@ onMounted(async () => {
|
|||||||
window.addEventListener('keydown', handleMediaPreviewKeydown)
|
window.addEventListener('keydown', handleMediaPreviewKeydown)
|
||||||
const selection =
|
const selection =
|
||||||
messageMedia.consumeMany<ComposerContext>('feather:composer')
|
messageMedia.consumeMany<ComposerContext>('feather:composer')
|
||||||
|
const profileSelection =
|
||||||
|
messageMedia.consumeMany<ProfileMediaContext>('feather:profile-avatar')
|
||||||
if (selection) {
|
if (selection) {
|
||||||
if (selection.context) {
|
if (selection.context) {
|
||||||
composerBody.value = selection.context.body
|
composerBody.value = selection.context.body
|
||||||
@@ -706,8 +751,21 @@ onMounted(async () => {
|
|||||||
photos.value.push({ id: media.id, url: media.url })
|
photos.value.push({ id: media.id, url: media.url })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (profileSelection) {
|
||||||
|
if (profileSelection.context) {
|
||||||
|
editing.value = profileSelection.context.editing
|
||||||
|
selectedProfilePhoto.value = profileSelection.context.selectedPhoto
|
||||||
|
}
|
||||||
|
if (profileSelection.media[0]) {
|
||||||
|
selectedProfilePhoto.value = {
|
||||||
|
id: profileSelection.media[0].id,
|
||||||
|
url: profileSelection.media[0].url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (route.query.compose === '1') screen.value = 'composer'
|
if (route.query.compose === '1') screen.value = 'composer'
|
||||||
await feather.bootstrap()
|
await feather.bootstrap()
|
||||||
|
if (route.query.profileEdit === '1' && feather.profile) screen.value = 'edit'
|
||||||
if (feather.onboarded) {
|
if (feather.onboarded) {
|
||||||
const easyShareId = String(route.query.easyShareId ?? '')
|
const easyShareId = String(route.query.easyShareId ?? '')
|
||||||
if (easyShareId && route.query.easyShareKind === 'profile') {
|
if (easyShareId && route.query.easyShareKind === 'profile') {
|
||||||
@@ -1126,8 +1184,8 @@ onMounted(async () => {
|
|||||||
<div class="feather-edit__identity">
|
<div class="feather-edit__identity">
|
||||||
<div class="feather-edit__avatar">
|
<div class="feather-edit__avatar">
|
||||||
<img
|
<img
|
||||||
v-if="feather.profile?.avatar_url"
|
v-if="editProfileAvatarUrl"
|
||||||
:src="feather.profile.avatar_url"
|
:src="editProfileAvatarUrl"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<UserRound v-else :size="28" />
|
<UserRound v-else :size="28" />
|
||||||
@@ -1139,6 +1197,20 @@ onMounted(async () => {
|
|||||||
<span class="feather-edit__badge"><PencilLine :size="14" /></span>
|
<span class="feather-edit__badge"><PencilLine :size="14" /></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="feather-edit__photo">
|
||||||
|
<strong>{{ t('chooseAvatar') }}</strong>
|
||||||
|
<div class="feather-edit__photo-actions">
|
||||||
|
<kButton tonal rounded @click="openProfileMedia('photos')">
|
||||||
|
<Images :size="16" />
|
||||||
|
<span>{{ t('chooseGallery') }}</span>
|
||||||
|
</kButton>
|
||||||
|
<kButton tonal rounded @click="openProfileMedia('camera')">
|
||||||
|
<Camera :size="16" />
|
||||||
|
<span>{{ t('takePhoto') }}</span>
|
||||||
|
</kButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<kList strong inset class="feather-edit__fields">
|
<kList strong inset class="feather-edit__fields">
|
||||||
<kListInput
|
<kListInput
|
||||||
:value="editing.displayName"
|
:value="editing.displayName"
|
||||||
@@ -1163,16 +1235,6 @@ onMounted(async () => {
|
|||||||
</kListInput>
|
</kListInput>
|
||||||
</kList>
|
</kList>
|
||||||
|
|
||||||
<kButton
|
|
||||||
large
|
|
||||||
rounded
|
|
||||||
:disabled="!canSaveProfile"
|
|
||||||
class="feather-primary feather-edit__save"
|
|
||||||
@click="saveProfile"
|
|
||||||
>
|
|
||||||
<PencilLine :size="16" />
|
|
||||||
<span>{{ busy ? t('loading') : t('saveProfile') }}</span>
|
|
||||||
</kButton>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
@@ -2014,6 +2076,7 @@ onMounted(async () => {
|
|||||||
component="button"
|
component="button"
|
||||||
type="button"
|
type="button"
|
||||||
class="feather-compose-fab"
|
class="feather-compose-fab"
|
||||||
|
:colors="composeFabColors"
|
||||||
:aria-label="t('newPost')"
|
:aria-label="t('newPost')"
|
||||||
@click="openComposer()"
|
@click="openComposer()"
|
||||||
>
|
>
|
||||||
@@ -3761,7 +3824,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
.feather-network-person :deep(.feather-network-person__inner) {
|
.feather-network-person :deep(.feather-network-person__inner) {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding-block: 0;
|
padding-block: 0 7px;
|
||||||
}
|
}
|
||||||
.feather-network-person :deep(.feather-network-person__title-wrap) {
|
.feather-network-person :deep(.feather-network-person__title-wrap) {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -3803,9 +3866,12 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
.feather-network-person__name {
|
.feather-network-person__name {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
overflow: hidden;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -3816,6 +3882,10 @@ onMounted(async () => {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.feather-network-person__name span {
|
.feather-network-person__name span {
|
||||||
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -4141,22 +4211,26 @@ onMounted(async () => {
|
|||||||
z-index: 12;
|
z-index: 12;
|
||||||
right: 14px;
|
right: 14px;
|
||||||
bottom: 91px;
|
bottom: 91px;
|
||||||
width: 42px;
|
width: 46px;
|
||||||
height: 42px;
|
height: 46px;
|
||||||
min-width: 42px;
|
min-width: 46px;
|
||||||
border: 1px solid color-mix(in srgb, var(--feather-blue) 72%, #fff);
|
border: 1px solid color-mix(in srgb, var(--feather-blue) 55%, #fff);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: var(--feather-blue);
|
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 7px 20px rgb(29 155 240 / 32%),
|
0 9px 24px rgb(29 155 240 / 38%),
|
||||||
0 2px 7px rgb(0 0 0 / 18%);
|
0 3px 8px rgb(0 0 0 / 22%),
|
||||||
|
inset 0 1px 0 rgb(255 255 255 / 32%);
|
||||||
transition:
|
transition:
|
||||||
transform 150ms ease,
|
transform 150ms ease,
|
||||||
box-shadow 150ms ease;
|
box-shadow 150ms ease,
|
||||||
|
filter 150ms ease;
|
||||||
}
|
}
|
||||||
.feather-compose-fab:active {
|
.feather-compose-fab:active {
|
||||||
transform: scale(0.93);
|
filter: brightness(0.94);
|
||||||
box-shadow: 0 3px 10px rgb(29 155 240 / 25%);
|
transform: scale(0.94);
|
||||||
|
box-shadow:
|
||||||
|
0 4px 12px rgb(29 155 240 / 28%),
|
||||||
|
inset 0 1px 0 rgb(255 255 255 / 22%);
|
||||||
}
|
}
|
||||||
.feather-tab-icon b {
|
.feather-tab-icon b {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -4705,6 +4779,34 @@ onMounted(async () => {
|
|||||||
background: var(--feather-blue);
|
background: var(--feather-blue);
|
||||||
box-shadow: 0 5px 14px rgb(29 155 240 / 25%);
|
box-shadow: 0 5px 14px rgb(29 155 240 / 25%);
|
||||||
}
|
}
|
||||||
|
.feather-edit__photo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
border: 1px solid var(--feather-border);
|
||||||
|
border-radius: 18px;
|
||||||
|
padding: 11px 12px 12px;
|
||||||
|
background: color-mix(in srgb, var(--feather-panel) 96%, transparent);
|
||||||
|
box-shadow: 0 10px 28px rgb(0 0 0 / 7%);
|
||||||
|
}
|
||||||
|
.feather-edit__photo > strong {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
.feather-edit__photo-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.feather-edit__photo-actions :deep(.k-button) {
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 35px;
|
||||||
|
gap: 6px;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--feather-blue) 24%, transparent);
|
||||||
|
color: var(--feather-blue);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
.feather-edit__fields {
|
.feather-edit__fields {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
@@ -4731,17 +4833,6 @@ onMounted(async () => {
|
|||||||
color: var(--feather-muted);
|
color: var(--feather-muted);
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
}
|
}
|
||||||
.feather-edit__save {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 43px;
|
|
||||||
margin-top: 1px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 800;
|
|
||||||
box-shadow: 0 8px 20px rgb(29 155 240 / 22%);
|
|
||||||
}
|
|
||||||
.feather-edit__save :deep(.k-icon) {
|
|
||||||
margin-right: 7px;
|
|
||||||
}
|
|
||||||
.feather-connections {
|
.feather-connections {
|
||||||
padding-top: 10px !important;
|
padding-top: 10px !important;
|
||||||
}
|
}
|
||||||
@@ -4755,6 +4846,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
.feather-connections__tabs :deep(button) {
|
.feather-connections__tabs :deep(button) {
|
||||||
min-height: 31px;
|
min-height: 31px;
|
||||||
|
border-radius: 11px !important;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user