Skip to content

Commit 7058446

Browse files
committed
fix: normalize updated page items to always return ContactChange
1 parent 3e999af commit 7058446

3 files changed

Lines changed: 68 additions & 41 deletions

File tree

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ type UpdatedPageBase = {
9393
totalContacts: number;
9494
};
9595

96-
type UpdatedPage =
97-
| (UpdatedPageBase & { mode: 'full'; items: Contact[] })
98-
| (UpdatedPageBase & { mode: 'delta'; items: ContactChange[] });
96+
type UpdatedPage = UpdatedPageBase & {
97+
mode: 'delta' | 'full';
98+
items: ContactChange[];
99+
};
99100
```
100101

101102
API reference & examples
@@ -108,7 +109,7 @@ API reference & examples
108109
| `type PhoneNumberUpdate` | Represents an individual phone number that changed within a contact delta (`previous``current`). |
109110
| `type PhoneNumberChanges` | Buckets the numbers added/removed/updated in a `ContactChange`. Useful when reconciling diffs. |
110111
| `type ContactChange` | Extends `Contact` with delta metadata (`changeType`, `isDeleted`, `phoneNumberChanges`, and an optional `previous` snapshot). |
111-
| `type UpdatedPage` | Union returned by paging APIs (`mode`, `items`, `nextSince`, and `totalContacts` so you know the device book size). |
112+
| `type UpdatedPage` | Page returned by paging APIs (`mode`, `items`, `nextSince`, and `totalContacts` so you know the device book size). Items are always `ContactChange[]`. |
112113

113114
### Functions (promise / async)
114115

@@ -151,7 +152,7 @@ API reference & examples
151152
```ts
152153
const page = await getUpdatedSincePaged(lastToken, 0, 200);
153154
if (page.mode === 'full') {
154-
page.items.forEach((contact) => console.log('Full contact', contact.id));
155+
page.items.forEach((change) => console.log('Full contact', change.id));
155156
} else {
156157
page.items.forEach((change) => console.log(change.changeType, change.id));
157158
}

example/src/screens/ContactsDemoScreen.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,10 @@ const ContactsDemoScreen = ({ onTotalContactsChange }: Props) => {
105105
if (response.items.length === 0) {
106106
return false;
107107
}
108-
if (response.mode === 'delta') {
109-
collected.push(...response.items);
110-
} else {
108+
if (response.mode === 'full') {
111109
usedFullFallback = true;
112-
collected.push(
113-
...response.items.map((contact) => ({
114-
...contact,
115-
changeType: 'created' as ContactChange['changeType'],
116-
isDeleted: false,
117-
phoneNumberChanges: {
118-
created: contact.phoneNumbers,
119-
deleted: [],
120-
updated: [],
121-
},
122-
previous: null,
123-
}))
124-
);
125110
}
111+
collected.push(...response.items);
126112
pageOffset += response.items.length;
127113
return true;
128114
}

src/index.tsx

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,11 @@ type UpdatedPageBase = {
1212
totalContacts: number;
1313
};
1414

15-
type DeltaPage = UpdatedPageBase & {
16-
mode: 'delta';
15+
export type UpdatedPage = UpdatedPageBase & {
16+
mode: 'delta' | 'full';
1717
items: ContactChange[];
1818
};
1919

20-
type FullPage = UpdatedPageBase & {
21-
mode: 'full';
22-
items: Contact[];
23-
};
24-
25-
export type UpdatedPage = DeltaPage | FullPage;
26-
2720
type UpdatedPageHandler = (
2821
page: UpdatedPage
2922
) => void | boolean | Promise<void | boolean>;
@@ -95,6 +88,47 @@ function resolveTotalContacts(nativeResult: {
9588
return Array.isArray(nativeResult.items) ? nativeResult.items.length : 0;
9689
}
9790

91+
function synthesizeChangeFromContact(contact: Contact): ContactChange {
92+
const numbers = Array.isArray(contact.phoneNumbers)
93+
? contact.phoneNumbers
94+
: [];
95+
96+
return {
97+
...contact,
98+
changeType: 'created',
99+
isDeleted: false,
100+
phoneNumberChanges: {
101+
created: numbers,
102+
deleted: [],
103+
updated: [],
104+
},
105+
previous: null,
106+
};
107+
}
108+
109+
function normalizeDeltaChange(change: ContactChange): ContactChange {
110+
const changes = change.phoneNumberChanges ?? {
111+
created: [],
112+
deleted: [],
113+
updated: [],
114+
};
115+
116+
return {
117+
...change,
118+
changeType: change.changeType ?? (change.isDeleted ? 'deleted' : 'updated'),
119+
isDeleted: Boolean(change.isDeleted),
120+
phoneNumberChanges: {
121+
created: Array.isArray(changes.created) ? changes.created : [],
122+
deleted: Array.isArray(changes.deleted) ? changes.deleted : [],
123+
updated: Array.isArray(changes.updated) ? changes.updated : [],
124+
},
125+
previous:
126+
change.previous && typeof change.previous === 'object'
127+
? change.previous
128+
: null,
129+
};
130+
}
131+
98132
// Convenience: fetch the entire contacts list in one call.
99133
export async function getAll(): Promise<Contact[]> {
100134
const contacts = await ContactsLastUpdated.getAll();
@@ -116,25 +150,31 @@ const getUpdatedSincePagedImpl = async (
116150
);
117151
const nextSince = result.nextSince ?? '';
118152
const totalContacts = resolveTotalContacts(result);
119-
if (result.mode === 'full') {
120-
return {
121-
mode: 'full',
122-
items: result.items,
123-
nextSince,
124-
totalContacts,
125-
};
126-
}
127-
if (!nextSince && since.trim().length === 0) {
153+
const shouldTreatAsFull =
154+
result.mode === 'full' || (since.trim().length === 0 && !nextSince);
155+
156+
if (shouldTreatAsFull) {
157+
const contacts =
158+
result.mode === 'full'
159+
? result.items
160+
: (result as unknown as { items: Contact[] }).items;
128161
return {
129162
mode: 'full',
130-
items: (result as unknown as { items: Contact[] }).items,
163+
items: Array.isArray(contacts)
164+
? contacts.map(synthesizeChangeFromContact)
165+
: [],
131166
nextSince,
132167
totalContacts,
133168
};
134169
}
170+
171+
const deltas =
172+
result.mode === 'delta'
173+
? result.items
174+
: (result as unknown as { items: ContactChange[] }).items;
135175
return {
136176
mode: 'delta',
137-
items: result.items,
177+
items: Array.isArray(deltas) ? deltas.map(normalizeDeltaChange) : [],
138178
nextSince,
139179
totalContacts,
140180
};

0 commit comments

Comments
 (0)