diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts
index df004799..64a88cde 100644
--- a/src/state/lib/api.ts
+++ b/src/state/lib/api.ts
@@ -97,6 +97,33 @@ export async function unrepost(adx: AdxClient, user: string, uri: string) {
return numDels > 0
}
+export async function follow(
+ adx: AdxClient,
+ user: string,
+ subject: {did: string; name: string},
+) {
+ return await adx
+ .repo(user, true)
+ .collection('blueskyweb.xyz:Follows')
+ .create('Follow', {
+ $type: 'blueskyweb.xyz:Follow',
+ subject,
+ createdAt: new Date().toISOString(),
+ })
+}
+
+export async function unfollow(
+ adx: AdxClient,
+ user: string,
+ subject: {did: string},
+) {
+ const coll = adx.repo(user, true).collection('blueskyweb.xyz:Follows')
+ const numDels = await deleteWhere(coll, 'Follow', record => {
+ return record.value.subject.did === subject.did
+ })
+ return numDels > 0
+}
+
type WherePred = (_record: GetRecordResponseValidated) => Boolean
async function deleteWhere(
coll: AdxRepoCollectionClient,
@@ -104,7 +131,7 @@ async function deleteWhere(
cond: WherePred,
) {
const toDelete: string[] = []
- iterateAll(coll, schema, record => {
+ await iterateAll(coll, schema, record => {
if (cond(record)) {
toDelete.push(record.key)
}
diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts
index bca4c615..b245335f 100644
--- a/src/state/models/profile-view.ts
+++ b/src/state/models/profile-view.ts
@@ -1,6 +1,7 @@
-import {makeAutoObservable} from 'mobx'
+import {makeAutoObservable, runInAction} from 'mobx'
import {bsky} from '@adxp/mock-api'
import {RootStoreModel} from './root-store'
+import * as apilib from '../lib/api'
export class ProfileViewMyStateModel {
hasFollowed: boolean = false
@@ -67,6 +68,27 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
await this._load()
}
+ async toggleFollowing() {
+ if (this.myState.hasFollowed) {
+ await apilib.unfollow(this.rootStore.api, 'alice.com', {
+ did: this.did,
+ })
+ runInAction(() => {
+ this.followersCount--
+ this.myState.hasFollowed = false
+ })
+ } else {
+ await apilib.follow(this.rootStore.api, 'alice.com', {
+ did: this.did,
+ name: this.name,
+ })
+ runInAction(() => {
+ this.followersCount++
+ this.myState.hasFollowed = true
+ })
+ }
+ }
+
// state transitions
// =
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 57dc0ef8..6a15599d 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -1,5 +1,4 @@
import React, {useState, forwardRef, useImperativeHandle} from 'react'
-import {observer} from 'mobx-react-lite'
import {KeyboardAvoidingView, StyleSheet, TextInput, View} from 'react-native'
import Toast from '../util/Toast'
import ProgressCircle from '../util/ProgressCircle'
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index 50bc2bd1..1eace94a 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -1,12 +1,20 @@
import React, {useState, useEffect} from 'react'
import {observer} from 'mobx-react-lite'
-import {ActivityIndicator, Image, StyleSheet, Text, View} from 'react-native'
+import {
+ ActivityIndicator,
+ Button,
+ Image,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native'
import {OnNavigateContent} from '../../routes/types'
import {ProfileViewModel} from '../../../state/models/profile-view'
import {useStores} from '../../../state'
import {pluralize} from '../../lib/strings'
import {s} from '../../lib/styles'
import {AVIS} from '../../lib/assets'
+import Toast from '../util/Toast'
export const ProfileHeader = observer(function ProfileHeader({
user,
@@ -29,6 +37,23 @@ export const ProfileHeader = observer(function ProfileHeader({
newView.setup().catch(err => console.error('Failed to fetch profile', err))
}, [user, view?.params.user, store])
+ const onPressToggleFollow = () => {
+ view?.toggleFollowing().then(
+ () => {
+ Toast.show(
+ `${view.myState.hasFollowed ? 'Following' : 'No longer following'} ${
+ view.displayName || view.name
+ }`,
+ {
+ duration: Toast.durations.LONG,
+ position: Toast.positions.TOP,
+ },
+ )
+ },
+ err => console.error('Failed to toggle follow', err),
+ )
+ }
+
// loading
// =
if (
@@ -81,6 +106,12 @@ export const ProfileHeader = observer(function ProfileHeader({
{pluralize(view.postsCount, 'post')}
+
+
+
)
})
diff --git a/todos.txt b/todos.txt
index d0c51e1a..29e365ba 100644
--- a/todos.txt
+++ b/todos.txt
@@ -1,8 +1,6 @@
Paul's todo list
- Profile view
- - Follow / Unfollow
- - Badges
- Followers list
- Follows list
- Composer