Skip to content

Commit

Permalink
feat(friends): build 'add friend' functionality for mobile (#4144)
Browse files Browse the repository at this point in the history
* feat(friends): build 'add friend' functionality for mobile

* fix(friends): stop friends-list column from over-expanding

* Revert "fix(friends): stop friends-list column from over-expanding"

This reverts commit da342d6.

* fix(friends): prevent friends-list from becoming squashed

* fix(friends): adjust page spacing

* fix(friends): add title to friends list
  • Loading branch information
nathan-power committed Aug 8, 2022
1 parent bb4a43b commit ddb5fc8
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 110 deletions.
43 changes: 2 additions & 41 deletions components/views/friends/add/Add.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,6 @@
<TypographyText :text="$t('friends.add_description')" />
</div>
</div>
<InteractablesInput
v-model="friendId"
type="primary"
size="small"
:placeholder="$t('friends.search_placeholder')"
@update="_searchFriend"
ref="input"
autofocus
/>
<TypographyError v-if="error" :text="$t(error)" />
<UiLoadersLoadingBar v-else-if="searching" />
<FriendsFriend
v-else-if="user && user.did"
:user="user"
@requestSent="onFriendRequestSent"
isPreview
/>
<!--
<TypographyHorizontalRuleText plaintext value="OR" />
<TypographyTitle :size="6" :text="$t('friends.add_via_qr')" />
<TypographyText :text="$t('friends.add_qrcode_description')" />
<div class="card">
<div class="card-content">
<div class="content">
<div class="columns">
<div class="column">
<TypographyText :text="$t('friends.scan_code')" />
<InteractablesButton
type="primary"
size="small"
:text="$t('friends.camera_scan')"
/>
</div>
<div class="column">
<TypographyText :text="$t('friends.friend_code')" />
<qrcode-vue :value="friendInviteUrl" :size="150" level="H" />
</div>
</div>
</div>
</div>
</div> -->
<FriendsSearch />
<FriendsQrSection />
</div>
7 changes: 1 addition & 6 deletions components/views/friends/add/Add.less
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
.add {
display: flex;
flex-direction: column;
gap: @normal-spacing;
gap: 8px;

.title-container {
display: flex;
gap: 0.75rem;
}
.card {
&:extend(.background-semitransparent-light);
&:extend(.bordered);
&:extend(.font-primary);
}
}
61 changes: 0 additions & 61 deletions components/views/friends/add/Add.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
<template src="./Add.html"></template>
<script lang="ts">
// @ts-ignore
import QrcodeVue from 'qrcode.vue'
import { UserPlusIcon } from 'satellite-lucide-icons'
import Vue from 'vue'
import { mapState } from 'vuex'
import { debounce } from 'lodash'
import iridium from '~/libraries/Iridium/IridiumManager'
import { FriendRequest, User } from '~/libraries/Iridium/friends/types'
import { RootState } from '~/types/store/store'
export default Vue.extend({
components: {
QrcodeVue,
UserPlusIcon,
},
data() {
return {
error: '',
Expand All @@ -30,57 +20,6 @@ export default Vue.extend({
showSidebar: (state) => (state as RootState).ui.showSidebar,
}),
},
async mounted() {
if (this.$route.params && this.$route.params.id) {
this.$data.friendId = this.$route.params.id
this._searchFriend()
}
iridium.friends?.on('request/error', (err) => {
this.error = err
})
},
methods: {
_searchFriend: debounce(async function (this: any) {
if (!this.friendId.length) {
this.error = ''
this.user = null
this.searching = false
return
}
await this.searchFriend()
this.searching = false
}, 500),
async searchFriend() {
this.user = null
this.error = ''
this.searching = true
const friendId = this.friendId.trim()
if (friendId === iridium.connector?.id) {
this.error = this.$t('friends.self_add') as string
return
}
const hasFriend = iridium.friends.isFriend(friendId)
if (hasFriend) {
this.error = this.$t('friends.already_friend') as string
}
this.user = {
did: friendId,
name: friendId,
status: 'offline',
}
this.searching = false
},
onFriendRequestSent() {
this.request = null
this.user = null
this.friendId = ''
// @ts-ignore
const input = this.$refs.input.$refs.input as HTMLInputElement
input.value = ''
this.$toast.show(this.$t('friends.request_sent') as string)
},
},
})
</script>
<style scoped lang="less" src="./Add.less"></style>
1 change: 0 additions & 1 deletion components/views/friends/friend/Friend.less
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
display: flex;
align-items: center;
padding: @light-spacing @normal-spacing;
margin: 0 @light-spacing;
gap: @normal-spacing;
&:extend(.no-select);
&:extend(.round-corners);
Expand Down
96 changes: 96 additions & 0 deletions components/views/friends/qrSection/QrSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<div>
<TypographyHorizontalRuleText plaintext value="OR" />
<TypographyTitle :size="6" :text="$t('friends.add_via_qr')" />
<TypographyText :text="$t('friends.add_qrcode_description')" />
<div class="card">
<div class="qr-container">
<div class="qr-section">
<TypographyText :text="$t('friends.scan_code')" />
<InteractablesButton
type="primary"
size="small"
:text="$t('friends.camera_scan')"
/>
</div>

<div class="qr-section">
<TypographyText :text="$t('friends.friend_code')" />
<qrcode-vue
:value="friendInviteUrl"
:size="150"
level="H"
class="qr-code"
/>
</div>
</div>
</div>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
// @ts-ignore
import QrcodeVue from 'qrcode.vue'
export default Vue.extend({
name: 'QrSection',
components: {
QrcodeVue,
},
data() {
return {
friendInviteUrl: '',
}
},
async mounted() {
// this.friendInviteUrl = await iridium.friends.getFriendInviteUrl()
},
})
</script>

<style lang="less" scoped>
.card {
&:extend(.background-semitransparent-light);
&:extend(.bordered);
&:extend(.font-primary);
max-width: 400px;
margin: auto;
margin-top: 32px;
@media only screen and (max-width: @small-breakpoint) {
max-width: 320px;
}
.qr-container {
flex: 1;
padding: 24px;
display: flex;
justify-content: space-between;
gap: 32px;
@media only screen and (max-width: @small-breakpoint) {
flex-direction: column-reverse;
justify-content: center;
align-items: center;
}
.qr-section {
display: flex;
flex-direction: column;
gap: 16px;
@media only screen and (max-width: @small-breakpoint) {
width: 100%;
}
.qr-code {
@media only screen and (max-width: @small-breakpoint) {
display: flex;
justify-content: center;
}
}
}
}
}
</style>
135 changes: 135 additions & 0 deletions components/views/friends/search/Search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<template>
<div>
<InteractablesInput
ref="input"
v-model="friendId"
type="primary"
size="small"
:placeholder="$t('friends.search_placeholder')"
autofocus
@update="_searchFriend"
/>
<TypographyError v-if="error" :text="$t(error)" />
<UiLoadersLoadingBar v-else-if="searching" />
<div v-else-if="!friendId" class="id-container">
<button class="id-button" @click="copyId">
<TypographyText :text="$t('friends.copy_your_id')" class="id" />
</button>
</div>
<FriendsFriend
v-else-if="user && user.did"
:user="user"
is-preview
class="friend-item"
@requestSent="onFriendRequestSent"
/>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
import { debounce } from 'lodash'
import { mapState } from 'vuex'
import iridium from '~/libraries/Iridium/IridiumManager'
import { FriendRequest, User } from '~/libraries/Iridium/friends/types'
import { RootState } from '~/types/store/store'
export default Vue.extend({
data() {
return {
error: '',
friendId: '',
searching: false,
request: null as FriendRequest | null,
user: null as User | null,
}
},
computed: {
...mapState({
accounts: (state) => (state as RootState).accounts,
}),
},
async mounted() {
if (this.$route.params && this.$route.params.id) {
this.$data.friendId = this.$route.params.id
this._searchFriend()
}
iridium.friends?.on('request/error', (err: string) => {
this.error = err
})
},
methods: {
_searchFriend: debounce(async function (this: any) {
if (!this.friendId.length) {
this.error = ''
this.user = null
this.searching = false
return
}
await this.searchFriend()
this.searching = false
}, 500),
async searchFriend() {
this.user = null
this.error = ''
this.searching = true
const friendId = this.friendId.trim()
if (friendId === iridium.connector?.id) {
this.error = this.$t('friends.self_add') as string
return
}
const hasFriend = iridium.friends.isFriend(friendId)
if (hasFriend) {
this.error = this.$t('friends.already_friend') as string
}
this.user = {
did: friendId,
name: friendId,
status: 'offline',
}
this.searching = false
},
onFriendRequestSent() {
this.request = null
this.user = null
this.friendId = ''
// @ts-ignore
const input = this.$refs.input.$refs.input as HTMLInputElement
input.value = ''
// @ts-ignore
this.$toast.show(this.$t('friends.request_sent') as string)
},
copyId() {
// @ts-ignore
this.$toast.show(this.$t('ui.copied') as string)
navigator.clipboard.writeText(this.accounts.active)
},
},
})
</script>

<style lang="less" scoped>
.friend-item {
margin-top: 16px;
}
.id-container {
display: flex;
justify-content: flex-end;
user-select: none;
.id-button {
.id {
white-space: nowrap;
color: @text-muted;
padding: 4px 8px;
&:hover {
opacity: 0.8;
}
}
}
}
</style>
1 change: 1 addition & 0 deletions locales/en-US.js
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ export default {
options: 'Options',
cancel: 'Cancel request',
request: 'Requests',
copy_your_id: 'Copy your ID',
},
market_place: {
title: 'Marketplace\nComing soon',
Expand Down
Loading

0 comments on commit ddb5fc8

Please sign in to comment.