Skip to content
This repository was archived by the owner on Jul 7, 2025. It is now read-only.

Commit ce742f6

Browse files
authored
Merge pull request #288 from xmtp/rygine/consent-update
Update DB schema, add migration
2 parents d3c350d + ab6ec41 commit ce742f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+809
-502
lines changed

.changeset/slimy-shrimps-attend.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@xmtp/react-sdk": patch
3+
---
4+
5+
Update DB schema, add migration

apps/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"@xmtp/content-type-reply": "^1.1.11",
2727
"@xmtp/content-type-text": "^1.0.0",
2828
"@xmtp/react-sdk": "workspace:*",
29-
"@xmtp/xmtp-js": "^12.0.0",
29+
"@xmtp/xmtp-js": "^12.1.0",
3030
"date-fns": "^3.6.0",
3131
"react": "^18.3.1",
3232
"react-18-blockies": "^1.0.6",

apps/react/src/components/library/ReactionsBar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ export const ReactionsBar: React.FC<ReactionsBarProps> = ({
2626
{
2727
content: emoji,
2828
schema: "unicode",
29-
reference: message.xmtpID,
29+
reference: message.id,
3030
action: "added",
3131
},
3232
ContentTypeReaction,
3333
);
3434
},
35-
[conversation, message.xmtpID, sendMessage],
35+
[conversation, message.id, sendMessage],
3636
);
3737

3838
return (

apps/react/src/components/library/ReactionsContent.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,13 @@ export const ReactionsContent: React.FC<ReactionsContentProps> = ({
6868
{
6969
content: emoji,
7070
schema: "unicode",
71-
reference: message.xmtpID,
71+
reference: message.id,
7272
action: hasReacted ? "removed" : "added",
7373
},
7474
ContentTypeReaction,
7575
);
7676
},
77-
[
78-
client?.address,
79-
conversation,
80-
emojiReactions,
81-
message.xmtpID,
82-
sendMessage,
83-
],
77+
[client?.address, conversation, emojiReactions, message.id, sendMessage],
8478
);
8579

8680
return (

packages/react-sdk/README.md

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,7 @@ yarn add react @xmtp/react-sdk @xmtp/xmtp-js @xmtp/content-type-reaction @xmtp/c
4343

4444
### Buffer polyfill
4545

46-
The Node Buffer API must be polyfilled in some cases. To do so, add the `buffer` dependency to your project and then polyfill it in your entry file.
47-
48-
**Example**
49-
50-
```ts
51-
import { Buffer } from "buffer";
52-
53-
window.Buffer = window.Buffer ?? Buffer;
54-
```
46+
If you run into issues with Buffer and polyfills, see this [solution](https://xmtp.org/docs/faq#why-is-my-app-failing-with-a-buffer-is-not-found-error).
5547

5648
#### Create React App
5749

packages/react-sdk/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
"@xmtp/content-type-reply": "^1.1.11",
9696
"@xmtp/content-type-text": "^1.0.0",
9797
"@xmtp/tsconfig": "workspace:*",
98-
"@xmtp/xmtp-js": "^12.0.0",
98+
"@xmtp/xmtp-js": "^12.1.0",
9999
"eslint": "^8.57.0",
100100
"eslint-config-xmtp-web": "workspace:*",
101101
"fake-indexeddb": "^6.0.0",
@@ -120,7 +120,7 @@
120120
"@xmtp/content-type-reaction": "^1.1.7",
121121
"@xmtp/content-type-remote-attachment": "^1.1.8",
122122
"@xmtp/content-type-reply": "^1.1.9",
123-
"@xmtp/xmtp-js": "^12.0.0",
123+
"@xmtp/xmtp-js": "^12.1.0",
124124
"react": "^16.14.0 || ^17 || ^18"
125125
},
126126
"engines": {

packages/react-sdk/src/contexts/XMTPContext.tsx

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { createContext, useMemo, useState } from "react";
1+
import { createContext, useMemo, useRef, useState } from "react";
22
import type { Client } from "@xmtp/xmtp-js";
3-
import Dexie from "dexie";
43
import type { ContentCodec } from "@xmtp/content-type-primitives";
4+
import type Dexie from "dexie";
55
import type {
66
ContentTypeConfiguration,
77
ContentTypeMessageProcessors,
88
ContentTypeMessageValidators,
99
} from "@/helpers/caching/db";
10-
import { getDbInstance } from "@/helpers/caching/db";
1110
import { combineNamespaces } from "@/helpers/combineNamespaces";
1211
import { combineMessageProcessors } from "@/helpers/combineMessageProcessors";
1312
import { combineCodecs } from "@/helpers/combineCodecs";
@@ -23,9 +22,13 @@ export type XMTPContextValue = {
2322
*/
2423
codecs: ContentCodec<any>[];
2524
/**
26-
* Local DB instance
25+
* Content type configurations used by the XMTP client instance
2726
*/
28-
db: Dexie;
27+
contentTypeConfigs?: ContentTypeConfiguration[];
28+
/**
29+
* Reference to Dexie database instance
30+
*/
31+
dbRef: React.MutableRefObject<Dexie | null>;
2932
/**
3033
* Namespaces for content types
3134
*/
@@ -44,11 +47,10 @@ export type XMTPContextValue = {
4447
validators: ContentTypeMessageValidators;
4548
};
4649

47-
const initialDb = new Dexie("__XMTP__");
48-
4950
export const XMTPContext = createContext<XMTPContextValue>({
5051
codecs: [],
51-
db: initialDb,
52+
contentTypeConfigs: [],
53+
dbRef: { current: null },
5254
namespaces: {},
5355
processors: {},
5456
setClient: () => {},
@@ -73,6 +75,7 @@ export const XMTPProvider: React.FC<XMTPProviderProps> = ({
7375
contentTypeConfigs,
7476
}) => {
7577
const [client, setClient] = useState<Client | undefined>(initialClient);
78+
const dbRef = useRef<Dexie | null>(null);
7679

7780
// combine all message processors
7881
const processors = useMemo(
@@ -98,28 +101,19 @@ export const XMTPProvider: React.FC<XMTPProviderProps> = ({
98101
[contentTypeConfigs],
99102
);
100103

101-
// DB instance for caching
102-
const db = useMemo(
103-
() =>
104-
getDbInstance({
105-
db: initialDb,
106-
contentTypeConfigs,
107-
}),
108-
[contentTypeConfigs],
109-
);
110-
111104
// memo-ize the context value to prevent unnecessary re-renders
112105
const value = useMemo(
113106
() => ({
114107
client,
115108
codecs,
116-
db,
109+
contentTypeConfigs: contentTypeConfigs ?? [],
110+
dbRef,
117111
namespaces,
118112
processors,
119113
setClient,
120114
validators,
121115
}),
122-
[client, codecs, db, namespaces, processors, validators],
116+
[client, codecs, contentTypeConfigs, namespaces, processors, validators],
123117
);
124118

125119
return <XMTPContext.Provider value={value}>{children}</XMTPContext.Provider>;

packages/react-sdk/src/helpers/caching/consent.test.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
const testWallet1 = createRandomWallet();
1515
const testWallet2 = createRandomWallet();
1616

17-
const db = getDbInstance();
17+
const db = await getDbInstance();
1818

1919
beforeEach(async () => {
2020
await clearCache(db);
@@ -25,50 +25,59 @@ describe("Consent helpers", () => {
2525
it("should add a new entry and update it", async () => {
2626
const undefinedEntry = await getCachedConsentEntry(
2727
testWallet1.account.address,
28+
"address",
2829
testWallet2.account.address,
2930
db,
3031
);
3132
expect(undefinedEntry).toBeUndefined();
3233
await putConsentState(
3334
testWallet1.account.address,
35+
"address",
3436
testWallet2.account.address,
3537
"allowed",
3638
db,
3739
);
3840
const entry = await getCachedConsentEntry(
3941
testWallet1.account.address,
42+
"address",
4043
testWallet2.account.address,
4144
db,
4245
);
4346
expect(entry).toEqual({
44-
peerAddress: testWallet2.account.address,
47+
type: "address",
48+
value: testWallet2.account.address,
4549
state: "allowed",
4650
walletAddress: testWallet1.account.address,
4751
});
4852
const state = await getCachedConsentState(
4953
testWallet1.account.address,
54+
"address",
5055
testWallet2.account.address,
5156
db,
5257
);
5358
expect(state).toBe("allowed");
5459
await putConsentState(
5560
testWallet1.account.address,
61+
"address",
5662
testWallet2.account.address,
5763
"denied",
5864
db,
5965
);
6066
const updatedEntry = await getCachedConsentEntry(
6167
testWallet1.account.address,
68+
"address",
6269
testWallet2.account.address,
6370
db,
6471
);
6572
expect(updatedEntry).toEqual({
66-
peerAddress: testWallet2.account.address,
73+
value: testWallet2.account.address,
74+
type: "address",
6775
state: "denied",
6876
walletAddress: testWallet1.account.address,
6977
});
7078
const updatedState = await getCachedConsentState(
7179
testWallet1.account.address,
80+
"address",
7281
testWallet2.account.address,
7382
db,
7483
);
@@ -81,12 +90,14 @@ describe("Consent helpers", () => {
8190
await bulkPutConsentState(
8291
[
8392
{
84-
peerAddress: testWallet2.account.address,
93+
value: testWallet2.account.address,
94+
type: "address",
8595
state: "allowed",
8696
walletAddress: testWallet1.account.address,
8797
},
8898
{
89-
peerAddress: testWallet1.account.address,
99+
value: testWallet1.account.address,
100+
type: "address",
90101
state: "denied",
91102
walletAddress: testWallet2.account.address,
92103
},
@@ -117,6 +128,7 @@ describe("Consent helpers", () => {
117128
const testClient = await Client.create(testWallet1, { env: "local" });
118129
await putConsentState(
119130
testWallet1.account.address,
131+
"address",
120132
testWallet2.account.address,
121133
"denied",
122134
db,

packages/react-sdk/src/helpers/caching/consent.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,45 @@
11
import type { Table } from "dexie";
22
import type Dexie from "dexie";
33
import { Mutex } from "async-mutex";
4-
import type { Client, ConsentState } from "@xmtp/xmtp-js";
4+
import type { Client, ConsentState, ConsentListEntryType } from "@xmtp/xmtp-js";
55
import { ConsentListEntry } from "@xmtp/xmtp-js";
66

77
export type CachedConsentEntry = {
8-
peerAddress: string;
8+
type: ConsentListEntryType;
9+
value: string;
910
state: ConsentState;
1011
walletAddress: string;
1112
};
1213

1314
export type CachedConsentTable = Table<CachedConsentEntry, string>;
1415

1516
export type CachedConsentEntryMap = {
16-
[peerAddress: string]: ConsentListEntry;
17+
[value: string]: ConsentListEntry;
1718
};
1819

1920
/**
20-
* Retrieve a cached consent entry by wallet and peer address
21+
* Retrieve a cached consent entry by wallet address, type, and value
2122
*
2223
* @returns The cached consent entry if found, otherwise `undefined`
2324
*/
2425
export const getCachedConsentEntry = async (
2526
walletAddress: string,
26-
peerAddress: string,
27+
type: ConsentListEntryType,
28+
value: string,
2729
db: Dexie,
2830
) => {
2931
const consentTable = db.table("consent") as CachedConsentTable;
3032
return consentTable
3133
.where({
3234
walletAddress,
33-
peerAddress,
35+
type,
36+
value,
3437
})
3538
.first();
3639
};
3740

3841
/**
39-
* Retrieve all cached consent entries
42+
* Retrieve all cached consent entries for a given wallet address
4043
*
4144
* @returns An array of ConsentListEntry instances
4245
*/
@@ -47,7 +50,7 @@ export const getCachedConsentEntries = async (
4750
const consentTable = db.table("consent") as CachedConsentTable;
4851
const entries = await consentTable.where({ walletAddress }).toArray();
4952
return entries.map((entry) =>
50-
ConsentListEntry.fromAddress(entry.peerAddress, entry.state),
53+
ConsentListEntry.fromAddress(entry.value, entry.state),
5154
);
5255
};
5356

@@ -72,16 +75,17 @@ export const getCachedConsentEntriesMap = async (
7275
};
7376

7477
/**
75-
* Retrieve a cached consent state by wallet and peer address
78+
* Retrieve a cached consent state by wallet address, type, and value
7679
*
7780
* @returns The cached consent state if found, otherwise `undefined`
7881
*/
7982
export const getCachedConsentState = async (
8083
walletAddress: string,
81-
peerAddress: string,
84+
type: ConsentListEntryType,
85+
value: string,
8286
db: Dexie,
8387
) => {
84-
const entry = await getCachedConsentEntry(walletAddress, peerAddress, db);
88+
const entry = await getCachedConsentEntry(walletAddress, type, value, db);
8589
return entry?.state ?? "unknown";
8690
};
8791

@@ -97,21 +101,22 @@ export const loadConsentListFromCache = async (client: Client, db: Dexie) => {
97101
const putConsentStateMutex = new Mutex();
98102

99103
/**
100-
* Add or update a peer address's consent state
104+
* Add or update a consent state
101105
*/
102106
export const putConsentState = async (
103107
walletAddress: string,
104-
peerAddress: string,
108+
type: ConsentListEntryType,
109+
value: string,
105110
state: ConsentState,
106111
db: Dexie,
107112
) =>
108113
putConsentStateMutex.runExclusive(async () => {
109114
const consentTable = db.table("consent") as CachedConsentTable;
110-
await consentTable.put({ peerAddress, state, walletAddress });
115+
await consentTable.put({ type, value, state, walletAddress });
111116
});
112117

113118
/**
114-
* Add or update multiple peer addresses' consent state
119+
* Add or update multiple consent states
115120
*/
116121
export const bulkPutConsentState = async (
117122
entries: CachedConsentEntry[],

0 commit comments

Comments
 (0)