Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 19 additions & 37 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,31 @@
# Change log

## 1.2.0-rc.4

* Fixed: `host()` resolves with the live connection state instead of always `null`
* Fixed: `onConnection(false)` fires when a retired or background connection closes
* Fixed: notification taps are recorded after launching the app, so tap-opened screens land on top
* Fixed: `host()` reports `false` when there was nothing to subscribe instead of a spurious open

## 1.2.0-rc.3

* Added: `getInitialNotification()`, `onNotificationOpened()`, and the `PushNotificationOpened` type (topic + data)
* Added: live push connection open/close events surfaced via `onOpen`/`onClose`
* Fixed: notification taps delivered reliably once, via a `PushTaps` queue
* Fixed: foreground notifications de-duplicated per title
* Updated: `resume` leaves a live Push host's connection untouched

## 1.2.0-rc.2

* Added: `Push.getInitialNotification()` returns the notification whose tap launched the app
* Added: `Push.onNotificationOpened(callback)` fires on each notification tap
* Added: `Push.backgroundStatus()` reports Android exact-alarm, battery, and foreground-service state
* Added: `Push.requestExactAlarms()` and `Push.requestIgnoreBatteryOptimizations()` helpers
* Added: `SubscribeOptions.notifyInForeground` to post notifications while the app is visible
* Added: `PushBackgroundStatus` and `PushNotificationOpened` exported types
* Fixed: reliable Android background wake-ups via a periodic watchdog and alternating jobs
* Fixed: `resume` keeps subscriptions only for the same signed-in user
* Fixed: notification images are size-capped to prevent out-of-memory crashes

## 1.2.0-rc.1
## 2.0.0

* Breaking: `Teams.createMembership` takes `membershipId` after `roles`; positional callers must update
* Added: `Account.listPasskeys`, `createPasskey`, `getPasskey`, `updatePasskey` and `deletePasskey`
* Added: `Account.updatePasskeyVerification` to finish registering a passkey
* Added: `Account.createPasskeyToken` and `Account.updatePasskeyToken` for passkey sign-in
* Added: `Passkey`, `PasskeyList`, `PasskeyChallenge` and `TokenSecret` models
* Added: `Push` service for MQTT realtime push with Android background delivery
* Added: `Topic` builder, `Client.setPushEndpoint()` and `Client.setPushClientId()`
* Added: background push notifications render the server `notification` title, body, and image
* Added: Android shows notification images (BigPictureStyle) and long bodies (BigTextStyle)
* Added: the SDK requests Android 13+ `POST_NOTIFICATIONS` itself on first background subscribe
* Added: notification `data` now carries `topic` and `payload`
* Fixed: a server-sent title dedupes to one notification per topic instead of one per subscription

## 1.2.0-rc.0

* Added: `getInitialNotification()`, `onNotificationOpened()` and the `PushNotificationOpened` type
* Added: `onOpen`/`onClose` events for live push connections
* Added: `Push.backgroundStatus()`, `requestExactAlarms()` and `requestIgnoreBatteryOptimizations()` for Android
* Added: `SubscribeOptions.notifyInForeground` to post notifications while the app is visible
* Added: the SDK requests Android 13+ `POST_NOTIFICATIONS` on first background subscribe
* Added: `Analytics` service with `createEvent`
* Added: `Apps` service to manage OAuth2 apps, keys, secrets, and installations
* Added: `Oauth2` service for authorization, consent, device, PAR, and token flows
* Added: `Push` service for MQTT realtime push with Android background delivery
* Added: `Topic` builder and native TCP modules for push
* Added: `Avatars.updatePhoto()` and `Avatars.deletePhoto()` methods
* Added: `App`, `Oauth2*`, and `Account` models
* Added: `state` parameter to `Account.createOAuth2Session` and `Account.createOAuth2Token`
* Added: `duration` on `createEmailPasswordSession` and `current` on `deleteSessions`
* Added: `total` parameter to `Account.listSessions`, `Locale` list methods and `listTransactions`
* Added: `Webflow` to the `OAuthProvider` enum
* Fixed: response models fill missing fields from spec defaults instead of throwing
* Updated: SDK now targets Appwrite 2.4 (`X-Appwrite-Response-Format: 2.4.0`)
* Fixed: cookie session fallback is guarded to Expo web where `localStorage` exists

## 1.1.0
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Appwrite React Native SDK

![License](https://img.shields.io/github/license/appwrite/sdk-for-react-native.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.3.0-blue.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.4.0-blue.svg?style=flat-square)
[![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)

**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ.
**This SDK targets Appwrite server version 2.4.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ.

Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the React Native SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

Expand Down
1 change: 1 addition & 0 deletions docs/examples/account/create-o-auth-2-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ account.createOAuth2Session({
success: 'https://example.com', // optional
failure: 'https://example.com', // optional
scopes: [], // optional
state: '<STATE>', // optional
});
```
1 change: 1 addition & 0 deletions docs/examples/account/create-o-auth-2-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ account.createOAuth2Token({
success: 'https://example.com', // optional
failure: 'https://example.com', // optional
scopes: [], // optional
state: '<STATE>', // optional
});
```
13 changes: 13 additions & 0 deletions docs/examples/account/create-passkey-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.createPasskeyToken();

console.log(result);
```
16 changes: 16 additions & 0 deletions docs/examples/account/create-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.createPasskey({
passkeyId: '<PASSKEY_ID>', // optional
name: '<NAME>', // optional
});

console.log(result);
```
15 changes: 15 additions & 0 deletions docs/examples/account/delete-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.deletePasskey({
passkeyId: '<PASSKEY_ID>',
});

console.log(result);
```
15 changes: 15 additions & 0 deletions docs/examples/account/get-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.getPasskey({
passkeyId: '<PASSKEY_ID>',
});

console.log(result);
```
16 changes: 16 additions & 0 deletions docs/examples/account/list-passkeys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.listPasskeys({
queries: [], // optional
total: false, // optional
});

console.log(result);
```
16 changes: 16 additions & 0 deletions docs/examples/account/update-passkey-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.updatePasskeyToken({
challengeId: '<CHALLENGE_ID>',
credential: {},
});

console.log(result);
```
16 changes: 16 additions & 0 deletions docs/examples/account/update-passkey-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.updatePasskeyVerification({
passkeyId: '<PASSKEY_ID>',
credential: {},
});

console.log(result);
```
16 changes: 16 additions & 0 deletions docs/examples/account/update-passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```javascript
import { Client, Account } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const account = new Account(client);

const result = await account.updatePasskey({
passkeyId: '<PASSKEY_ID>',
name: '<NAME>',
});

console.log(result);
```
27 changes: 27 additions & 0 deletions docs/examples/analytics/create-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```javascript
import { Client, Analytics } from 'react-native-appwrite';

const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

const analytics = new Analytics(client);

const result = await analytics.createEvent({
propertyId: '<PROPERTY_ID>',
name: '<NAME>',
url: 'https://example.com',
domain: '<DOMAIN>', // optional
referrer: '<REFERRER>', // optional
screenWidth: null, // optional
sessionHash: '<SESSION_HASH>', // optional
scrollDepth: 0, // optional
engagementTime: 0, // optional
props: [], // optional
userId: '<USER_ID>', // optional
ip: '<IP>', // optional
userAgent: '<USER_AGENT>', // optional
});

console.log(result);
```
2 changes: 1 addition & 1 deletion docs/examples/oauth2/authorize-post.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const result = await oauth2.authorizePost({
prompt: '<PROMPT>', // optional
maxAge: 0, // optional
authorizationDetails: '<AUTHORIZATION_DETAILS>', // optional
resource: '', // optional
resource: [], // optional
audience: '<AUDIENCE>', // optional
requestUri: '<REQUEST_URI>', // optional
});
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/oauth2/authorize.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const result = await oauth2.authorize({
prompt: '<PROMPT>', // optional
maxAge: 0, // optional
authorizationDetails: '<AUTHORIZATION_DETAILS>', // optional
resource: '', // optional
resource: [], // optional
audience: '<AUDIENCE>', // optional
requestUri: '<REQUEST_URI>', // optional
});
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/oauth2/create-device-authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const result = await oauth2.createDeviceAuthorization({
clientId: '<CLIENT_ID>', // optional
scope: '<SCOPE>', // optional
authorizationDetails: '<AUTHORIZATION_DETAILS>', // optional
resource: '', // optional
resource: [], // optional
audience: '<AUDIENCE>', // optional
});

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/oauth2/create-par.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const result = await oauth2.createPAR({
prompt: '<PROMPT>', // optional
maxAge: 0, // optional
authorizationDetails: '<AUTHORIZATION_DETAILS>', // optional
resource: '', // optional
resource: [], // optional
audience: '<AUDIENCE>', // optional
});

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/oauth2/create-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const result = await oauth2.createToken({
clientSecret: '<CLIENT_SECRET>', // optional
codeVerifier: '<CODE_VERIFIER>', // optional
redirectUri: 'https://example.com', // optional
resource: '', // optional
resource: [], // optional
audience: '<AUDIENCE>', // optional
});

Expand Down
1 change: 1 addition & 0 deletions docs/examples/teams/create-membership.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const teams = new Teams(client);
const result = await teams.createMembership({
teamId: '<TEAM_ID>',
roles: ['editor'],
membershipId: '<MEMBERSHIP_ID>', // optional
email: 'email@example.com', // optional
userId: '<USER_ID>', // optional
phone: '+12065550100', // optional
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "react-native-appwrite",
"homepage": "https://appwrite.io/support",
"description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API",
"version": "1.2.0-rc.4",
"version": "2.0.0",
"license": "BSD-3-Clause",
"main": "dist/cjs/sdk.js",
"exports": {
Expand Down
48 changes: 46 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,50 @@ export const JSONbig = {
stringify: JSONbigSerializer.stringify,
};

type DefaultsShape = string | { items: DefaultsShape };

type DefaultsModel = {
defaults?: Record<string, unknown>;
properties?: Record<string, DefaultsShape>;
};

type Defaults = {
root: DefaultsShape;
models: Record<string, DefaultsModel>;
};

export function applyDefaults<T>(value: T, tree: Defaults): T {
return fillDefaults(value, tree.root, tree.models) as T;
}

function fillDefaults(
value: unknown,
shape: DefaultsShape,
models: Record<string, DefaultsModel>,
): unknown {
if (typeof shape !== 'string') {
if (!Array.isArray(value)) {
return value;
}
const items = shape.items;
return value.map((item) => fillDefaults(item, items, models));
}
const model = models[shape];
if (!model || typeof value !== 'object' || value === null) {
return value;
}
if (Array.isArray(value)) {
return value;
}
const result: Record<string, unknown> = { ...model.defaults, ...value };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserve response toString when cloning models

Client.call attaches a non-enumerable JSON toString to parsed responses, but this spread drops it whenever a response passes through applyDefaults. As a result, affected API results now stringify as [object Object] instead of JSON; preserving the custom method on the cloned root would avoid this compatibility regression.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/client.ts
Line: 83

Comment:
**Preserve response toString when cloning models**

`Client.call` attaches a non-enumerable JSON `toString` to parsed responses, but this spread drops it whenever a response passes through `applyDefaults`. As a result, affected API results now stringify as `[object Object]` instead of JSON; preserving the custom method on the cloned root would avoid this compatibility regression.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · bug · Reply if this doesn't apply.

for (const [key, child] of Object.entries(model.properties ?? {})) {
if (key in result) {
result[key] = fillDefaults(result[key], child, models);
}
}
return result;
}

type Payload = {
[key: string]: any;
};
Expand Down Expand Up @@ -218,8 +262,8 @@ class Client {
'x-sdk-name': 'React Native',
'x-sdk-platform': 'client',
'x-sdk-language': 'reactnative',
'x-sdk-version': '1.2.0-rc.4',
'X-Appwrite-Response-Format': '2.3.0',
'x-sdk-version': '2.0.0',
'X-Appwrite-Response-Format': '2.4.0',
};

/**
Expand Down
Loading
Loading