Skip to content

Commit b802781

Browse files
authored
Feat: Pending Connections (#11)
* feat: implement pending connection functionality * pending connection fixes * fix: connection navigation * change bg of qr code * Add terms of service opt-in on login page
1 parent 63bc99d commit b802781

29 files changed

+2902
-158
lines changed

apps/reauth/src/routes/LoginForm.svelte

+11-1
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,19 @@
131131
<p class="mt-4 text-red-600">{systemError}</p>
132132
{/if}
133133
</div>
134+
<p class="text-xs text-gray-400 mt-4 w-full">
135+
By continuing, you agree to Resplice's
136+
<a
137+
class="underline text-brand-primary"
138+
href="https://resplice.notion.site/Resplice-T-C-f86ee1053bd94e4ead8df5ab1f9555ab"
139+
>
140+
Terms of Service
141+
</a>
142+
and opt-in to receiving verification codes via sms from Resplice.
143+
</p>
134144
<!-- We have to include this text according to google :( -->
135145
<!-- https://developers.google.com/recaptcha/docs/faq#id-like-to-hide-the-recaptcha-badge.-what-is-allowed -->
136-
<p class="text-xs text-gray-400 mt-4 w-full">
146+
<p class="text-xs text-gray-400 mt-2 w-full">
137147
This site is protected by reCAPTCHA and the Google
138148
<a class="underline text-brand-primary" href="https://policies.google.com/privacy">
139149
Privacy Policy

apps/resplice/src/Router.svelte

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import QrInvitePage from '$modules/invite/pages/QrInvitePage.svelte'
2222
import ScanQrPage from '$modules/invite/pages/ScanQrPage.svelte'
2323
import QrConnectionDetailPage from '$modules/invite/pages/QrConnectionDetailPage.svelte'
24+
import PendingConnectionDetailPage from '$modules/invite/pages/PendingConnectionDetailPage.svelte'
2425
// Connection Pages
2526
import ConnectionDetailPage from '$modules/connection/pages/ConnectionDetailPage.svelte'
2627
import ChangeConnectionPage from '$modules/connection/pages/ChangeConnectionPage.svelte'
@@ -34,18 +35,15 @@
3435
'/attribute/add': SelectAttributeTypePage,
3536
'/attribute/add/:type': AddAttributePage,
3637
'/attribute/:id/change': ChangeAttributePage,
37-
// '/connection/pending/:id': PendingContactDetailPage,
3838
'/connection/:id/details': ConnectionDetailPage,
39-
// '/connection/:id/chat': ContactChatPage,
4039
'/connection/:id/change': ChangeConnectionPage,
41-
// '/connection/:id/shares': ContactSharesPage,
4240
'/home/*': HomePage,
43-
// '/invite': SelectInviteMethodPage,
4441
'/invite/contacts': InviteContactsPage,
4542
'/invite/create/*': CreateInvitePage,
4643
'/invite/qr-scan': ScanQrPage,
4744
'/invite/qr': QrInvitePage,
4845
'/invite/qr/:uuid': QrConnectionDetailPage,
46+
'/invite/pending/:id/details': PendingConnectionDetailPage,
4947
'/invite/:invite-id/details': InviteDetailPage,
5048
'/profile': ProfilePage,
5149
'/search': SearchPage,

apps/resplice/src/common/components/NavActions.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@
6161
class="bg-white rounded-xl p-8 flex flex-col items-start space-y-4"
6262
style="will-change: transform; transform: translateY({$translateY}px) scale({$scale})"
6363
>
64-
<!-- <button
64+
<button
6565
class="flex items-center space-x-2 focus:ring-4 focus:ring-green-200 focus:outline-none rounded-lg w-full"
6666
on:click={() => push('/invite/contacts')}
6767
>
6868
<div class="p-2 rounded-lg bg-brand-primary text-brand-primary bg-opacity-20">
6969
<PeopleIcon width={24} height={24} />
7070
</div>
7171
<p>Import Contacts</p>
72-
</button> -->
72+
</button>
7373
<!-- <button
7474
class="flex items-center space-x-2 focus:ring-4 focus:ring-green-200 focus:outline-none rounded-lg w-full"
7575
on:click={() => push('/invite/create/handle')}

apps/resplice/src/modules/_context/context.protocol.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import type { Stores } from '$common/stores'
1414
import type { Session } from '$modules/session/session.types'
1515
import { applyAccountEvent, type AccountAggregate } from '$modules/account/account.state'
1616
import { applyAttributeEvent, type AttributeAggregate } from '$modules/attribute/attribute.state'
17-
import { applyInviteEvent, type InviteAggregate } from '$modules/invite/invite.state'
17+
import {
18+
applyInviteEvent,
19+
applyPendingConnectionEvent,
20+
type InviteAggregate,
21+
type PendingConnectionAggregate
22+
} from '$modules/invite/invite.state'
1823
import {
1924
applyConnectionEvent,
2025
type ConnectionAggregate
@@ -105,6 +110,14 @@ function contextProtocolFactory({ cache, stores, commuter }: Dependencies): Cont
105110
return aggregate
106111
})
107112

113+
stores.invite.pendingConnections.update((state) => {
114+
let aggregate: PendingConnectionAggregate = state || new Map()
115+
events.forEach((event) => {
116+
aggregate = applyPendingConnectionEvent(aggregate, event)
117+
})
118+
return aggregate
119+
})
120+
108121
stores.connection.update((state) => {
109122
let aggregate: ConnectionAggregate = state || new Map()
110123
events.forEach((event) => {
@@ -188,18 +201,21 @@ function contextProtocolFactory({ cache, stores, commuter }: Dependencies): Cont
188201
let accountAggregate: AccountAggregate = {} as AccountAggregate
189202
let attributeAggregate: AttributeAggregate = new Map()
190203
let inviteAggregate: InviteAggregate = new Map()
204+
let pendingConnectionAggregate: PendingConnectionAggregate = new Map()
191205
let connectionAggregate: ConnectionAggregate = new Map()
192206

193207
events.forEach((event) => {
194208
accountAggregate = applyAccountEvent(accountAggregate, event)
195209
attributeAggregate = applyAttributeEvent(attributeAggregate, event)
196210
inviteAggregate = applyInviteEvent(inviteAggregate, event)
211+
pendingConnectionAggregate = applyPendingConnectionEvent(pendingConnectionAggregate, event)
197212
connectionAggregate = applyConnectionEvent(connectionAggregate, event)
198213
})
199214

200215
stores.account.set(accountAggregate)
201216
stores.attribute.set(attributeAggregate)
202217
stores.invite.invites.set(inviteAggregate)
218+
stores.invite.pendingConnections.set(pendingConnectionAggregate)
203219
stores.connection.set(connectionAggregate)
204220
}
205221
}

apps/resplice/src/modules/attribute/components/AttributeShareContext.svelte

+28-22
Original file line numberDiff line numberDiff line change
@@ -43,35 +43,41 @@
4343
}
4444
</script>
4545

46-
<section class="w-full flex-1 flex flex-col space-y-2">
46+
<button class="w-full flex-1 flex flex-col space-y-2" on:click={() => (isEditing = true)}>
4747
<div class="w-full flex justify-between items-center mb-2">
4848
<h2 class="font-semibold text-xl">Sharing</h2>
49-
<Button class="text-sm uppercase" color="brand-light" on:click={() => (isEditing = true)}>
50-
Edit
51-
</Button>
49+
<Button class="text-sm uppercase" color="brand-light">Change</Button>
5250
</div>
5351

5452
<div class="w-full flex-1 flex flex-nowrap space-x-4 overflow-scroll">
55-
{#each [...selected] as id}
56-
<div class="text-center w-12">
57-
<AttributeAction itemType="user" attribute={getAttribute(id)} actionIdx={0} disableAction />
58-
<p class="w-full whitespace-nowrap overflow-hidden text-ellipsis">
59-
{getAttribute(id).name}
60-
</p>
53+
{#if selected.size}
54+
{#each [...selected] as id}
55+
<div class="text-center w-12">
56+
<AttributeAction
57+
itemType="user"
58+
attribute={getAttribute(id)}
59+
actionIdx={0}
60+
disableAction
61+
/>
62+
<p class="w-full whitespace-nowrap overflow-hidden text-ellipsis">
63+
{getAttribute(id).name}
64+
</p>
65+
</div>
66+
{/each}
67+
{:else}
68+
<div class="text-center">
69+
<button
70+
type="button"
71+
class="outline-none border-none p-3 bg-opacity-20 rounded-lg focus:outline-none bg-brand-primary text-brand-primary-dark"
72+
on:click={() => (isEditing = true)}
73+
>
74+
<AddIcon height={24} width={24} />
75+
</button>
76+
<p>Share</p>
6177
</div>
62-
{/each}
63-
<div class="text-center">
64-
<button
65-
type="button"
66-
class="outline-none border-none p-3 bg-opacity-20 rounded-lg focus:outline-none bg-brand-primary text-brand-primary-dark"
67-
on:click={() => (isEditing = true)}
68-
>
69-
<AddIcon height={24} width={24} />
70-
</button>
71-
<p>Add</p>
72-
</div>
78+
{/if}
7379
</div>
74-
</section>
80+
</button>
7581

7682
{#if isEditing}
7783
<Modal let:close on:close={() => (isEditing = false)} initialY={56}>

apps/resplice/src/modules/connection/pages/ConnectionDetailPage.svelte

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
let selectedAttributes = new Set<number>()
1818
let scrollEl: HTMLDivElement
1919
let showConnectionOnHeader = false
20-
$: id = parseInt(params.id, 10)
21-
$: connection = $connectionStore.get(id)!
20+
$: connection = $connectionStore.get(parseInt(params.id, 10))!
2221
2322
$: {
2423
if (!connection) {

apps/resplice/src/modules/connection/pages/ConnectionListPage.svelte

+11-10
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,23 @@
66
ConnectionEmptyIcon,
77
CameraIcon,
88
QRCodeIcon,
9-
PersonAddIcon
9+
PeopleIcon
1010
} from '@resplice/components'
1111
import connectionStore from '$modules/connection/connection.store'
1212
import inviteStores from '$modules/invite/invite.store'
1313
import { connectionsList } from '$modules/connection/connection.helpers'
1414
import SearchHeader from '$common/components/SearchHeader.svelte'
1515
import ConnectionList from '$modules/connection/components/ConnectionList.svelte'
16-
// import PendingConnectionList from '$modules/invite/components/PendingConnectionList.svelte'
16+
import PendingConnectionList from '$modules/invite/components/PendingConnectionList.svelte'
1717
import InviteList from '$modules/invite/components/InviteList.svelte'
1818
19-
// const pendingConnectionStore = inviteStores.pendingConnections
20-
const inviteStore = inviteStores.invites
21-
// let pendingConnections: PendingConnection[] = Array.from($pendingConnectionStore.values())
2219
let scrollEl: HTMLDivElement
20+
21+
const inviteStore = inviteStores.invites
22+
const pendingConnectionStore = inviteStores.pendingConnections
2323
$: connections = connectionsList($connectionStore)
2424
$: invites = Array.from($inviteStore.values())
25+
$: pendingConnections = Array.from($pendingConnectionStore.values())
2526
2627
onMount(() => {
2728
// Get scroll position
@@ -48,7 +49,7 @@
4849
bind:this={scrollEl}
4950
class="w-full flex-1 flex flex-col overflow-auto bg-white rounded-t-3xl"
5051
>
51-
{#if !connections.length && !invites.length}
52+
{#if !connections.length && !invites.length && !pendingConnections.length}
5253
<div class="w-full h-full flex flex-col justify-center items-center">
5354
<div class="rounded-full overflow-hidden w-48">
5455
<ConnectionEmptyIcon width={192} height={144} />
@@ -61,10 +62,10 @@
6162
<Button
6263
color="brand-light"
6364
class="flex items-center justify-center w-56"
64-
on:click={() => push('/invite/create/phone')}
65+
on:click={() => push('/invite/contacts')}
6566
>
66-
<PersonAddIcon width={24} height={24} />
67-
<span class="ml-2">Invite with phone</span>
67+
<PeopleIcon width={24} height={24} />
68+
<span class="ml-2">Import Contacts</span>
6869
</Button>
6970
</div>
7071

@@ -91,7 +92,7 @@
9192
</div>
9293
</div>
9394
{:else}
94-
<!-- <PendingConnectionList {pendingConnections} /> -->
95+
<PendingConnectionList {pendingConnections} />
9596
<ConnectionList {connections} />
9697
<InviteList {invites} />
9798
{/if}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script lang="ts">
2+
import { pop, replace } from 'svelte-spa-router'
3+
import { BackIcon, Button, EllipsisHorizontalIcon, IconButton, Modal } from '@resplice/components'
4+
import useProtocol from '$common/protocol/useProtocol'
5+
import type { PendingConnection } from '$modules/invite/invite.types'
6+
7+
const protocol = useProtocol()
8+
9+
export let pendingConnection: PendingConnection
10+
let showContextMenu = false
11+
12+
function removePendingConnection() {
13+
protocol.invite.declineInvite({
14+
connectionId: pendingConnection.connectionId,
15+
inviteId: pendingConnection.inviteId
16+
})
17+
replace('/home/connections')
18+
}
19+
</script>
20+
21+
<nav class="flex-none flex items-center justify-between p-4">
22+
<IconButton Icon={BackIcon} on:click={() => pop()} />
23+
<IconButton Icon={EllipsisHorizontalIcon} on:click={() => (showContextMenu = true)} />
24+
</nav>
25+
26+
{#if showContextMenu && !!pendingConnection}
27+
<Modal on:close={() => (showContextMenu = false)}>
28+
<div class="w-full space-y-4 p-8">
29+
<Button class="w-full" color="danger-light" on:click={removePendingConnection}>
30+
Decline Invite
31+
</Button>
32+
</div>
33+
</Modal>
34+
{/if}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
<script lang="ts">
2-
import { formatDistance } from 'date-fns'
3-
import { respliceDateTimeToDateTime } from '@resplice/utils'
42
import { Avatar } from '@resplice/components'
53
import type { PendingConnection } from '$modules/invite/invite.types'
64
75
export let pendingConnection: PendingConnection
8-
9-
$: expiryDistance = formatDistance(
10-
new Date(),
11-
respliceDateTimeToDateTime(pendingConnection.expiresAt)
12-
)
136
</script>
147

158
<button class="flex items-center space-x-2 w-full px-4 py-2" on:click>
16-
<Avatar seed={`${pendingConnection.id}`} src={pendingConnection.avatarUrl} />
9+
<Avatar seed={`${pendingConnection.connectionId}`} src={pendingConnection.avatarUrl} />
1710
<div class="flex flex-col">
18-
<h4 class="font-semibold text-gray-900 text-left">{pendingConnection.name}</h4>
19-
<p class="text-yellow-600">Expires in {expiryDistance}</p>
11+
<h4 class="font-semibold text-slate-900 text-left">{pendingConnection.name}</h4>
12+
<p class="text-slate-600">Invited via {pendingConnection.inviteValue}</p>
2013
</div>
2114
</button>

apps/resplice/src/modules/invite/components/PendingConnectionList.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
{#if pendingConnections.length}
1010
<div class="w-full relative">
1111
<div class="p-4">
12-
<h2 class="text-3xl text-yellow-600 font-semibold px-2 my-2 capitalize">Pending</h2>
12+
<h2 class="text-3xl text-gray-700 font-semibold px-2 my-2 capitalize">Pending</h2>
1313
</div>
1414

1515
{#each pendingConnections as pendingConnection}
1616
<PendingConnectionItem
1717
{pendingConnection}
1818
on:click={() => {
19-
push(`/pending-connection/${pendingConnection.id}/details`)
19+
push(`/invite/pending/${pendingConnection.connectionId}/details`)
2020
}}
2121
/>
2222
{/each}

0 commit comments

Comments
 (0)