diff --git a/.env.example b/.env.example index 5df5a8df0d..a2f969f036 100644 --- a/.env.example +++ b/.env.example @@ -11,8 +11,6 @@ ASSET_PREFIX= INKEEP_CHAT_ENABLED= INKEEP_CHAT_API_KEY= -INKEEP_CHAT_INTEGRATION_ID= -INKEEP_CHAT_ORGANIZATION_ID= INSIGHTS_ENABLED=false INSIGHTS_DEBUG=true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3607502668..b7af73e6ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -340,3 +340,153 @@ API references make use of [partials](content/partials/) where content is reused The Control API is a REST API and the spec can be found in [`static/open-specs/control-v1.yaml](static/open-specs/control-v1.yaml). This is then referenced in [`src/pages/api/control-api.tsx](src/pages/api/control-api.tsx) for rendering. + +### Contributing to Examples + +The `examples` directory contains feature-specific demonstrations of how to use Ably in different environments, organized by framework. Each example resides in a structured path like `/examples//react` or `/examples//javascript`, showcasing practical implementations for various use cases. + +#### Example file structure + +1. Create a new directory for your example under `/examples`, this should be named after the feature/example you're building, e.g. `chat-typing-indicator`. +2. Inside this directory, create a subdirectory named after the language or framework (e.g., `javascript`, `react`). You can have both `javascript` and `react` subdirectories within the same example directory. + +Currently, we support Javascript (Typescript) and React, both using Vite. + +#### Create the project + +To create a new project using Vite, follow the instructions below for either JavaScript/TypeScript or React. + +##### JavaScript/TypeScript + +1. Open your terminal. +2. Navigate to the directory where you want to create your project. +3. Run the following command to create a new Vite project: + +```sh +npm create vite@latest javascript -- --template vanilla +``` + +4. Navigate into your project directory: + +```sh +cd javascript +``` + +##### React + +1. Open your terminal. +2. Navigate to the directory where you want to create your project. +3. Run the following command to create a new Vite project with React template: + +```sh +npm create vite@latest react -- --template react +``` + +4. Navigate into your project directory: + +```sh +cd react +``` + +After following these steps, you will have a Vite project set up with JavaScript/TypeScript or React, ready for further development. + +#### README.md + +Each example must include a `README.md` file with instructions to get the developer running the example locally as quick as possible. These instructions should follow the structure below: + +- **Introduction**: Describe the Ably features used, what problem is this solving for the intended audience? +- **Resources**: List components and functions from the Ably SDKs and their purposes. +- **Getting started**: Provide step-by-step instructions: + 1. Clone the docs repository. + 2. Navigate to the examples directory. + 3. Rename environment variable files. (`.env.example` -> `.env.local`) + 4. Update environment variables. + 5. Install dependencies with `yarn install`. + 6. Run the project. +- **Opening in CodeSandbox**: Instructions for opening the example in CodeSandbox. (At this moment, this heading contains the renaming of the environment variables) + +#### NPM Workspaces + +Add entries for each example to the `/examples/package.json` file under `workspaces` and `scripts`, an example of this is shown below: + +```json +"workspaces": [ + "chat-typing-indicator/react", + "chat-typing-indicator/javascript", +], +"scripts": { + "chat-typing-indicator-javascript": "yarn workspace chat-typing-indicator-javascript dev", + "chat-typing-indicator-react": "yarn workspace chat-typing-indicator-react dev", +}, +``` + +The intention of the scripts entries is to make the command simpler for the developer to run locally. + +#### Shared configurations + +All examples use config from the examples root directory. Update these files inside your project directory: + +- `vite.config.ts`: + + ```typescript + import { defineConfig } from 'vite'; + import baseConfig from '../../vite.config'; + + export default defineConfig({ + ...baseConfig, + envDir: '../../', + }); + ``` + +- `tailwind.config.ts`: + + ```typescript + import baseConfig from '../../tailwind.config'; + import type { Config } from 'tailwindcss'; + + const config: Config = { + ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], + }; + + export default config; + ``` + +- `tsconfig.json`: + + ```json + { + "extends": "../../tsconfig.json" + } + ``` + +- `vite-env.d.ts` (Javascript only): + + ```typescript + interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; + } + + interface ImportMeta { + readonly env: ImportMetaEnv; + } + ``` + + +#### Styling consistency + +For styling consistency purposes, each example will need to use [Franken-ui](https://franken-ui.dev/) for components, and any other styling to use tailwindcss. Some examples for this are the button and input components below: + +**Button:** + +```html + +``` + +**Input:** + +```html + +``` diff --git a/content/account/control-api.textile b/content/account/control-api.textile index 4140dd0e5f..c07e6c3d84 100644 --- a/content/account/control-api.textile +++ b/content/account/control-api.textile @@ -169,13 +169,10 @@ The only operation available at the account-level is to retrieve account-wide st To retrieve account-level statistics: ```[sh] -curl --location --request POST 'https://control.ably.net/v1/accounts/${ACCOUNT_ID}/stats' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: Bearer ${ACCESS_TOKEN}' \ ---data-raw '{ - "unit": "minute", - "limit": 2 -}' +curl --request GET \ + --url 'https://control.ably.net/v1/accounts/${ACCOUNT_ID}/stats?unit=minute&limit=2' \ + --header 'Authorization: Bearer ${ACCESS_TOKEN}' \ + --header 'Content-Type: application/json' ``` See the "API reference":/docs/api/control-api#tag/accounts/paths/~1accounts~1{account_id}~1stats/get for information on the request body. diff --git a/content/api/rest-api.textile b/content/api/rest-api.textile index 17debdb98c..08f2f56387 100644 --- a/content/api/rest-api.textile +++ b/content/api/rest-api.textile @@ -29,6 +29,7 @@ jump_to: - list channel subscriptions#list-channel-subscriptions - list channels#list-channels - publish directly to device#push-publish + - publish via batch push API#push-publish-batch Authentication API: - requestToken#request-token - revokeTokens#revoke-tokens @@ -1152,6 +1153,56 @@ A successful request returns an empty response. An unsuccessful request returns an error. +h3(#push-publish-batch). Publish via batch push API + +Convenience endpoint to deliver multiple push notification payloads to multiple devices or browsers in a single request by specifying a list of recipients and corresponding payloads. +Currently, the batch push endpoint allows a maximum of 10,000 notifications per request (note that each recipient for a given payload counts as a separate notification). + +h6. POST rest.ably.io/push/batch/publish + +The request body is an array of objects of the form: + +bc[json]. { + recipient: + payload: +} + +Where the recipient and payload fields are the same as those used in the "Publish a push notification to a single device":#push-publish endpoint. + +Example request: + +bc[sh]. curl -X POST https://rest.ably.io/push/admin/batch/publish \ + -u "{{API_KEY}}" \ + -H "Content-Type: application/json" \ + --data \ + ' +[ + { + "recipient": { + "deviceId": "01ARZ3NDEKTSV4RRFFQ69G5FAV" + }, + "payload": { + "notification": { + "title": "Message 1", + "body": "Example push notification from Ably." + } + } + }, + { + "recipient": { + "clientId": "myClientId" + }, + "payload": { + "notification": { + "title": "Message 2", + "body": "Example push notification from Ably." + } + } + } +] +' + + h2(#authentication). Authentication h3(#request-token). Request an access token diff --git a/content/chat/connect.textile b/content/chat/connect.textile index c2036095c0..8ff0618cc2 100644 --- a/content/chat/connect.textile +++ b/content/chat/connect.textile @@ -50,7 +50,7 @@ const MyComponent = () => { ``` ```[swift] -let status = await chatClient.connection.status +let status = chatClient.connection.status ``` ```[kotlin] diff --git a/content/chat/index.textile b/content/chat/index.textile index 8e749a5c9b..86d1ec266a 100644 --- a/content/chat/index.textile +++ b/content/chat/index.textile @@ -1,6 +1,7 @@ --- title: About Chat meta_description: "Learn more about Ably Chat and the features that enable you to quickly build functionality into new and existing applications." +product: chat redirect_from: /docs/products/chat --- diff --git a/content/chat/rooms/index.textile b/content/chat/rooms/index.textile index 794656727e..f872101c62 100644 --- a/content/chat/rooms/index.textile +++ b/content/chat/rooms/index.textile @@ -270,7 +270,7 @@ const MyComponent = () => { ``` ```[swift] -let status = try await room.status +let status = room.status ``` ```[kotlin] @@ -288,7 +288,7 @@ blang[javascript,swift,kotlin]. ``` ```[swift] - let statusSubscription = try await room.onStatusChange() + let statusSubscription = room.onStatusChange() for await status in statusSubscription { print("Room status: \(status)") } diff --git a/content/chat/rooms/occupancy.textile b/content/chat/rooms/occupancy.textile index 0916c7f438..9de3af98ad 100644 --- a/content/chat/rooms/occupancy.textile +++ b/content/chat/rooms/occupancy.textile @@ -49,7 +49,7 @@ const MyComponent = () => { ``` ```[swift] -let occupancySubscription = try await room.occupancy.subscribe() +let occupancySubscription = room.occupancy.subscribe() for await event in occupancySubscription { occupancyInfo = "Connections: \(event.presenceMembers) (\(event.connections))" } diff --git a/content/chat/rooms/presence.textile b/content/chat/rooms/presence.textile index 01d55510b8..66bc1ba6cf 100644 --- a/content/chat/rooms/presence.textile +++ b/content/chat/rooms/presence.textile @@ -60,7 +60,7 @@ const MyComponent = () => { ``` ```[swift] -let presenceSubscription = try await room.presence.subscribe(events: [.enter, .leave, .update]) +let presenceSubscription = room.presence.subscribe(events: [.enter, .leave, .update]) for await event in presenceSubscription { print("Presence event `\(event.action)` from `\(event.clientId)` with data `\(event.data)`") } @@ -89,10 +89,10 @@ blang[javascript,swift]. ```[swift] // Subscribe to only 'enter' events: - let presenceSubscription = try await room.presence.subscribe(event: .enter) + let presenceSubscription = room.presence.subscribe(event: .enter) // Subscribe to 'update' and 'leave' events: - let presenceSubscription = try await room.presence.subscribe(events: [.leave, .update]) + let presenceSubscription = room.presence.subscribe(events: [.leave, .update]) ``` blang[react,kotlin]. diff --git a/content/chat/rooms/reactions.textile b/content/chat/rooms/reactions.textile index dbf8a9fa15..895afb2d34 100644 --- a/content/chat/rooms/reactions.textile +++ b/content/chat/rooms/reactions.textile @@ -45,7 +45,7 @@ const MyComponent = () => { ``` ```[swift] -let reactionSubscription = try await room.reactions.subscribe() +let reactionSubscription = room.reactions.subscribe() for await reaction in reactionSubscription { print("Received a reaction of type \(reaction.type), and metadata \(reaction.metadata)") } diff --git a/content/chat/rooms/typing.textile b/content/chat/rooms/typing.textile index 59dc51a51b..882af12eb4 100644 --- a/content/chat/rooms/typing.textile +++ b/content/chat/rooms/typing.textile @@ -49,7 +49,7 @@ const MyComponent = () => { ``` ```[swift] -let typingSubscription = try await room.typing.subscribe() +let typingSubscription = room.typing.subscribe() for await typing in typingSubscription { typingInfo = typing.currentlyTyping.isEmpty ? "" : "Typing: \(typing.currentlyTyping.joined(separator: ", "))..." diff --git a/content/livesync/mongodb/index.textile b/content/livesync/mongodb/index.textile index 5149c982ff..fec10c2b06 100644 --- a/content/livesync/mongodb/index.textile +++ b/content/livesync/mongodb/index.textile @@ -23,10 +23,6 @@ The integration rule exists as a "database connector" component that is entirely When a change event is received over the Change Streams API it is published to an Ably channel. When you configure the integration rule, you can specify how change events are mapped to individual Ably channels. Clients can then subscribe to database changes by subscribing to Ably channels. Ably "Auth":/docs/auth and "Capabilities":/docs/auth/capabilities control which channels a client can interact with. -h2(#development-status). Development status - -The MongoDB database connector is currently in beta status. Your "feedback":https://docs.google.com/forms/d/e/1FAIpQLSd00n1uxgXWPGvMjKwMVL1UDhFKMeh3bSrP52j9AfXifoU-Pg/viewform will help prioritize improvements and fixes for subsequent releases. - h2(#integration-rule). Integration rule h3(#create). Create a rule diff --git a/content/metadata-stats/metadata/subscribe.textile b/content/metadata-stats/metadata/subscribe.textile index e15b4d7a8b..3e536886b8 100644 --- a/content/metadata-stats/metadata/subscribe.textile +++ b/content/metadata-stats/metadata/subscribe.textile @@ -36,7 +36,7 @@ It is never possible to publish or be present on a metachannel, however you can The following is an example of a capability that provides access to subscribe to all metachannels: ```[json] -{"[meta]*":["subscribe"]} +{"[meta]*":["subscribe"]} ``` h2(#connection-lifecycle). Connection lifecycle events diff --git a/content/metadata-stats/stats.textile b/content/metadata-stats/stats.textile index 70e2b205d6..a5d2d97f83 100644 --- a/content/metadata-stats/stats.textile +++ b/content/metadata-stats/stats.textile @@ -36,45 +36,29 @@ Statistics are available as: h2(#account). Account statistics -Account statistics provide information about all applications in your account. - -You can retrieve account statistics from: - -* The "Control API or REST API":#account-api -* Your "account dashboard":#account-dashboard +Account statistics aggregate metrics from all applications in your account with additional "account-only metrics":#account-only relating to peak rates monitored and enforced at an account level. h3(#account-api). Account statistics via API -You can retrieve account statistics from the "Control API":/docs/account/control-api or the "REST API":/docs/api/rest-api. - -h4(#account-control). Account statistics via Control API - You can retrieve account statistics using the "Control API":/docs/account/control-api by making a call to the @accounts@ endpoint using your @accountId@ and @accessToken@. For example, to retrieve the last two minutes of account statistics: ```[curl] -curl --location --request POST 'https://control.ably.net/v1/accounts/${ACCOUNT_ID}/stats' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: Bearer ${ACCESS_TOKEN}' \ ---data-raw '{ - "unit": "minute", - "limit": 2 -}' -``` - -Use the @/me@ endpoint to find your @accountId@, or alternatively find it in the "*Settings*":https://ably.com/accounts/any/edit page of your account dashboard: - -```[curl] -curl --location --request GET 'https://control.ably.net/v1/me' \ ---header 'Authorization: Bearer ${ACCESS_TOKEN}' +curl --request GET \ + --url 'https://control.ably.net/v1/accounts/${ACCOUNT_ID}/stats?unit=minute&limit=2' \ + --header 'Authorization: Bearer ${ACCESS_TOKEN}' \ + --header 'Content-Type: application/json' ``` -h4(#account-rest). Account statistics via REST API +This endpoint returns a "statistics response type":#payload including "account-only metrics":#account-only. -You can use the "REST API":/docs/api/rest-api to retrieve account statistics by making a GET request to the "@stats@ endpoint":/docs/api/rest-api#stats. For example, to retrieve account statistics aggregated by hour: + ```[curl] -curl https://rest.ably.io/stats?unit=hour \ - -u "{{API_KEY}}" +curl 'https://control.ably.net/v1/me' \ +--header 'Authorization: Bearer ${ACCESS_TOKEN}' ``` h3(#account-dashboard). Account statistics from the dashboard @@ -83,21 +67,19 @@ Your account statistics are available as graphs, tabular data, and for download h2(#app). App statistics -App statistics provide information about a specific application. - -You can retrieve account statistics: +App statistics provide metrics scoped to each application. You can retrieve account statistics: * Via the "Control API":#app-control * Programmatically using the "Pub/Sub SDK":#app-sdk * In realtime by subscribing to a "meta channel":#app-meta * From your "application dashboard":#app-dashboard -h3(#app-control). App statistics via API +h3(#app-control). App statistics via Control API -You can retrieve app statistics using the "Control API":/docs/account/control-api by making a call to the @apps@ endpoint using your @appId@ and @accessToken@: +You can retrieve app statistics using the "Control API":/docs/account/control-api by making a call to the @apps@ endpoint using your @APP_ID@ and @ACCESS_TOKEN@: ```[curl] -curl --location --request POST 'https://control.ably.net/v1/apps/${APP_ID}/stats' \ +curl 'https://control.ably.net/v1/apps/${APP_ID}/stats' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ${ACCESS_TOKEN}' \ --data-raw '{ @@ -106,13 +88,19 @@ curl --location --request POST 'https://control.ably.net/v1/apps/${APP_ID}/stats }' ``` -Your @appId@ can be found in the "*Settings*":https://ably.com/accounts/any/apps/any/edit tab of an application within your application dashboard. +This endpoint returns a "statistics response type":#payload. -h3(#app-sdk). App statistics programmatically + -App-level statistics are also available programmatically using an Ably SDK at one minute intervals, or aggregated up to the hour, day or month. +h3(#app-sdk). App statistics via SDK -The following is an example of querying app-level statistics: +App-level statistics are also available programmatically using an "Ably SDK":/docs/sdks at one minute intervals, or aggregated up to the hour, day or month. Whilst it is possible to obtain statistics via the SDKs, this API is not actively maintained and may be deprecated in future. We recommend using "app-level stats via the Control API":#app-control. + +The following is an example of querying app-level statistics using the Ably SDK "@stats@ method":/docs/api/rest-sdk/statistics. ```[realtime_javascript] const realtime = new Ably.Realtime('{{API_KEY}}'); @@ -274,7 +262,7 @@ h3(#app-meta). Subscribe to app statistics You can subscribe to app statistics using the "@[meta]stats:minute@ metachannel":/docs/metadata-stats/metadata/subscribe#stats. Events are published at one minute intervals and contain the statistics for the previous minute. -The following is an example of subscribing to the @[meta]stats@ channel: +The following is an example of subscribing to the @[meta]stats:minute@ channel: ```[realtime_javascript] const channel = ably.channels.get("[meta]stats:minute"); @@ -331,40 +319,48 @@ h3(#app-dashboard). App statistics from the dashboard App statistics are available as graphs, tabular data, and for download from each "application dashboard":https://ably.com/accounts/any/apps/any in your account. -h2(#payload). Statistics response payload +h2(#payload). Statistics response -Statistics responses are sparse to reduce the size of the JSON and improve performance. +Statistic API and SDK requests return an array of "Statistics entry types":#metrics. -This means that if a metric is empty, or contains only zero values then the metric will be omitted completely from the response. +An example simplified response with one stats entry is shown below: ```[json] -{ - entries: { - 'messages.all.all.count': 245, - 'messages.all.all.data': 58654, - 'messages.all.all.uncompressedData': 58654, - 'messages.all.all.billableCount': 245, - 'messages.all.messages.count': 245, - 'messages.all.messages.data': 58654, - 'messages.all.messages.uncompressedData': 58654, - 'messages.all.messages.billableCount': 245, - 'messages.inbound.realtime.all.count': 145, - 'messages.inbound.realtime.all.data': 29127, - 'messages.inbound.realtime.all.uncompressedData': 29127, - 'messages.inbound.realtime.messages.count': 145, - ... - }, - schema: 'https://schemas.ably.com/json/app-stats-0.0.5.json', - appId: '', - inProgress: '2025-01-20:15:11', - unit: 'hour', - intervalId: '2025-01-20:15' -} +[ + { + entries: { + 'messages.all.all.count': 245, + 'messages.all.all.data': 58654, + 'messages.all.all.uncompressedData': 58654, + 'messages.all.all.billableCount': 245, + 'messages.all.messages.count': 245, + 'messages.all.messages.data': 58654, + 'messages.all.messages.uncompressedData': 58654, + 'messages.all.messages.billableCount': 245, + 'messages.inbound.realtime.all.count': 145, + 'messages.inbound.realtime.all.data': 29127, + 'messages.inbound.realtime.all.uncompressedData': 29127, + 'messages.inbound.realtime.messages.count': 145, + ... + }, + schema: 'https://schemas.ably.com/json/app-stats-0.0.5.json', + appId: '', + inProgress: '2025-01-20:15:11', + unit: 'hour', + intervalId: '2025-01-20:15' + } +] ``` -h2(#metrics). Statistics metrics + + +h2(#metrics). Statistics entry type -The following metadata is returned for each request: +The following metadata is returned for each entry: |_. Property |_. Description | | appId | The ID of the Ably application the statistics are for. Only present when querying app statistics. | @@ -376,7 +372,7 @@ The following metadata is returned for each request: h3(#messages). All messages -The following metrics can be returned for all messages. 'All messages' includes totals for all messages sent and received by Ably and clients. +All messages metrics include all messages types, such as those sent and received by Ably and clients, messages delivered via integrations and push notifications delivered to devices. |_. Metric |_. Description | | messages.all.all.count | Total number of messages that were successfully sent, summed over all message types and transports. | @@ -404,7 +400,7 @@ The following metrics can be returned for all messages. 'All messages' includes h3(#inbound). Inbound messages -The following metrics can be returned for inbound messages. Inbound messages are messages that are sent by clients and received by Ably. +Inbound messages metrics include those messages that are sent by clients and received inbound from those clients by Ably. |_. Metric |_. Description | | messages.inbound.realtime.all.count | Total inbound realtime message count, received by Ably from clients. | @@ -464,7 +460,7 @@ The following metrics can be returned for inbound messages. Inbound messages are h3(#outbound). Outbound messages -The following metrics can be returned for outbound messages. Outbound messages are messages that are sent from Ably to a client. +Outbound message metrics include those messages that are sent outbound from Ably to either clients that subscribe or request messages (such as history requests), to "integrations":docs/integrations, or to "push notification":/docs/push targets. |_. Metric |_. Description | | messages.outbound.realtime.all.count | Total outbound realtime message count, sent from Ably to clients. | @@ -600,7 +596,7 @@ The following metrics can be returned for outbound messages. Outbound messages a h3(#persisted). Persisted messages -The following metrics can be returned for persisted messages. Persisted messages are messages "stored":/docs/storage-history/storage by Ably. +Persisted messages metrics are calculated from the number of "messages stored":/docs/storage-history/storage by Ably. |_. Metric |_. Description | | messages.persisted.all.count | Total count of persisted messages. | @@ -615,7 +611,7 @@ The following metrics can be returned for persisted messages. Persisted messages h3(#deltas). Message deltas -The following metrics can be returned for message deltas. Message deltas are messages that are compressed by using the "delta":/docs/channels/options/deltas feature. +Message deltas metrics are calculated from the number of "messages delta":/docs/channels/options/deltas generated. |_. Metric |_. Description | | messages.processed.delta.xdelta.succeeded | Total number of message deltas successfully generated. | @@ -624,7 +620,7 @@ The following metrics can be returned for message deltas. Message deltas are mes h3(#connections). Connections -The following metrics can be returned for connections. Connections are all client "connections":/docs/connect made to Ably. +Connection metrics include all realtime "connections":/docs/connect made to Ably. |_. Metric |_. Description | | connections.all.peak | Peak connection count. | @@ -635,7 +631,7 @@ The following metrics can be returned for connections. Connections are all clien h3(#channels). Channels -The following metrics can be returned for channels. "Channels":/docs/channels are used to separate messages into different topics throughout Ably. +Channel metrics include all active "channels":/docs/channels that are used to separate messages into different topics throughout Ably. |_. Metric |_. Description | | channels.peak | Peak active channel count. | @@ -646,7 +642,7 @@ The following metrics can be returned for channels. "Channels":/docs/channels ar h3(#api). API requests -The following metrics can be returned for API requests. API requests are all HTTP requests made to Ably, including those related to "token authentication":/docs/auth/token and "push notifications":/docs/push. They do not include those requests made to the "Control API":/docs/account/control-api. +API request metrics include all HTTP requests made to Ably, including those related to "token authentication":/docs/auth/token and "push notifications":/docs/push. They do not include those requests made to the "Control API":/docs/account/control-api. |_. Metric |_. Description | | apiRequests.all.succeeded | Total number of API requests made. | @@ -664,7 +660,7 @@ The following metrics can be returned for API requests. API requests are all HTT h3(#push). Push notifications -The following metrics can be returned for push notifications. "Push notifications":/docs/push are all notifications sent to devices via a push transport. +Push notification metrics include all "notifications":/docs/push sent to devices via one of the push transports such as APNS, FCM or web. |_. Metric |_. Description | | push.channelMessages | Total number of channel messages published over Ably that contained a @push@ payload. Each of these may have triggered notifications to be sent to a device with a matching registered push subscription. | @@ -694,10 +690,10 @@ The following metrics can be returned for push notifications. "Push notification h3(#account-only). Account-only metrics -The following metrics can only be returned when querying account statistics: +The following metrics will only be returned when querying account statistics. |_. Metric |_. Description | -| peakRates.messages | ThePeak rate of messages. | +| peakRates.messages | Peak rate of messages. | | peakRates.apiRequests | Peak rate of api requests. | | peakRates.tokenRequests | Peak rate of token requests. | | peakRates.connections | Peak rate of opened connections. | diff --git a/content/platform/architecture/index.textile b/content/platform/architecture/index.textile new file mode 100644 index 0000000000..889dee1daa --- /dev/null +++ b/content/platform/architecture/index.textile @@ -0,0 +1,139 @@ +--- +title: Architecture overview +meta_description: "Learn more about Ably's platform architecture." +meta_keywords: "architecture" +--- + +Ably's platform architecture is built to deliver dependable realtime experiences at global scale. + +As the definitive realtime experience platform, Ably serves billions of devices monthly and delivers over half a trillion messages monthly. + +h2. At a glance + +Ably's globally distributed infrastructure forms the foundation of the platform, allowing Ably to maintain exceptional performance, while serving massive scale and providing industry-leading quality of service guarantees. + +Ably characterizes the system across "4 pillars of dependability":https://ably.com/four-pillars-of-dependability : + +* **Performance**: Ably focuses on predictability of latencies to provide certainty in uncertain operating conditions. +** <30ms round trip latency within datacenter (99th percentile) +** <65ms global round trip latency (99th percentile) +* **Integrity**: Guarantees for message ordering and delivery. +** Exactly-once delivery semantics +** Guaranteed message ordering from publishers to subscribers +** Automatic connection recovery with message continuity +* **Reliability**: Fault tolerant architecture at regional and global levels to survive multiple failures without outages. +** 99.999999% message survivability +** 99.99999999% persisted data survivability +** Edge network failure resolution by the client SDKs within 30s +** Automated routing of all traffic away from an abrupt failure of datacenter in less than two minutes +* **Availability**: Meticulously designed to provide continuity of service even in the case of instance or whole datacenter failures. +** 99.9999% global service availability (5 minutes 15 seconds of downtime per year) +** 50% global capacity margin for instant demand surges + +h2. Design objectives + +Ably's platform is a global service that supports all realtime messaging and associated services. It is architected to achieve horizontal scalability with "no effective ceiling":https://ably.com/blog/ablys-four-pillars-no-scale-ceiling on application scale, while maintaining consistent latency, message integrity, and system reliability across the global network. + +The platform has been designed with the following primary objectives in mind: + +* **Horizontal scalability**: As more nodes are added, load is automatically redistributed across the cluster so that global capacity increases linearly with the number of instances Ably runs. +* **No single point of congestion**: As the system scales, there is no single point of congestion for any data path, and data within the system is routed peer-to-peer, ensuring no single component becomes overloaded as traffic scales for an individual app or across the cluster. +* **Fault tolerance**: Faults in the system are expected, and the system must have redundancy at every layer in the stack to ensure availability and reliability. +* **Autonomy**: Each component in the system should be able to operate fully without reliance on a global controller. For example, two isolated data centers should continue to service realtime requests while isolated. +* **Consistent low latencies**: Within data centers, Ably aims for latencies to be in the low 10s of milliseconds and less than 100ms globally. Consistently achieving low latencies requires careful consideration of the placement of data and services across the system as well as prioritisation of the computation performed by each service. +* **Quality of service**: Ably intentionally designs for high QoS targets to enable sophisticated realtime applications that would be impossible on platforms with weaker guarantees. + +h2. Cluster architecture + +Ably's platform runs on AWS EC2 infrastructure with a globally distributed architecture. Ably's "clusters":/docs/platform-customization#dedicated-and-isolated-clusters typically span multiple regions, usually between two and ten. This multi-region approach maximizes availability and is a critical aspect of providing a fault tolerant service. + +Each regional deployment operates independently, handling its own subscriber connections, REST traffic, channel management and message routing. When activity occurs on a channel across multiple regions, messages flow peer-to-peer between regions directly, eliminating central bottlenecks and single points of failure. + +Ably's architecture consists of four primary layers: + +* **Routing Layer**: Provides intelligent, latency optimized routing for robust end client connectivity. +* **Gossip Layer**: Distributes network topology information and facilitates service discovery. +* **Frontend Layer**: Handles REST requests and maintains realtime connections (such as WebSocket, Comet and SSE). +* **Core Layer**: Performs all central message processing for channels. + +Each component scales independently in each region based on demand. Ably continuously monitors CPU, memory, and other key metrics, triggering autoscaling based on aggregated performance indicators. + +The key to Ably's horizontal scalability is intelligent load distribution that efficiently utilizes new capacity as it becomes available: + +* In the frontend layer, new instances join the load balancer pool and begin handling their share of incoming connections and REST requests. +* In the core layer, Ably employs consistent hashing to distribute channels across core processes - each core maintains a set of pseudo-randomly generated hashes that determine channel placement. As the cluster scales, channels automatically relocate to maintain even load distribution. + +h3. Routing layer + +Ably's latency-optimized routing infrastructure provides multiple layers of resilience to ensure reliable connectivity even during partial system failures. + +h4. Intelligent routing + +Ably's custom routing layer provides sophisticated traffic management through deep integration with the application architecture. + +The routing layer is cluster-aware and implements advanced retry strategies, enables zero-downtime deployments, and distributes instrumentation across the cluster. + +This intelligent routing ensures traffic is directed optimally even as the system scales or during partial failures. + +h4. DNS and edge protection + +Ably uses latency-based routing to ensure clients are consistently routed to their closest datacenter. + +Ably employs multiple DNS providers and global load balancing strategies for resilient connectivity, allowing client libraries to route around regional issues and ensuring consistent connectivity. + +The routing layer automatically removes unhealthy regions from DNS resolution and offers advanced DDoS protection at the edge. + +h4. Connection management + +Ably's SDKs implement sophisticated connection management with automatic failover capabilities. + +Clients seamlessly handle transient disconnections and the system preserves message continuity if the connection is re-established within two minutes. + +Clients automatically select the best available transport (such as WebSocket or Comet) and seamlessly migrate connections across transports to provide the best possible service. + +Re-connection attempts cycle through up to 6 globally distributed endpoints. Clients automatically attempt to reconnect via alternative endpoints if a connection attempt fails. + +Progressive connection timeout strategies ensure rapid recovery from transient issues, while continuous connection quality monitoring triggers proactive reconnection when performance degrades. + +h3. Gossip layer + +Every cluster Ably operates includes a network of gossip nodes, spanning all regions, that participate in a (Scuttlebutt-inspired) gossip protocol to facilitate service discovery and even distribution of work across the cluster. Other nodes in the cluster communicate with the gossip layer to broadcast and receive information about the state of the cluster. As nodes are added and removed, or fail abruptly, the gossip layer ensures a single consistent view of the network is shared by all. The gossip layer also allows node health to be consistently determined system-wide without the need for any single coordinator. + +h3. Frontend layer + +Ably's frontend infrastructure consists of several components that handle individual requests and connections from realtime subscribers. Each component scales independently according to demand, ensuring requests are processed in the optimal location based on client location and system state. + +h4. Request handling + +Nodes in the frontend layer process all incoming REST and realtime requests. They participate in the active service discovery mechanism between all nodes, maintaining realtime awareness of channel locations across the cluster, even as channels migrate during scaling events. Message fan-out is achieved by frontend nodes efficiently processing published messages and distributing them to all subscribed clients. The frontend layer also handles authentication, enforces rate limits, and provides additional DDoS protection. + +h4. Protocol adapters + +Ably's "protocol adapters":/docs/protocols enable interoperability with multiple industry protocols. These adapters translate between external protocols and Ably's internal protocols in both directions. Ably supports the MQTT standard as well as proprietary protocols like PubNub and Pusher. + +h3. Core layer + +Ably's core infrastructure handles all messages as they transit through the system. It is designed to scale elastically according to demand while managing all associated channel and system state. + +h4. Resource placement + +Ably uses "consistent hashing":https://ably.com/blog/implementing-efficient-consistent-hashing to distribute resources such as channels, apps and accounts across available core compute capacity. + +Each compute instance within the core layer has a set of pseudo-randomly generated hashes which determines which resources are located at that instance. As the cluster scales, channels relocate to maintain an even load distribution. Any number of channels can exist as long as sufficient compute capacity is available. Whether handling many lightly-loaded channels or heavily-loaded ones, Ably's scaling and placement strategies ensure capacity is added as required and load is effectively distributed. + +When a core node fails, the system detects the failure through the cluster-wide gossip protocol, and its resources are automatically redistributed to healthy nodes. + +h4. Message processing + +Nodes in the core layer are responsible for all channel message processing and persistence and apply operations such as "delta":/docs/channels/options/deltas computation, "batching":/docs/messages/batch and "integration rule":/docs/integrations invocation. They also aggregate and persist account and app "statistics":/docs/metadata-stats/stats and enforce "limits":/docs/pricing/limits. Nodes in the core layer communicate cross-regionally to facilitate inter-data-center message transit. + +h4. Message persistence + +Messages are persisted in multiple locations to ensure that message availability and continuity are maintained even during individual node or data center failures. + +Once a message is acknowledged, it is stored in multiple physical locations, providing statistical guarantees of 99.999999% (8 nines) for message availability and survivability. This redundancy enables Ably to maintain its quality of service guarantees even during infrastructure failures. + +Messages are stored in two ways: + +* **Ephemeral Storage**: Messages are held for 2 minutes in an in-memory database (Redis). This data is distributed according to Ably's consistent hashing mechanism and relocated when channels move between nodes. This short-term storage enables low-latency message delivery and retrieval and supports features like "connection recovery":/docs/connect/states. +* **Persisted Storage**: Messages can optionally be stored persistently on disk if longer term retention is required. Ably uses a globally distributed and clustered database (Cassandra) for this purpose, deployed across multiple data centers with message data replicated to three regions to ensure integrity and availability even if a region fails. diff --git a/content/platform/deprecate/index.textile b/content/platform/deprecate/index.textile new file mode 100644 index 0000000000..988734a6e1 --- /dev/null +++ b/content/platform/deprecate/index.textile @@ -0,0 +1,37 @@ +--- +title: Deprecation policy +meta_description: "A policy detailing how Ably deprecates SDKs and APIs." +meta_keywords: "Ably, deprecation policy, deprecation, sunset" +--- + +Ably's SDKs are regularly updated to add support for new features, bugfixes, and to accommodate version or API changes in dependencies. Whilst Ably ensures changes are backwards-compatible wherever possible, there are some instances where the previous API structure is no longer suitable. + +In all cases, updates to SDKs conform to "semantic versioning":https://semver.org/ guidelines. It is recommended that you always update to the latest version of an SDK when it is available. + +h2(#applicability). Applicability + +This policy applies to the following: + +Ably SDKs and associated APIs, including; + +* HTTP APIs +* Ably's realtime protocol + +h2(#timelines). Deprecation timelines + +Ably will support all releases for at least 12 months from the date of release of a version that supersedes it. For example, if version @1.1.0@ is released on 1st January 2025, version @1.0.X@ will be supported until at least 1st January 2026. + +Releases will be always be deprecated within 24 months of being superseded. + +h3(#sunset). Sunset date + +The sunset date is the date on which a version of an SDK or API will no longer function, at the end of the deprecation period. Ably will usually sunset features by a process of rejecting just a (gradually increasing) proportion of requests to avoid large-scale disruption where possible. However, this won't always be the case, so you should assume that features or versions will stop working on the sunset date. + +h2(#support). Supportability + +Ably will no longer provide the following when a version is deprecated: + +* Technical support for that version. +* Bugfixes for that version. + +Ably also reserves the right to withhold Service Level Agreement (SLA) remedies in the case of service failure, if the use of the deprecated version is believed to be a contributing factor in that failure. diff --git a/content/platform/deprecate/protocol-v1.textile b/content/platform/deprecate/protocol-v1.textile new file mode 100644 index 0000000000..8776b87b01 --- /dev/null +++ b/content/platform/deprecate/protocol-v1.textile @@ -0,0 +1,33 @@ +--- +title: Deprecation of protocol version 1 - November 2025 +meta_description: "A policy detailing how Ably deprecates SDKs and APIs." +meta_keywords: "Ably, deprecation policy, deprecation, sunset" +--- + +SDKs using version 1 of Ably's realtime protocol will be sunset on 1st November 2025. + +This version was superseded by version 2 in January 2023. It introduced new functionality, with a more efficient and scalable backend implementation. New features are not available using version 1 of the protocol, nor in any SDKs implementing it. + +h2(#sdks). Affected SDKs + +SDK versions earlier than those listed below are now considered deprecated. If you are using a version older than those listed, upgrade before the 1st November 2025. + +|_. Product |_. Language |_. Versions | +| Pub/Sub | JavaScript | < "1.2.36":https://github.com/ably/ably-js/releases/tag/1.2.36 | +| Pub/Sub | Java | < "1.2.35":https://github.com/ably/ably-java/releases/tag/v1.2.35 | +| Pub/Sub | Swift / Objective-C | < "1.2.24":https://github.com/ably/ably-cocoa/releases/tag/1.2.24 | +| Pub/Sub | .NET | < "1.2.12":https://github.com/ably/ably-dotnet/releases/tag/1.2.12 | +| Pub/Sub | Go | < "1.2.14":https://github.com/ably/ably-go/releases/tag/v1.2.14 | +| Pub/Sub | Python | < "2.0.0-beta.6":https://github.com/ably/ably-python/releases/tag/v2.0.0-beta.6 | +| Pub/Sub | Ruby | < "1.2.5":https://github.com/ably/ably-ruby/releases/tag/v1.2.5 | +| Pub/Sub | PHP | < "1.1.9":https://github.com/ably/ably-php/releases/tag/1.1.9 | +| Pub/Sub | Flutter | < "1.2.25":https://github.com/ably/ably-flutter/releases/tag/v1.2.25 | +| Kafka Connect | - | < "2.1.4":https://github.com/ably/kafka-connect-ably/releases/tag/v2.1.4 | + +Contact "support":https://ably.com/support if you have any questions. + +h2(#requests). Request failures + +On the 1st November 2025 attempts to connect to Ably using an SDK that uses version 1 of the protocol will start to fail. Failures will be phased, with only a fraction of traffic being rejected at first, until 100% of requests are rejected after several weeks. + +Requests that are rejected will contain an error message and code referencing this deprecation notice and the associated "deprecation policy.":/docs/platform/deprecate diff --git a/content/pricing/free.textile b/content/pricing/free.textile index 192dd5d95f..3e1a33cb17 100644 --- a/content/pricing/free.textile +++ b/content/pricing/free.textile @@ -31,9 +31,9 @@ h2(#support). Support Free package support includes Ably's best effort Service Level Objective (SLO) and access to community support, including "Discord":https://discord.gg/g8yqePUVDn and "Stack Overflow":https://stackoverflow.com/questions/tagged/ably-realtime. -h2(#upgrade). Upgrade +h2(#upgrade). Upgrade from Free to Standard or Pro -To upgrade your account from a Free package: +To upgrade your account from a Free package to a Standard or Pro package: 1. Ensure you are the "account owner":/docs/account/users. 2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. @@ -41,6 +41,20 @@ To upgrade your account from a Free package: Note that your first invoice will be issued at the start of the following calendar month. It covers consumption from the point of upgrade up until the end of that month. All subsequent invoices will follow the same pattern of billing for the previous month's consumption. The base package price will be charged pro-rata from the point in the month that you upgraded. -Once you upgrade to a "Standard":/docs/pricing/standard, "Pro":/docs/pricing/pro or "Enterprise":/docs/pricing/enterprise package, your consumption is counted from that point onwards. For example, if you upgrade in the middle of the month, you are charged for the first message you send after upgrading, not after the 6,000,000 messages allowed on the Free package. +Once you upgrade to a "Standard":/docs/pricing/standard or "Pro":/docs/pricing/pro package, your consumption is counted from that point onwards. For example, if you upgrade in the middle of the month, you are charged for the first message you send after upgrading, not after the 6,000,000 messages allowed on the Free package. + +h2(#free-enterprise). Upgrade from Free to Enterprise + +To upgrade your account from a Free package to an "Enterprise package":/docs/pricing/enterprise, "contact us":https://ably.com/contact?cta=enterprise_package to discuss your options. + +h2(#downgrade). Downgrade + +To downgrade your account to a Free package: + +1. Ensure you are the "account owner":/docs/account/users. +2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. +3. Click the *Downgrade* button. + +If you downgrade to a Free package, you will finish the month on your current package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice covering your last month's consumption and package fee. Once the final invoice has been paid, your payment details are removed from the system. If you wish to cancel your account, have the account owner "contact us":https://ably.com/support. Be aware that this will permanently delete all the information associated with your account. diff --git a/content/pricing/pro.textile b/content/pricing/pro.textile index 812b753af1..f634e27413 100644 --- a/content/pricing/pro.textile +++ b/content/pricing/pro.textile @@ -34,7 +34,29 @@ h2(#support). Support Standard package support includes access to Ably support via email with a Service Level Agreement (SLA) of less than 1 business day for general guidance and less than 2 business hours for system impediments or system down. -h2(#upgrade). Upgrade +h2(#free-pro). Upgrade from Free to Pro + +To upgrade your account from a Free package to a Pro package: + +1. Ensure you are the "account owner":/docs/account/users. +2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. +3. Choose the plan you wish to upgrade to and follow the instructions. Upgrades take effect immediately. + +Note that your first invoice will be issued at the start of the following calendar month. It covers consumption from the point of upgrade up until the end of that month. All subsequent invoices will follow the same pattern of billing for the previous month's consumption. The base package price will be charged pro-rata from the point in the month that you upgraded. + +Once you upgrade to a Pro, your consumption is counted from that point onwards. For example, if you upgrade in the middle of the month, you are charged for the first message you send after upgrading, not after the 6,000,000 messages allowed on the Free package. + +h2(#standard-pro). Upgrade from Standard to Pro + +To upgrade your account from a Standard package to a Pro package: + +1. Ensure you are the "account owner":/docs/account/users. +2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. +3. Choose the plan you wish to upgrade to and follow the instructions. Upgrades take effect immediately. + +The base package price will be charged pro-rata from the point in the month that you upgraded. This is true for the current Standard package base price, as well as the upgraded Pro package base price. + +h2(#pro-enterprise). Upgrade from Pro to Enterprise To upgrade your account from a Pro package, "contact us":https://ably.com/contact?cta=enterprise_package to discuss "Enterprise package":/docs/pricing/enterprise options. @@ -48,6 +70,6 @@ To downgrade your account from a Pro package: If you downgrade to a "Standard package":/docs/pricing/standard, you will finish the month on the Pro package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice for the Pro package. Subsequent invoices will be for your Standard package. -If you downgrade to a "Free package":/docs/pricing/free, you will finish the month on the Pro package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice covering your last month's consumption. Once the final invoice has been paid, your payment details are removed from the system. +If you downgrade to a "Free package":/docs/pricing/free, you will finish the month on the Pro package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice covering your last month's consumption and package fee. Once the final invoice has been paid, your payment details are removed from the system. If you wish to cancel your account, downgrade to the Free package first and then have the account owner "contact us":https://ably.com/support. Be aware that this will permanently delete all the information associated with your account. diff --git a/content/pricing/standard.textile b/content/pricing/standard.textile index fb4703836f..7dcf67e3df 100644 --- a/content/pricing/standard.textile +++ b/content/pricing/standard.textile @@ -34,9 +34,21 @@ h2(#support). Support Standard package support includes access to Ably support via email with a Service Level Agreement (SLA) of less than 1 business day for general guidance and for system impediments or system down. -h2(#upgrade). Upgrade +h2(#free-standard). Upgrade from Free to Standard -To upgrade your account from a Standard package: +To upgrade your account from a Free package to a Standard package: + +1. Ensure you are the "account owner":/docs/account/users. +2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. +3. Choose the plan you wish to upgrade to and follow the instructions. Upgrades take effect immediately. + +Note that your first invoice will be issued at the start of the following calendar month. It covers consumption from the point of upgrade up until the end of that month. All subsequent invoices will follow the same pattern of billing for the previous month's consumption. The base package price will be charged pro-rata from the point in the month that you upgraded. + +Once you upgrade to a Standard, your consumption is counted from that point onwards. For example, if you upgrade in the middle of the month, you are charged for the first message you send after upgrading, not after the 6,000,000 messages allowed on the Free package. + +h2(#standard-pro). Upgrade from Standard to Pro + +To upgrade your account from a Standard package to a Pro package: 1. Ensure you are the "account owner":/docs/account/users. 2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. @@ -44,6 +56,10 @@ To upgrade your account from a Standard package: The base package price will be charged pro-rata from the point in the month that you upgraded. This is true for the current Standard package base price, as well as the upgraded "Pro package":/docs/pricing/pro base price. +h2(#standard-enterprise). Upgrade from Standard to Enterprise + +To upgrade your account from a Standard package to an "Enterprise package":/docs/pricing/enterprise, "contact us":https://ably.com/contact?cta=enterprise_package to discuss your options. + h2(#downgrade). Downgrade To downgrade your account from a Standard package to a Free package: @@ -52,6 +68,6 @@ To downgrade your account from a Standard package to a Free package: 2. Log in to your "account":https://ably.com/login and select *Billing* from the *Account* menu. 3. Click the *Downgrade* button. -If you downgrade to a "Free package":/docs/pricing/free, you will finish the month on the Standard package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice covering your last month's consumption. Once the final invoice has been paid, your payment details are removed from the system. +If you downgrade to a "Free package":/docs/pricing/free, you will finish the month on the Standard package with its associated limits and benefits. At the beginning of the following month you will receive a final invoice covering your last month's consumption and package fee. Once the final invoice has been paid, your payment details are removed from the system. If you wish to cancel your account, downgrade to the Free package first and then have the account owner "contact us":https://ably.com/support. Be aware that this will permanently delete all the information associated with your account. diff --git a/content/push/publish.textile b/content/push/publish.textile index e41e8f5c3a..a1a65f91fc 100644 --- a/content/push/publish.textile +++ b/content/push/publish.textile @@ -141,6 +141,8 @@ h2(#direct-publishing). Publish directly Direct publishing sends push notifications directly to individual devices via the "Ably SDK":https://ably.com/docs/sdks, bypassing the intermediary of channels. This approach delivers personalized or precise notifications customized for individual users. Direct publishing proves beneficial during the transition phase to Ably's platform and when the objective is to engage existing push notification devices. +Direct publishing is also available in "batch mode":#via-batch-push-api, enabling you to publish to a large number of devices in one request. + Push notifications are targeted explicitly towards devices identified by: * "@deviceId@":#device-id @@ -163,7 +165,8 @@ var recipient = { var data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -178,7 +181,8 @@ var recipient = { var data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -193,7 +197,8 @@ recipient = { data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -201,20 +206,18 @@ realtime.push.admin.publish(recipient, data) ``` ```[realtime_java] - JsonObject payload = JsonUtils.object() .add("notification", JsonUtils.object() .add("title", "Hello from Ably!") .add("body", "Example push notification from Ably.") - ) - .add("data", JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux") + .add("ttl", 3600) // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ) .toJson(); - -realtime.push.admin.publish(new Param[]{new Param("deviceId", "xxxxxxxxxxxx")}, payload); +realtime.push.admin.publish( + new Param[] { new Param("deviceId", "xxxxxxxxxxxx") }, + payload +); ``` ```[realtime_python] @@ -225,7 +228,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -243,7 +247,8 @@ var data = new JObject ["notification"] = new JObject { { "title", "Hello from Ably!" }, - { "body", "Example push notification from Ably." } + { "body", "Example push notification from Ably." }, + { "ttl", 3600 } // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -258,7 +263,8 @@ var recipient = { var data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -273,7 +279,8 @@ var recipient = { var data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -288,7 +295,8 @@ recipient = { data = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -300,15 +308,14 @@ JsonObject payload = JsonUtils.object() .add("notification", JsonUtils.object() .add("title", "Hello from Ably!") .add("body", "Example push notification from Ably.") - ) - .add("data", JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux") + .add("ttl", 3600) // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ) .toJson(); - -rest.push.admin.publish(new Param[]{new Param("deviceId", "xxxxxxxxxxxx")}, payload); +rest.push.admin.publish( + new Param[] { new Param("deviceId", "xxxxxxxxxxxx") }, + payload +); ``` ```[rest_python] @@ -319,7 +326,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -334,14 +342,15 @@ $recipient = [ $data = [ 'notification' => [ 'title' => 'Hello from Ably!', - 'body' => 'Example push notification from Ably.' + 'body' => 'Example push notification from Ably.', + 'ttl' => 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ] ]; $rest->push->admin->publish($recipient, $data); -var channel = rest.channels.get('pushenabled:foo'); -channel.publish({'name': 'example', 'data': 'data', 'extras': extras}); +$channel = $rest->channels->get('pushenabled:foo'); +$channel->publish(['name' => 'example', 'data' => 'data', 'extras' => $extras]); ``` ```[rest_csharp] @@ -355,7 +364,8 @@ var data = new JObject ["notification"] = new JObject { { "title", "Hello from Ably!" }, - { "body", "Example push notification from Ably." } + { "body", "Example push notification from Ably." }, + { "ttl", 3600 } // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -372,13 +382,14 @@ The following example publishes a push notification using the @clientId@: ```[realtime_javascript] var recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' }; var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -387,13 +398,14 @@ realtime.push.admin.publish(recipient, notification); ```[realtime_nodejs] var recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' }; var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -402,13 +414,14 @@ realtime.push.admin.publish(recipient, notification); ```[realtime_ruby] recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' } notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -420,10 +433,7 @@ JsonObject payload = JsonUtils.object() .add("notification", JsonUtils.object() .add("title", "Hello from Ably!") .add("body", "Example push notification from Ably.") - ) - .add("data", JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux") + .add("ttl", 3600) // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ) .toJson(); @@ -438,7 +448,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -446,14 +457,15 @@ realtime.push.admin.publish(recipient, message) ``` ```[realtime_csharp] -var recipient = new { clientId = "bob" }; +var recipient = new { clientId = "xxxxxxxxxxxx" }; var notification = new { notification = new { title = "Hello from Ably!", - body = "Example push notification from Ably." + body = "Example push notification from Ably.", + ttl = 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -462,13 +474,14 @@ realtime.Push.Admin.Publish(recipient, notification); ```[rest_javascript] var recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' }; var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -477,13 +490,14 @@ rest.push.admin.publish(recipient, notification); ```[rest_nodejs] var recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' }; var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -492,13 +506,14 @@ rest.push.admin.publish(recipient, notification); ```[rest_ruby] recipient = { - clientId: 'bob' + clientId: 'xxxxxxxxxxxx' } notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -510,10 +525,7 @@ JsonObject payload = JsonUtils.object() .add("notification", JsonUtils.object() .add("title", "Hello from Ably!") .add("body", "Example push notification from Ably.") - ) - .add("data", JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux") + .add("ttl", 3600) // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ) .toJson(); @@ -528,7 +540,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -543,25 +556,27 @@ $recipient = [ $data = [ 'notification' => [ 'title' => 'Hello from Ably!', - 'body' => 'Example push notification from Ably.' + 'body' => 'Example push notification from Ably.', + 'ttl' => 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ] ]; $rest->push->admin->publish($recipient, $data); -var channel = rest.channels.get('pushenabled:foo'); -channel.publish({'name': 'example', 'data': 'data', 'extras': extras}); +$channel = $rest->channels->get('pushenabled:foo'); +$channel->publish(['name' => 'example', 'data' => 'data', 'extras' => $extras]); ``` ```[rest_csharp] -var recipient = new { clientId = "bob" }; +var recipient = new { clientId = "xxxxxxxxxxxx" }; var notification = new { notification = new { title = "Hello from Ably!", - body = "Example push notification from Ably." + body = "Example push notification from Ably.", + ttl = 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -586,7 +601,8 @@ var recipient = { var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -602,7 +618,8 @@ var recipient = { var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -618,7 +635,8 @@ recipient = { notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -630,14 +648,16 @@ Message message = new Message("example", "rest data"); message.extras = io.ably.lib.util.JsonUtils.object() .add("notification", io.ably.lib.util.JsonUtils.object() .add("title", "Hello from Ably!") - .add("body", "Example push notification from Ably.")) - .add("data", io.ably.lib.util.JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux")); - -realtime.push.admin.publish(arrayOf(Param("transportType", "apns"), Param("deviceToken", deviceToken)), message); + .add("body", "Example push notification from Ably.") + .add("ttl", 3600)); // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) -ably.push.admin.publish(new Param[]{new Param("transportType","fcm"),new Param("registrationToken","")},payload); +realtime.push.admin.publish( + new Param[] { + new Param("transportType", "apns"), + new Param("deviceToken", deviceToken) + }, + message +); ``` ```[realtime_python] @@ -649,7 +669,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -657,14 +678,15 @@ realtime.push.admin.publish(recipient, message) ``` ```[realtime_csharp] -var recipient = new { transport_type: 'apns' }; +var recipient = new { transport_type = "apns", deviceToken = "XXXXXXXXXX" }; var notification = new { notification = new { title = "Hello from Ably!", - body = "Example push notification from Ably." + body = "Example push notification from Ably.", + ttl = 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -680,7 +702,8 @@ var recipient = { var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -696,7 +719,8 @@ var recipient = { var notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; @@ -712,7 +736,8 @@ recipient = { notification = { notification: { title: 'Hello from Ably!', - body: 'Example push notification from Ably.' + body: 'Example push notification from Ably.', + ttl: 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -724,12 +749,16 @@ Message message = new Message("example", "rest data"); message.extras = io.ably.lib.util.JsonUtils.object() .add("notification", io.ably.lib.util.JsonUtils.object() .add("title", "Hello from Ably!") - .add("body", "Example push notification from Ably.")) - .add("data", io.ably.lib.util.JsonUtils.object() - .add("foo", "bar") - .add("baz", "qux")); + .add("body", "Example push notification from Ably.") + .add("ttl", 3600)); // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) -rest.push.admin.publish(arrayOf(Param("transportType", "apns"), Param("deviceToken", deviceToken)), message); +rest.push.admin.publish( + new Param[] { + new Param("transportType", "apns"), + new Param("deviceToken", deviceToken) + }, + message +); ``` ```[rest_python] @@ -741,7 +770,8 @@ recipient = { message = { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.' + 'body': 'Example push notification from Ably.', + 'ttl': 3600 # Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } } @@ -751,33 +781,100 @@ rest.push.admin.publish(recipient, message) ```[rest_php] $recipient = [ 'transportType' => 'apns', - 'deviceToken' => 'XXXXXXX', + 'deviceToken' => 'XXXXXXX' +]; $data = [ 'notification' => [ 'title' => 'Hello from Ably!', - 'body' => 'Example push notification from Ably.' + 'body' => 'Example push notification from Ably.', + 'ttl' => 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) ] ]; -$rest->push->admin->publish( $recipient, $data ); +$rest->push->admin->publish($recipient, $data); ``` ```[rest_csharp] -var recipient = new { transport_type: 'apns' }; +var recipient = new { transport_type = "apns", deviceToken = "XXXXXXXXXX" }; var notification = new { notification = new { title = "Hello from Ably!", - body = "Example push notification from Ably." + body = "Example push notification from Ably.", + ttl = 3600 // Required for Web Push on some platforms and browsers like Microsoft Edge (WNS) } }; rest.Push.Admin.Publish(recipient, notification); ``` +h3(#via-batch-push-api). Publish via batch push API + +The batch push API enables you to publish push notifications to multiple devices or browsers in a single request. + +This is useful when you need to send a large number of distinct push notifications to multiple recipients. If you are publishing the same notification to multiple recipients, prefer publishing "via channels":#via-channels. + +The batch push endpoint accepts a JSON array of @PushPublishSpec@ objects, each of which contain a @recipient@ or array of recipients, and a @payload@, where @payload@ is the same as the payload you would use in a normal direct publish request. + +Currently, the batch push endpoint allows a maximum of 10,000 notifications per request (each recipient for a given payload counts as a separate notification). + +The following example shows how to publish multiple push notifications in one request using the batch API with the generic REST "@request()@":/docs/api/rest-sdk#request method: + +```[rest_javascript] +await rest.request('POST', '/push/batch/publish', null, [ + { + recipient: { + deviceId: 'xxxxxxxxxxx' + }, + payload: { + notification: { title: 'Message 1', body: 'Example push notification from Ably.' } + } + }, + { + recipient: [ + { + deviceId: 'xxxxxxxxxxx' + }, + { + deviceId: 'xxxxxxxxxxx', + } + ], + payload: { + notification: { title: 'Message 2', body: 'Example push notification from Ably.' } + } + } +]) +``` + +```[rest_nodejs] +await rest.request('POST', '/push/batch/publish', null, [ + { + recipient: { + deviceId: 'xxxxxxxxxxx' + }, + payload: { + notification: { title: 'Message 1', body: 'Example push notification from Ably.' } + } + }, + { + recipient: [ + { + deviceId: 'xxxxxxxxxxx' + }, + { + deviceId: 'xxxxxxxxxxx', + } + ], + payload: { + notification: { title: 'Message 2', body: 'Example push notification from Ably.' } + } + } +]) +``` + h2(#via-channels). Publish via channels Publishing via channels is modeled on Ably's "channel":#channels infrastructure, facilitating the delivery of push notifications across a network of subscribed devices or browsers. This process publishes messages through predefined channels, which devices or browsers must "subscribe":#sub-channels to in order to receive updates. This process ensures registered devices or browsers in the specified channels receive the correct push notifications. Publishing via channels is particularly useful for publishing notifications to multiple groups with varying privileges. diff --git a/data/onCreateNode/create-graphql-schema-customization.ts b/data/onCreateNode/create-graphql-schema-customization.ts index 39e493d1c5..50ef4ee23c 100644 --- a/data/onCreateNode/create-graphql-schema-customization.ts +++ b/data/onCreateNode/create-graphql-schema-customization.ts @@ -51,8 +51,6 @@ export const createSchemaCustomization: GatsbyNode['createSchemaCustomization'] oneTrustTest: String inkeepEnabled: String inkeepApiKey: String - inkeepIntegrationId: String - inkeepOrganizationId: String insightsEnabled: Boolean insightsDebug: Boolean mixpanelApiKey: String diff --git a/data/yaml/page-content/homepage.yaml b/data/yaml/page-content/homepage.yaml deleted file mode 100644 index 3ceaf0afd9..0000000000 --- a/data/yaml/page-content/homepage.yaml +++ /dev/null @@ -1,68 +0,0 @@ -name: Homepage -meta: - title: Docs | Ably Realtime - description: Ably documentation for 25+ web, mobile, and IoT SDKs, quickstart guides and tutorials, and realtime concepts. - image: https://files.ably.io/website/images/meta-tags/ably-generic%402x.jpg - twitter: '@ablyrealtime' -sections: - - columns: 1 - bottomMargin: 48 - cards: - - title: Ably Documentation - type: hero - content: Realtime experience infrastructure that just works at any scale. - - columns: 2 - bottomMargin: 80 - cards: - - title: Platform - type: feature - content: Understand the core concepts of Ably's platform, its pricing and integrations. - image: 'platform.png' - links: - - text: Get started - href: /platform - - title: Pub/Sub - type: feature - content: Quickly create realtime digital experiences with Ably's core building blocks. - image: 'pub-sub.png' - links: - - text: Get started - href: /basics - - title: Chat - type: feature - content: Quickly add chat features into any application. - image: 'chat.png' - links: - - text: Get started - href: /chat - - title: Spaces - type: feature - content: Build collaborative environments in just a few lines of code. - image: 'spaces.png' - links: - - text: Get started - href: /spaces - - title: LiveSync - type: feature - content: Seamlessly sync database changes with frontend clients. - image: 'livesync.png' - links: - - text: Get started - href: /livesync - - title: Asset Tracking - type: feature - content: Track the location of assets in realtime. - image: 'asset-tracking.png' - links: - - text: Get started - href: /asset-tracking - - columns: 1 - bottomMargin: 160 - cards: - - title: SDKs - content: Ably SDKs provide a consistent and idiomatic API across a variety of supported platforms. - image: 'sdks@2x.png' - type: sdk - callToAction: - text: View all SDKs - href: /sdks diff --git a/examples/.env.example b/examples/.env.example index 22af5f7ebd..58243868bb 100644 --- a/examples/.env.example +++ b/examples/.env.example @@ -1,2 +1 @@ -VITE_PUBLIC_ABLY_KEY= -NEXT_PUBLIC_ABLY_KEY= +VITE_ABLY_KEY= diff --git a/examples/auth-generate-jwt/javascript/README.md b/examples/auth-generate-jwt/javascript/README.md index 29399485c1..9150c880de 100644 --- a/examples/auth-generate-jwt/javascript/README.md +++ b/examples/auth-generate-jwt/javascript/README.md @@ -54,7 +54,7 @@ cd /examples/ mv .env.example .env.local ``` -7. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +7. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 8. Install dependencies: @@ -72,4 +72,4 @@ yarn run auth-generate-jwt-server ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/auth-generate-jwt/javascript/postcss.config.ts b/examples/auth-generate-jwt/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/auth-generate-jwt/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/auth-generate-jwt/javascript/tailwind.config.ts b/examples/auth-generate-jwt/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/auth-generate-jwt/javascript/tailwind.config.ts +++ b/examples/auth-generate-jwt/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/auth-generate-jwt/javascript/vite-env.d.ts b/examples/auth-generate-jwt/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/auth-generate-jwt/javascript/vite-env.d.ts +++ b/examples/auth-generate-jwt/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/auth-generate-jwt/javascript/vite.config.ts b/examples/auth-generate-jwt/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/auth-generate-jwt/javascript/vite.config.ts +++ b/examples/auth-generate-jwt/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/auth-generate-jwt/react/README.md b/examples/auth-generate-jwt/react/README.md index 495829ed7e..7fdc34df01 100644 --- a/examples/auth-generate-jwt/react/README.md +++ b/examples/auth-generate-jwt/react/README.md @@ -54,7 +54,7 @@ cd /examples/ mv .env.example .env.local ``` -7. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +7. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 8. Install dependencies: @@ -72,4 +72,4 @@ yarn run auth-generate-jwt-server ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/auth-generate-jwt/react/index.html b/examples/auth-generate-jwt/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/auth-generate-jwt/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/auth-generate-jwt/react/next.config.ts b/examples/auth-generate-jwt/react/next.config.ts deleted file mode 100644 index 4678774e6d..0000000000 --- a/examples/auth-generate-jwt/react/next.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -export default nextConfig; diff --git a/examples/auth-generate-jwt/react/package.json b/examples/auth-generate-jwt/react/package.json index eb6ca6c27f..6e1cc1e7f2 100644 --- a/examples/auth-generate-jwt/react/package.json +++ b/examples/auth-generate-jwt/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/auth-generate-jwt/react/postcss.config.ts b/examples/auth-generate-jwt/react/postcss.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/auth-generate-jwt/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/auth-generate-jwt/react/src/App.tsx b/examples/auth-generate-jwt/react/src/App.tsx new file mode 100644 index 0000000000..110c1a55d9 --- /dev/null +++ b/examples/auth-generate-jwt/react/src/App.tsx @@ -0,0 +1,84 @@ +import React, { useState, useEffect } from 'react'; +import * as Ably from 'ably'; +import { AblyProvider, useConnectionStateListener } from 'ably/react'; +import './styles/styles.css'; + +interface StatusMessage { + id: number; + success: boolean; + message: string; +} + +interface StatusMessagesProps { + messages: StatusMessage[]; + setMessages: React.Dispatch>; +} + +const StatusMessages = ({ messages, setMessages }: StatusMessagesProps) => { + useConnectionStateListener((stateChange) => { + if (stateChange.current === 'connected') { + setMessages((prevMessages) => prevMessages.map((msg) => (msg.id === 3 ? { ...msg, success: true } : msg))); + } + }); + + return ( +
+
+
+

Authentication to Ably with a JWT

+

The Ably client has been successfully initialized.

+
+ +
+ {messages.map((msg) => ( +
+ + {msg.success ? : } + + {msg.message} +
+ ))} +
+
+
+ ); +}; + +export default function App() { + const [client, setClient] = useState(null); + const [messages, setMessages] = useState([ + { id: 1, success: false, message: 'Client requests JWT from server' }, + { id: 2, success: false, message: 'Client initializes connection to Ably with generated JWT' }, + { id: 3, success: false, message: 'Client is connected' }, + ]); + + const handleConnect = async () => { + // Navigate to authenticated page + window.location.href = '/authenticated'; + }; + + return ( +
+
+
+

Authentication to Ably with a JWT

+

Press the Connect button to initialize the client:

+ +
+ +
+ {messages.map((msg, index) => ( +
+ + {msg.success ? : } + + {msg.message} +
+ ))} +
+
+
+ ); +} diff --git a/examples/auth-generate-jwt/react/src/app/authenticated/page.tsx b/examples/auth-generate-jwt/react/src/app/authenticated/page.tsx deleted file mode 100644 index 38a84c0e45..0000000000 --- a/examples/auth-generate-jwt/react/src/app/authenticated/page.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client'; - -import * as Ably from 'ably'; -import { AblyProvider, useConnectionStateListener } from 'ably/react'; -import { useState, useEffect } from 'react'; - -interface StatusMessage { - id: number; - success: boolean; - message: string; -} -interface StatusMessagesProps { - messages: StatusMessage[]; - setMessages: React.Dispatch>; -} - -const StatusMessages = ({ messages, setMessages }: StatusMessagesProps) => { - useConnectionStateListener((stateChange) => { - if (stateChange.current === 'connected') { - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 3 ? { ...msg, success: true } : msg - ) - ); - } - }); - - return ( -
-
-
-

Authentication to Ably with a JWT

-

The Ably client has been successfully initialized.

-
- -
- {messages.map((msg) => ( -
- - {msg.success ? - : - - } - - {msg.message} -
- ))} -
-
-
- ); -}; - -export default function Authenticated() { - const [messages, setMessages] = useState([ - { id: 1, success: false, message: "Client requests JWT from server" }, - { id: 2, success: false, message: "Client initializes connection to Ably with generated JWT" }, - { id: 3, success: false, message: "Client is connected"} - ]); - const [client, setClient] = useState(null); - - useEffect(() => { - try { - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 1 ? { ...msg, success: true } : msg - ) - ); - - const newClient = new Ably.Realtime({ authUrl: 'http://localhost:3001/generate-jwt' }); - setClient(newClient); - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 2 ? { ...msg, success: true } : msg - ) - ); - } catch (error) { - console.error('Failed to initialise client:', error); - } - }, []); - - if (!client) return null; - - return ( - - - - ); -} diff --git a/examples/auth-generate-jwt/react/src/app/layout.tsx b/examples/auth-generate-jwt/react/src/app/layout.tsx deleted file mode 100644 index f9a5b0e1e9..0000000000 --- a/examples/auth-generate-jwt/react/src/app/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import '../../styles/styles.css' -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/examples/auth-generate-jwt/react/src/app/page.tsx b/examples/auth-generate-jwt/react/src/app/page.tsx deleted file mode 100644 index 998a994d42..0000000000 --- a/examples/auth-generate-jwt/react/src/app/page.tsx +++ /dev/null @@ -1,55 +0,0 @@ -'use client'; - -import * as Ably from 'ably'; -import { useState } from 'react'; -import { useRouter } from 'next/navigation'; - -interface StatusMessage { - id: number; - success: boolean; - message: string; -} - -export default function Home() { - const [client, setClient] = useState(null); - const [messages, setMessages] = useState([ - { id: 1, success: false, message: "Client requests JWT from server" }, - { id: 2, success: false, message: "Client initializes connection to Ably with generated JWT" }, - { id: 3, success: false, message: "Client is connected"} - ]); - const router = useRouter(); - const handleConnect = async () => { - router.push('/authenticated'); - }; - - return ( -
-
-
-

Authentication to Ably with a JWT

-

Press the Connect button to initialize the client:

- -
- -
- {messages.map((msg, index) => ( -
- - {msg.success ? - : - - } - - {msg.message} -
- ))} -
-
-
- ); -} diff --git a/examples/auth-generate-jwt/react/src/env.d.ts b/examples/auth-generate-jwt/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/auth-generate-jwt/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/auth-generate-jwt/react/src/index.tsx b/examples/auth-generate-jwt/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/auth-generate-jwt/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/auth-generate-jwt/react/styles/styles.css b/examples/auth-generate-jwt/react/src/styles/styles.css similarity index 100% rename from examples/auth-generate-jwt/react/styles/styles.css rename to examples/auth-generate-jwt/react/src/styles/styles.css diff --git a/examples/auth-generate-jwt/react/tailwind.config.ts b/examples/auth-generate-jwt/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/auth-generate-jwt/react/tailwind.config.ts +++ b/examples/auth-generate-jwt/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/auth-generate-jwt/react/tsconfig.json b/examples/auth-generate-jwt/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/auth-generate-jwt/react/tsconfig.json +++ b/examples/auth-generate-jwt/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/auth-generate-jwt/react/vite.config.ts b/examples/auth-generate-jwt/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/auth-generate-jwt/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/auth-generate-jwt/server/src/server.ts b/examples/auth-generate-jwt/server/src/server.ts index 899dac1c50..c9ec3d265c 100644 --- a/examples/auth-generate-jwt/server/src/server.ts +++ b/examples/auth-generate-jwt/server/src/server.ts @@ -23,11 +23,11 @@ function base64urlEncode(str: string) { app.get('/generate-jwt', async (_req, res) => { console.log('1 - /generate-jwt endpoint called'); - const ablyApiKey = process.env.VITE_PUBLIC_ABLY_KEY || ''; + const ablyApiKey = process.env.VITE_ABLY_KEY || ''; const [apiKeyName, apiKeySecret] = ablyApiKey.split(':'); try { if (ablyApiKey === '') { - throw new Error('VITE_PUBLIC_ABLY_KEY is not set'); + throw new Error('VITE_ABLY_KEY is not set'); } const header = { diff --git a/examples/auth-request-token/javascript/README.md b/examples/auth-request-token/javascript/README.md index 9337d9dc7c..95c4cb654b 100644 --- a/examples/auth-request-token/javascript/README.md +++ b/examples/auth-request-token/javascript/README.md @@ -54,7 +54,7 @@ cd /examples/ mv .env.example .env.local ``` -7. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +7. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 8. Install dependencies: @@ -72,4 +72,4 @@ yarn run auth-request-token-server ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/auth-request-token/javascript/postcss.config.ts b/examples/auth-request-token/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/auth-request-token/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/auth-request-token/javascript/tailwind.config.ts b/examples/auth-request-token/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/auth-request-token/javascript/tailwind.config.ts +++ b/examples/auth-request-token/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/auth-request-token/javascript/vite-env.d.ts b/examples/auth-request-token/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/auth-request-token/javascript/vite-env.d.ts +++ b/examples/auth-request-token/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/auth-request-token/javascript/vite.config.ts b/examples/auth-request-token/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/auth-request-token/javascript/vite.config.ts +++ b/examples/auth-request-token/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/auth-request-token/react/README.md b/examples/auth-request-token/react/README.md index 84070ac7ff..5a7aa54be2 100644 --- a/examples/auth-request-token/react/README.md +++ b/examples/auth-request-token/react/README.md @@ -54,7 +54,7 @@ cd /examples/ mv .env.example .env.local ``` -7. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +7. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 8. Install dependencies: @@ -72,4 +72,4 @@ yarn run auth-request-token-server ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/auth-request-token/react/index.html b/examples/auth-request-token/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/auth-request-token/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/auth-request-token/react/next.config.ts b/examples/auth-request-token/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/auth-request-token/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/auth-request-token/react/package.json b/examples/auth-request-token/react/package.json index 1d1b86f9f9..3319cb0b57 100644 --- a/examples/auth-request-token/react/package.json +++ b/examples/auth-request-token/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/auth-request-token/react/postcss.config.ts b/examples/auth-request-token/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/auth-request-token/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/auth-request-token/react/src/App.tsx b/examples/auth-request-token/react/src/App.tsx new file mode 100644 index 0000000000..9e7c99d768 --- /dev/null +++ b/examples/auth-request-token/react/src/App.tsx @@ -0,0 +1,84 @@ +import React, { useState, useEffect } from 'react'; +import * as Ably from 'ably'; +import { AblyProvider, useConnectionStateListener } from 'ably/react'; +import './styles/styles.css'; + +interface StatusMessage { + id: number; + success: boolean; + message: string; +} + +interface StatusMessagesProps { + messages: StatusMessage[]; + setMessages: React.Dispatch>; +} + +const StatusMessages = ({ messages, setMessages }: StatusMessagesProps) => { + useConnectionStateListener((stateChange) => { + if (stateChange.current === 'connected') { + setMessages((prevMessages) => prevMessages.map((msg) => (msg.id === 3 ? { ...msg, success: true } : msg))); + } + }); + + return ( +
+
+
+

Authentication with Ably

+

The Ably client has been successfully initialized.

+
+ +
+ {messages.map((msg) => ( +
+ + {msg.success ? : } + + {msg.message} +
+ ))} +
+
+
+ ); +}; + +export default function App() { + const [client, setClient] = useState(null); + const [messages, setMessages] = useState([ + { id: 1, success: false, message: 'Client requests token from server' }, + { id: 2, success: false, message: 'Client initializes connection to Ably with generated Token' }, + { id: 3, success: false, message: 'Client is connected' }, + ]); + + const handleConnect = async () => { + // Navigate to authenticated page + window.location.href = '/authenticated'; + }; + + return ( +
+
+
+

Authentication with Ably

+

Press the Connect button to initialize the client:

+ +
+ +
+ {messages.map((msg, index) => ( +
+ + {msg.success ? : } + + {msg.message} +
+ ))} +
+
+
+ ); +} diff --git a/examples/auth-request-token/react/src/app/authenticated/page.tsx b/examples/auth-request-token/react/src/app/authenticated/page.tsx deleted file mode 100644 index d0804f765a..0000000000 --- a/examples/auth-request-token/react/src/app/authenticated/page.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client'; - -import * as Ably from 'ably'; -import { AblyProvider, useConnectionStateListener } from 'ably/react'; -import { useState, useEffect } from 'react'; - -interface StatusMessage { - id: number; - success: boolean; - message: string; -} -interface StatusMessagesProps { - messages: StatusMessage[]; - setMessages: React.Dispatch>; -} - -const StatusMessages = ({ messages, setMessages }: StatusMessagesProps) => { - useConnectionStateListener((stateChange) => { - if (stateChange.current === 'connected') { - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 3 ? { ...msg, success: true } : msg - ) - ); - } - }); - - return ( -
-
-
-

Authentication with Ably

-

The Ably client has been successfully initialized.

-
- -
- {messages.map((msg) => ( -
- - {msg.success ? - : - - } - - {msg.message} -
- ))} -
-
-
- ); -}; - -export default function Authenticated() { - const [messages, setMessages] = useState([ - { id: 1, success: false, message: "Client requests token from server" }, - { id: 2, success: false, message: "Client initializes connection to Ably with generated Token" }, - { id: 3, success: false, message: "Client is connected"} - ]); - const [client, setClient] = useState(null); - - useEffect(() => { - try { - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 1 ? { ...msg, success: true } : msg - ) - ); - - const newClient = new Ably.Realtime({ authUrl: 'http://localhost:3001/request-token' }); - setClient(newClient); - setMessages(prevMessages => - prevMessages.map(msg => - msg.id === 2 ? { ...msg, success: true } : msg - ) - ); - } catch (error) { - console.error('Failed to initialise client:', error); - } - }, []); - - if (!client) return null; - - return ( - - - - ); -} diff --git a/examples/auth-request-token/react/src/app/layout.tsx b/examples/auth-request-token/react/src/app/layout.tsx deleted file mode 100644 index ddb173747d..0000000000 --- a/examples/auth-request-token/react/src/app/layout.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import dynamic from 'next/dynamic'; -import '../../styles/styles.css' - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); -const inter = Inter({ subsets: ["latin"] }); - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/examples/auth-request-token/react/src/app/page.tsx b/examples/auth-request-token/react/src/app/page.tsx deleted file mode 100644 index 4fb7fd2f54..0000000000 --- a/examples/auth-request-token/react/src/app/page.tsx +++ /dev/null @@ -1,55 +0,0 @@ -'use client'; - -import * as Ably from 'ably'; -import { useState } from 'react'; -import { useRouter } from 'next/navigation'; - -interface StatusMessage { - id: number; - success: boolean; - message: string; -} - -export default function Home() { - const [client, setClient] = useState(null); - const [messages, setMessages] = useState([ - { id: 1, success: false, message: "Client requests token from server" }, - { id: 2, success: false, message: "Client initializes connection to Ably with generated Token" }, - { id: 3, success: false, message: "Client is connected"} - ]); - const router = useRouter(); - const handleConnect = async () => { - router.push('/authenticated'); - }; - - return ( -
-
-
-

Authentication with Ably

-

Press the Connect button to initialize the client:

- -
- -
- {messages.map((msg, index) => ( -
- - {msg.success ? - : - - } - - {msg.message} -
- ))} -
-
-
- ); -} diff --git a/examples/auth-request-token/react/src/env.d.ts b/examples/auth-request-token/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/auth-request-token/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/auth-request-token/react/src/index.tsx b/examples/auth-request-token/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/auth-request-token/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/chat-room-history/react/styles/styles.css b/examples/auth-request-token/react/src/styles/styles.css similarity index 100% rename from examples/chat-room-history/react/styles/styles.css rename to examples/auth-request-token/react/src/styles/styles.css diff --git a/examples/auth-request-token/react/tailwind.config.ts b/examples/auth-request-token/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/auth-request-token/react/tailwind.config.ts +++ b/examples/auth-request-token/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/auth-request-token/react/tsconfig.json b/examples/auth-request-token/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/auth-request-token/react/tsconfig.json +++ b/examples/auth-request-token/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/auth-request-token/react/vite.config.ts b/examples/auth-request-token/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/auth-request-token/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/auth-request-token/server/src/server.ts b/examples/auth-request-token/server/src/server.ts index e18cacedb8..c065fbba41 100644 --- a/examples/auth-request-token/server/src/server.ts +++ b/examples/auth-request-token/server/src/server.ts @@ -16,7 +16,7 @@ app.use( }), ); -const ably = new Ably.Rest(process.env.VITE_PUBLIC_ABLY_KEY || ''); +const ably = new Ably.Rest(process.env.VITE_ABLY_KEY || ''); app.get('/request-token', async (_req, res) => { console.log('1 - /request-token endpoint called'); diff --git a/examples/chat-online-status/javascript/README.md b/examples/chat-online-status/javascript/README.md index 7e52a88a9c..37bb40a712 100644 --- a/examples/chat-online-status/javascript/README.md +++ b/examples/chat-online-status/javascript/README.md @@ -12,9 +12,9 @@ Online statuses are implemented using [Ably Chat](/docs/products/chat). The Chat Use the following methods to add an online status implementation into a chat application: -* [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. -* [`rooms.presence.subscribe()`](/docs/chat/rooms/presence?lang=javascript#subscribe): subscribes to users' presence status events by registering a listener. Presence events are emitted when a user chooses a enter or leave the presence set or update their user data. -* [`room.presence.enter`, `room.presence.update`, `room.presence.leave`](/docs/chat/rooms/presence?lang=javascript#set): Emits a presence event when the user enters or leaves a presence set, or updates their user data. +- [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. +- [`rooms.presence.subscribe()`](/docs/chat/rooms/presence?lang=javascript#subscribe): subscribes to users' presence status events by registering a listener. Presence events are emitted when a user chooses a enter or leave the presence set or update their user data. +- [`room.presence.enter`, `room.presence.update`, `room.presence.leave`](/docs/chat/rooms/presence?lang=javascript#set): Emits a presence event when the user enters or leaves a presence set, or updates their user data. Find out more about [online status](/docs/chat/rooms/presence?lang=javascript). @@ -38,7 +38,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -56,4 +56,4 @@ yarn run chat-online-status-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-online-status/javascript/postcss.config.ts b/examples/chat-online-status/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/chat-online-status/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-online-status/javascript/src/script.ts b/examples/chat-online-status/javascript/src/script.ts index 9b7d7da7bb..1e392382ee 100644 --- a/examples/chat-online-status/javascript/src/script.ts +++ b/examples/chat-online-status/javascript/src/script.ts @@ -1,10 +1,11 @@ import * as Ably from 'ably'; import { ChatClient, PresenceEvent, PresenceMember, AllFeaturesEnabled } from '@ably/chat'; -import { faker } from '@faker-js/faker'; +import minifaker from 'minifaker'; +import 'minifaker/locales/en'; const realtimeClient = new Ably.Realtime({ - clientId: faker.person.firstName(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + clientId: minifaker.firstName(), + key: import.meta.env.VITE_ABLY_KEY as string, }); const chatClient = new ChatClient(realtimeClient); diff --git a/examples/chat-online-status/javascript/tailwind.config.ts b/examples/chat-online-status/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-online-status/javascript/tailwind.config.ts +++ b/examples/chat-online-status/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-online-status/javascript/vite-env.d.ts b/examples/chat-online-status/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/chat-online-status/javascript/vite-env.d.ts +++ b/examples/chat-online-status/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/chat-online-status/javascript/vite.config.ts b/examples/chat-online-status/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/chat-online-status/javascript/vite.config.ts +++ b/examples/chat-online-status/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/chat-online-status/react/README.md b/examples/chat-online-status/react/README.md index 4aa834e4d1..95ec9591a3 100644 --- a/examples/chat-online-status/react/README.md +++ b/examples/chat-online-status/react/README.md @@ -12,11 +12,11 @@ Online statuses are implemented using [Ably Chat](/docs/products/chat). The Chat Use the following components to add an online status implementation into a chat application: -* [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable real-time chat functionality across the application. -* [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and real-time interactions within that room via React context. -* [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. -* [`usePresence()`](/docs/chat/rooms/presence?lang=react#set) hook: a hook to manage the status updates made by users within a chat room. -* [`usePresenceListener()`](/docs/chat/rooms/presence?lang=react#subscribe) hook: a hook to manage the status updates made by users within a chat room. +- [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable real-time chat functionality across the application. +- [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and real-time interactions within that room via React context. +- [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. +- [`usePresence()`](/docs/chat/rooms/presence?lang=react#set) hook: a hook to manage the status updates made by users within a chat room. +- [`usePresenceListener()`](/docs/chat/rooms/presence?lang=react#subscribe) hook: a hook to manage the status updates made by users within a chat room. Find out more about [online status](/docs/chat/rooms/presence?lang=react). @@ -40,7 +40,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -58,4 +58,4 @@ yarn run chat-online-status-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-online-status/react/index.html b/examples/chat-online-status/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/chat-online-status/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/chat-online-status/react/next.config.ts b/examples/chat-online-status/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/chat-online-status/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-online-status/react/package.json b/examples/chat-online-status/react/package.json index f3e8a3b1fd..b3f01714d6 100644 --- a/examples/chat-online-status/react/package.json +++ b/examples/chat-online-status/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/chat-online-status/react/postcss.config.ts b/examples/chat-online-status/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/chat-online-status/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-online-status/react/src/app/page.tsx b/examples/chat-online-status/react/src/App.tsx similarity index 50% rename from examples/chat-online-status/react/src/app/page.tsx rename to examples/chat-online-status/react/src/App.tsx index 83c176e241..767c7830d3 100644 --- a/examples/chat-online-status/react/src/app/page.tsx +++ b/examples/chat-online-status/react/src/App.tsx @@ -1,26 +1,25 @@ -"use client"; - -import React from 'react'; -import { useChatClient, useRoom, usePresenceListener, usePresence } from '@ably/chat'; -import '../../styles/styles.css'; +import { useEffect, useState } from 'react'; +import { + useChatClient, + useRoom, + usePresenceListener, + usePresence, + ChatClient, + ChatClientProvider, + ChatRoomProvider, + AllFeaturesEnabled, +} from '@ably/chat'; +import { Realtime } from 'ably'; +import minifaker from 'minifaker'; +import 'minifaker/locales/en'; +import './styles/styles.css'; interface OnlineStatus { status: string; } -export default function Home() { - const { roomStatus, connectionStatus } = useRoom(); - - if (roomStatus !== 'attached' || connectionStatus !== 'connected') { - return
Loading...
; - } - - return ( -
- -
- ); -} +const realtimeClient = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: minifaker.firstName() }); +const chatClient = new ChatClient(realtimeClient); const Online = () => { const { presenceData } = usePresenceListener(); @@ -40,35 +39,71 @@ const Online = () => { >

- {onlineStatus.clientId}{onlineStatus.clientId === clientId ? ' (You)' : ''} -

-

- {(onlineStatus.data as OnlineStatus)?.status ?? 'Online'} + {onlineStatus.clientId} + {onlineStatus.clientId === clientId ? ' (You)' : ''}

+

{(onlineStatus.data as OnlineStatus)?.status ?? 'Online'}

))} {presenceData - .filter(status => status.clientId === clientId) - .map(userStatus => ( + .filter((status) => status.clientId === clientId) + .map((userStatus) => ( - )) - } + ))} ); +}; + +function Home() { + const { roomStatus, connectionStatus } = useRoom(); + + if (roomStatus !== 'attached' || connectionStatus !== 'connected') { + return
Loading...
; + } + + return ( +
+ +
+ ); +} + +export default function App() { + const [roomName, setRoomName] = useState('chat-online-status'); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const name = urlParams.get('name'); + + if (name !== null) { + setRoomName(name); + } + }, []); + + return ( +
+ + + + + +
+ ); } diff --git a/examples/chat-online-status/react/src/app/layout.tsx b/examples/chat-online-status/react/src/app/layout.tsx deleted file mode 100644 index 86fce9f5dc..0000000000 --- a/examples/chat-online-status/react/src/app/layout.tsx +++ /dev/null @@ -1,56 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { Realtime } from 'ably'; -import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; -import { ReactNode, useEffect, useState } from 'react'; -import { faker } from '@faker-js/faker'; -import dynamic from 'next/dynamic'; -import '../../styles/styles.css'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const realtimeClient = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: faker.person.firstName()}); -const chatClient = new ChatClient(realtimeClient); - -export default function RootLayout({ - children, -}: Readonly<{ - children: ReactNode; -}>) { - const [roomName, setRoomName] = useState('chat-online-status'); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const name = urlParams.get('name'); - - if (name !== null) { - setRoomName(name); - } - }, []); - - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/chat-online-status/react/src/env.d.ts b/examples/chat-online-status/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/chat-online-status/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/chat-online-status/react/src/index.tsx b/examples/chat-online-status/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/chat-online-status/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/chat-online-status/react/styles/styles.css b/examples/chat-online-status/react/src/styles/styles.css similarity index 92% rename from examples/chat-online-status/react/styles/styles.css rename to examples/chat-online-status/react/src/styles/styles.css index a788d5bae3..af551e2c6f 100644 --- a/examples/chat-online-status/react/styles/styles.css +++ b/examples/chat-online-status/react/src/styles/styles.css @@ -69,6 +69,16 @@ height: 100vh; } +input { + padding: 0.5rem; + width: 100%; + height: 2.5rem; + font-size: 0.875rem; + border-radius: 0.375rem; + outline: none; + transition: all 0.2s ease-in-out; +} + .away-button { margin-top: 1rem; padding: 0.5rem 1rem; diff --git a/examples/chat-online-status/react/tailwind.config.ts b/examples/chat-online-status/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-online-status/react/tailwind.config.ts +++ b/examples/chat-online-status/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-online-status/react/tsconfig.json b/examples/chat-online-status/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/chat-online-status/react/tsconfig.json +++ b/examples/chat-online-status/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/chat-online-status/react/vite.config.ts b/examples/chat-online-status/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/chat-online-status/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/chat-room-history/javascript/README.md b/examples/chat-room-history/javascript/README.md index f913ca6d56..c7a2cd4ff9 100644 --- a/examples/chat-room-history/javascript/README.md +++ b/examples/chat-room-history/javascript/README.md @@ -10,9 +10,9 @@ Message history is available in [Ably Chat](/docs/products/chat). The Chat SDK c Use the following methods to add message history into a chat application: -* [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. -* [`rooms.messages.subscribe()`](/docs/chat/rooms/history?lang=javascript#subscribe): subscribes to a room, retrieving previously sent messages while listening for future messages. -* [`room.messages.get`](/docs/chat/rooms/history?lang=javascript#get): retrieve messages that have been previously sent to a room. +- [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. +- [`rooms.messages.subscribe()`](/docs/chat/rooms/history?lang=javascript#subscribe): subscribes to a room, retrieving previously sent messages while listening for future messages. +- [`room.messages.get`](/docs/chat/rooms/history?lang=javascript#get): retrieve messages that have been previously sent to a room. Find out more about [message history](/docs/chat/rooms/history?lang=javascript). @@ -36,7 +36,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -54,4 +54,4 @@ yarn run chat-room-history-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-history/javascript/postcss.config.ts b/examples/chat-room-history/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/chat-room-history/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-history/javascript/src/script.ts b/examples/chat-room-history/javascript/src/script.ts index 5f2137ba75..7e04ead3d1 100644 --- a/examples/chat-room-history/javascript/src/script.ts +++ b/examples/chat-room-history/javascript/src/script.ts @@ -1,11 +1,12 @@ import * as Ably from 'ably'; import { ChatClient, Message, Room, AllFeaturesEnabled } from '@ably/chat'; -import { faker } from '@faker-js/faker'; +import minifaker from 'minifaker'; +import 'minifaker/locales/en'; import './styles.css'; const realtimeClient = new Ably.Realtime({ - clientId: faker.person.firstName(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + clientId: minifaker.firstName(), + key: import.meta.env.VITE_ABLY_KEY as string, }); // Number of times messages are sent to the chat room before the user enters the room. let sendCount = 0; diff --git a/examples/chat-room-history/javascript/tailwind.config.ts b/examples/chat-room-history/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-history/javascript/tailwind.config.ts +++ b/examples/chat-room-history/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-history/javascript/vite-env.d.ts b/examples/chat-room-history/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/chat-room-history/javascript/vite-env.d.ts +++ b/examples/chat-room-history/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/chat-room-history/javascript/vite.config.ts b/examples/chat-room-history/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/chat-room-history/javascript/vite.config.ts +++ b/examples/chat-room-history/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/chat-room-history/react/README.md b/examples/chat-room-history/react/README.md index 9dba127137..b0e9406ff0 100644 --- a/examples/chat-room-history/react/README.md +++ b/examples/chat-room-history/react/README.md @@ -10,10 +10,10 @@ Message history is available in [Ably Chat](/docs/products/chat). The Chat SDK c Use the following components to add message history into a chat application: -* [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable real-time chat functionality across the application. -* [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and real-time interactions within that room via React context. -* [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. -* [`useMessages()`](/docs/chat/rooms/history?lang=react#get) hook: a hook to retrieve messages that have been previously sent to a room. +- [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable real-time chat functionality across the application. +- [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and real-time interactions within that room via React context. +- [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. +- [`useMessages()`](/docs/chat/rooms/history?lang=react#get) hook: a hook to retrieve messages that have been previously sent to a room. Find out more about [message history](/docs/chat/rooms/history?lang=react). @@ -37,7 +37,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -55,4 +55,4 @@ yarn run chat-room-history-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-history/react/index.html b/examples/chat-room-history/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/chat-room-history/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/chat-room-history/react/next.config.ts b/examples/chat-room-history/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/chat-room-history/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-history/react/package.json b/examples/chat-room-history/react/package.json index b717622263..b324b1c97f 100644 --- a/examples/chat-room-history/react/package.json +++ b/examples/chat-room-history/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/chat-room-history/react/postcss.config.ts b/examples/chat-room-history/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/chat-room-history/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-history/react/src/App.tsx b/examples/chat-room-history/react/src/App.tsx new file mode 100644 index 0000000000..76e7f2cfc3 --- /dev/null +++ b/examples/chat-room-history/react/src/App.tsx @@ -0,0 +1,38 @@ +import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; +import { Realtime } from 'ably'; +import minifaker from 'minifaker'; +import 'minifaker/locales/en'; +import { useEffect, useState } from 'react'; +import './styles/styles.css'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; +import Home from './Home'; +import Chat from './Chat'; + +const realtimeClient = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: minifaker.firstName() }); +const chatClient = new ChatClient(realtimeClient); + +export default function App() { + const [roomName, setRoomName] = useState('chat-room-history'); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const name = urlParams.get('name'); + + if (name !== null) { + setRoomName(name); + } + }, []); + + return ( + + + + + } /> + } /> + + + + + ); +} diff --git a/examples/chat-room-history/react/src/app/chat/page.tsx b/examples/chat-room-history/react/src/Chat.tsx similarity index 67% rename from examples/chat-room-history/react/src/app/chat/page.tsx rename to examples/chat-room-history/react/src/Chat.tsx index c687361d29..1ab7b6a340 100644 --- a/examples/chat-room-history/react/src/app/chat/page.tsx +++ b/examples/chat-room-history/react/src/Chat.tsx @@ -1,8 +1,6 @@ -"use client"; - import { Message, useMessages, useChatClient } from '@ably/chat'; -import { useEffect, useState } from 'react'; -import '../../../styles/styles.css'; +import { useState } from 'react'; +import './styles/styles.css'; export default function Chat() { const [messages, setMessages] = useState([]); @@ -15,25 +13,25 @@ export default function Chat() { }); const getPastMessages = () => { - get({ limit: 10 }) - .then((result) => { - result.items.forEach(message => { - const messageExists = messages.some((prevMessage) => prevMessage.serial === message.serial); + get({ limit: 10 }).then((result) => { + result.items.forEach((message) => { + const messageExists = messages.some((prevMessage) => prevMessage.serial === message.serial); - if (messageExists) { - return messages; - } + if (messageExists) { + return messages; + } - setMessages((prevMessages: Message[]) => { - return [message, ...prevMessages]; - }); + setMessages((prevMessages: Message[]) => { + return [message, ...prevMessages]; }); - const loadButton = document.getElementById('load-past-messages'); - if (loadButton instanceof HTMLButtonElement) { - loadButton.disabled = true; - loadButton.className = 'uk-btn uk-btn-md uk-btn-primary mb-4 rounded absolute top-2 left-1/2 transform -translate-x-1/2 p-2'; - } }); + const loadButton = document.getElementById('load-past-messages'); + if (loadButton instanceof HTMLButtonElement) { + loadButton.disabled = true; + loadButton.className = + 'uk-btn uk-btn-md uk-btn-primary mb-4 rounded absolute top-2 left-1/2 transform -translate-x-1/2 p-2'; + } + }); }; const handleSubmit = (e: React.FormEvent) => { @@ -58,20 +56,15 @@ export default function Chat() { {messages.map((message, index) => (
- - {message.clientId === clientId ? "You" : message.clientId}: - - - {message.text} + + {message.clientId === clientId ? 'You' : message.clientId}: + {message.text}
))}
-
+
- -
diff --git a/examples/chat-room-history/react/src/app/layout.tsx b/examples/chat-room-history/react/src/app/layout.tsx deleted file mode 100644 index 3135b0f3eb..0000000000 --- a/examples/chat-room-history/react/src/app/layout.tsx +++ /dev/null @@ -1,56 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { Realtime } from 'ably'; -import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; -import { faker } from '@faker-js/faker'; -import '../../styles/styles.css' -import { useEffect, useState } from "react"; -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const realtimeClient = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: faker.person.firstName()}); -const chatClient = new ChatClient(realtimeClient); - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const [roomName, setRoomName] = useState('chat-room-history'); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const name = urlParams.get('name'); - - if (name !== null) { - setRoomName(name); - } - }, []); - - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/chat-room-history/react/src/env.d.ts b/examples/chat-room-history/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/chat-room-history/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/chat-room-history/react/src/index.tsx b/examples/chat-room-history/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/chat-room-history/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/auth-request-token/react/styles/styles.css b/examples/chat-room-history/react/src/styles/styles.css similarity index 85% rename from examples/auth-request-token/react/styles/styles.css rename to examples/chat-room-history/react/src/styles/styles.css index f6ee9ef952..778cb1fbfe 100644 --- a/examples/auth-request-token/react/styles/styles.css +++ b/examples/chat-room-history/react/src/styles/styles.css @@ -78,23 +78,3 @@ input { outline: none; transition: all 0.2s ease-in-out; } - -.container { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - position: relative; - background-color: #f4f8fb; - height: 100vh; -} - -input { - padding: 0.5rem; - width: 100%; - height: 2.5rem; - font-size: 0.875rem; - border-radius: 0.375rem; - outline: none; - transition: all 0.2s ease-in-out; -} diff --git a/examples/chat-room-history/react/tailwind.config.ts b/examples/chat-room-history/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-history/react/tailwind.config.ts +++ b/examples/chat-room-history/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-history/react/tsconfig.json b/examples/chat-room-history/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/chat-room-history/react/tsconfig.json +++ b/examples/chat-room-history/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/chat-room-history/react/vite.config.ts b/examples/chat-room-history/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/chat-room-history/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/chat-room-messages/javascript/README.md b/examples/chat-room-messages/javascript/README.md index abb9efcc64..579e944ff9 100644 --- a/examples/chat-room-messages/javascript/README.md +++ b/examples/chat-room-messages/javascript/README.md @@ -12,9 +12,9 @@ Messaging is implemented using [Ably Chat](/docs/products/chat). The Chat SDK co Use the following methods to send and receive messages into a chat application: -* [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. -* [`rooms.messages.subscribe()`](/docs/chat/rooms/messages?lang=javascript#subscribe): subscribes to room messages events by registering a listener. Message events are emitted when a user sends a message. -* [`room.messages.send`](/docs/chat/rooms/messages?lang=javascript#send): Emits a message event when the user sends a message to the room. +- [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. +- [`rooms.messages.subscribe()`](/docs/chat/rooms/messages?lang=javascript#subscribe): subscribes to room messages events by registering a listener. Message events are emitted when a user sends a message. +- [`room.messages.send`](/docs/chat/rooms/messages?lang=javascript#send): Emits a message event when the user sends a message to the room. Find out more about [rooms](/docs/chat/rooms?lang=javascript) and [messages](/docs/chat/rooms/messages?lang=javascript). @@ -38,7 +38,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -56,4 +56,4 @@ yarn run chat-room-messages-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-messages/javascript/postcss.config.ts b/examples/chat-room-messages/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/chat-room-messages/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-messages/javascript/src/script.ts b/examples/chat-room-messages/javascript/src/script.ts index f662c32c90..e852fb1d81 100644 --- a/examples/chat-room-messages/javascript/src/script.ts +++ b/examples/chat-room-messages/javascript/src/script.ts @@ -6,7 +6,7 @@ const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; const realtimeClient = new Ably.Realtime({ clientId: mockName(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + key: import.meta.env.VITE_ABLY_KEY as string, }); let chatClient: ChatClient; diff --git a/examples/chat-room-messages/javascript/tailwind.config.ts b/examples/chat-room-messages/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-messages/javascript/tailwind.config.ts +++ b/examples/chat-room-messages/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-messages/javascript/vite-env.d.ts b/examples/chat-room-messages/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/chat-room-messages/javascript/vite-env.d.ts +++ b/examples/chat-room-messages/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/chat-room-messages/javascript/vite.config.ts b/examples/chat-room-messages/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/chat-room-messages/javascript/vite.config.ts +++ b/examples/chat-room-messages/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/chat-room-messages/react/README.md b/examples/chat-room-messages/react/README.md index 1c52c75397..a2f9a38b09 100644 --- a/examples/chat-room-messages/react/README.md +++ b/examples/chat-room-messages/react/README.md @@ -12,10 +12,10 @@ Messaging is implemented using [Ably Chat](/docs/products/chat). The Chat SDK co Use the following components to send and receive messages into a chat application: -* [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. -* [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. -* [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. -* [`useMessages()`](/docs/chat/rooms/messages?lang=react#subscribe) hook: a hook to manage and track the messages sent by users within a chat room. +- [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. +- [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. +- [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. +- [`useMessages()`](/docs/chat/rooms/messages?lang=react#subscribe) hook: a hook to manage and track the messages sent by users within a chat room. Find out more about [rooms](/docs/chat/rooms?lang=react) and [messages](/docs/chat/rooms/messages?lang=react). @@ -39,7 +39,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -57,4 +57,4 @@ yarn run chat-room-messages-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-messages/react/index.html b/examples/chat-room-messages/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/chat-room-messages/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/chat-room-messages/react/next.config.ts b/examples/chat-room-messages/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/chat-room-messages/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-messages/react/package.json b/examples/chat-room-messages/react/package.json index c096e239bb..6bb6a74108 100644 --- a/examples/chat-room-messages/react/package.json +++ b/examples/chat-room-messages/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/chat-room-messages/react/postcss.config.ts b/examples/chat-room-messages/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/chat-room-messages/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-messages/react/src/app/page.tsx b/examples/chat-room-messages/react/src/App.tsx similarity index 57% rename from examples/chat-room-messages/react/src/app/page.tsx rename to examples/chat-room-messages/react/src/App.tsx index 7c3d670b77..dbbc3d2944 100644 --- a/examples/chat-room-messages/react/src/app/page.tsx +++ b/examples/chat-room-messages/react/src/App.tsx @@ -1,22 +1,22 @@ -"use client"; +import React, { useEffect, useState } from 'react'; +import { + Message, + useRoom, + useMessages, + useChatClient, + ChatClient, + ChatClientProvider, + ChatRoomProvider, + AllFeaturesEnabled, +} from '@ably/chat'; +import { Realtime } from 'ably'; +import './styles/styles.css'; -import { Message, useRoom, useMessages, useChatClient } from '@ably/chat'; -import { useState } from 'react'; -import '../../styles/styles.css' +const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; +const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; -export default function Home() { - const { roomStatus, connectionStatus } = useRoom(); - - if (roomStatus !== 'attached' || connectionStatus !== 'connected') { - return
Loading...
; - } - - return ( -
- -
- ); -} +const realtimeClient = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: mockName() }); +const chatClient = new ChatClient(realtimeClient); const Chat = () => { const [messages, setMessages] = useState([]); @@ -36,27 +36,22 @@ const Chat = () => { return (
-
-
+
{messages.map((message, index) => (
- - {message.clientId === clientId ? "You" : message.clientId}: - - - {message.text} + + {message.clientId === clientId ? 'You' : message.clientId}: + {message.text}
))}
- + { autoFocus />
-
); +}; + +function ChatRoomMessagesDemo() { + const { roomStatus, connectionStatus } = useRoom(); + + if (roomStatus !== 'attached' || connectionStatus !== 'connected') { + return
Loading...
; + } + + return ( +
+ +
+ ); +} + +export default function App() { + const [roomName, setRoomName] = useState('chat-room-messages'); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const name = urlParams.get('name'); + + if (name !== null) { + setRoomName(name); + } + }, []); + + return ( +
+ + + + + +
+ ); } diff --git a/examples/chat-room-messages/react/src/app/layout.tsx b/examples/chat-room-messages/react/src/app/layout.tsx deleted file mode 100644 index f39e48bea5..0000000000 --- a/examples/chat-room-messages/react/src/app/layout.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; -import { Realtime } from 'ably'; -import '../../styles/styles.css' -import { useEffect, useState } from "react"; -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; -const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; - -const realtimeClient = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: mockName()}); -const chatClient = new ChatClient(realtimeClient); - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const [roomName, setRoomName] = useState('chat-room-messages'); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const name = urlParams.get('name'); - - if (name !== null) { - setRoomName(name); - } - }, []); - - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/chat-room-messages/react/src/env.d.ts b/examples/chat-room-messages/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/chat-room-messages/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/chat-room-messages/react/src/index.tsx b/examples/chat-room-messages/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/chat-room-messages/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/pub-sub-channel-state/react/styles/styles.css b/examples/chat-room-messages/react/src/styles/styles.css similarity index 83% rename from examples/pub-sub-channel-state/react/styles/styles.css rename to examples/chat-room-messages/react/src/styles/styles.css index 4abae025b7..778cb1fbfe 100644 --- a/examples/pub-sub-channel-state/react/styles/styles.css +++ b/examples/chat-room-messages/react/src/styles/styles.css @@ -58,3 +58,23 @@ --chart-5: 340 75% 55%; } } + +.container { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + position: relative; + background-color: #f4f8fb; + height: 100vh; +} + +input { + padding: 0.5rem; + width: 100%; + height: 2.5rem; + font-size: 0.875rem; + border-radius: 0.375rem; + outline: none; + transition: all 0.2s ease-in-out; +} diff --git a/examples/chat-room-messages/react/tailwind.config.ts b/examples/chat-room-messages/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-messages/react/tailwind.config.ts +++ b/examples/chat-room-messages/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-messages/react/vite.config.ts b/examples/chat-room-messages/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/chat-room-messages/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/chat-room-reactions/javascript/README.md b/examples/chat-room-reactions/javascript/README.md index 5d476f23d6..4e0968bfca 100644 --- a/examples/chat-room-reactions/javascript/README.md +++ b/examples/chat-room-reactions/javascript/README.md @@ -12,9 +12,9 @@ Room reactions are implemented using [Ably Chat](/docs/products/chat). The Chat Use the following methods to add room reactions into a chat application: -* [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. -* [`rooms.reactions.subscribe()`](/docs/chat/rooms/reactions?lang=javascript#subscribe): subscribes to room reaction events events by registering a listener. Reaction events are emitted when a user chooses a reaction to send. -* [`room.reactions.send`](/docs/chat/rooms/reactions?lang=javascript#send): Emits a reaction event when the user chooses a room reaction. +- [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. +- [`rooms.reactions.subscribe()`](/docs/chat/rooms/reactions?lang=javascript#subscribe): subscribes to room reaction events events by registering a listener. Reaction events are emitted when a user chooses a reaction to send. +- [`room.reactions.send`](/docs/chat/rooms/reactions?lang=javascript#send): Emits a reaction event when the user chooses a room reaction. Find out more about [room reactions](/docs/chat/rooms/reactions?lang=javascript). @@ -38,7 +38,7 @@ cd /examples/ mv .env.example .env.local ``` -1. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +1. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 2. Install dependencies: @@ -56,4 +56,4 @@ yarn run chat-room-reactions-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-reactions/javascript/index.html b/examples/chat-room-reactions/javascript/index.html index 5f0e1e51d8..4554bf5f58 100644 --- a/examples/chat-room-reactions/javascript/index.html +++ b/examples/chat-room-reactions/javascript/index.html @@ -1,57 +1,46 @@ - - - - - - Chat room reactions Demo - - -
-
-
+ + + + + + + + Chat room reactions Demo + + + +
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+ + +
+
- - -
- -
- -
-
-
- - + +
+
+
+ + + \ No newline at end of file diff --git a/examples/chat-room-reactions/javascript/postcss.config.ts b/examples/chat-room-reactions/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/chat-room-reactions/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-reactions/javascript/src/script.ts b/examples/chat-room-reactions/javascript/src/script.ts index 27e0e4c7cb..e03b55f8e4 100644 --- a/examples/chat-room-reactions/javascript/src/script.ts +++ b/examples/chat-room-reactions/javascript/src/script.ts @@ -5,7 +5,7 @@ import './styles.css'; const realtimeClient = new Ably.Realtime({ clientId: nanoid(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + key: import.meta.env.VITE_ABLY_KEY as string, }); let room: Room; diff --git a/examples/chat-room-reactions/javascript/tailwind.config.ts b/examples/chat-room-reactions/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-reactions/javascript/tailwind.config.ts +++ b/examples/chat-room-reactions/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-reactions/javascript/vite-env.d.ts b/examples/chat-room-reactions/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/chat-room-reactions/javascript/vite-env.d.ts +++ b/examples/chat-room-reactions/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/chat-room-reactions/javascript/vite.config.ts b/examples/chat-room-reactions/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/chat-room-reactions/javascript/vite.config.ts +++ b/examples/chat-room-reactions/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/chat-room-reactions/react/README.md b/examples/chat-room-reactions/react/README.md index aad0d2650c..f971883527 100644 --- a/examples/chat-room-reactions/react/README.md +++ b/examples/chat-room-reactions/react/README.md @@ -12,10 +12,10 @@ Room reactions are implemented using [Ably Chat](/docs/products/chat). The Chat Use the following components to add room reactions into a chat application: -* [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. -* [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. -* [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. -* [`useRoomReactions()`](/docs/chat/rooms/reactions?lang=react#subscribe) hook: a hook to manage the reactions sent by users within a chat room. +- [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. +- [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. +- [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. +- [`useRoomReactions()`](/docs/chat/rooms/reactions?lang=react#subscribe) hook: a hook to manage the reactions sent by users within a chat room. Find out more about [room reactions](/docs/chat/rooms/reactions). @@ -39,7 +39,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -57,4 +57,4 @@ yarn run chat-room-reactions-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-room-reactions/react/index.html b/examples/chat-room-reactions/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/chat-room-reactions/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/chat-room-reactions/react/next.config.ts b/examples/chat-room-reactions/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/chat-room-reactions/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-reactions/react/package.json b/examples/chat-room-reactions/react/package.json index b89aa8caab..4a5b370a22 100644 --- a/examples/chat-room-reactions/react/package.json +++ b/examples/chat-room-reactions/react/package.json @@ -1,11 +1,12 @@ { "name": "chat-room-reactions-react", - "version": "0.1.0", "private": true, + "version": "0.1.0", + "type": "module", "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" } } diff --git a/examples/chat-room-reactions/react/postcss.config.ts b/examples/chat-room-reactions/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/chat-room-reactions/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-room-reactions/react/src/app/page.tsx b/examples/chat-room-reactions/react/src/App.tsx similarity index 58% rename from examples/chat-room-reactions/react/src/app/page.tsx rename to examples/chat-room-reactions/react/src/App.tsx index eabdf30a11..f564e4639e 100644 --- a/examples/chat-room-reactions/react/src/app/page.tsx +++ b/examples/chat-room-reactions/react/src/App.tsx @@ -1,54 +1,40 @@ -"use client"; - -import React, { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; +import { Realtime } from 'ably'; import { Reaction as ReactionInterface, useRoom, useRoomReactions } from '@ably/chat'; -import '../../styles/styles.css' +import './styles/styles.css'; -export default function Home() { - const { roomStatus, connectionStatus } = useRoom(); +const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; +const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; - if (roomStatus !== 'attached' || connectionStatus !== 'connected') { - return
Loading...
; - } +const realtimeClient = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: mockName() }); +const chatClient = new ChatClient(realtimeClient); - return ( -
- -
- ); -} - -const Chat = () => { +function Chat() { const [reactions, setReactions] = useState([]); - const emojis = ['❤️', '😲', '👍', '😊']; const { send } = useRoomReactions({ listener: (reaction) => { - setReactions((prevReactions: ReactionInterface[]) => [...prevReactions, {...reaction}]) + setReactions((prevReactions: ReactionInterface[]) => [...prevReactions, { ...reaction }]); setTimeout(() => { - setReactions(prevReactions => prevReactions.filter(r => r.createdAt !== reaction.createdAt)); + setReactions((prevReactions) => prevReactions.filter((r) => r.createdAt !== reaction.createdAt)); }, 4000); }, }); return (
-
+
-
+ >
{emojis.map((emoji, index) => ( - send({type: emoji})} - > + send({ type: emoji })}> {emoji} ))} @@ -57,14 +43,14 @@ const Chat = () => {
{reactions.map((reaction, index) => ( - {reaction.type} + + {reaction.type} + ))}
-
+ { autoFocus />
-
); } + +function ChatRoomReactionsDemo() { + const { roomStatus, connectionStatus } = useRoom(); + + if (roomStatus !== 'attached' || connectionStatus !== 'connected') { + return
Loading...
; + } + + return ( +
+ +
+ ); +} + +export default function App() { + const [roomName, setRoomName] = useState('chat-room-reactions'); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const name = urlParams.get('name'); + + if (name !== null) { + setRoomName(name); + } + }, []); + + return ( + + + + + + ); +} diff --git a/examples/chat-room-reactions/react/src/app/layout.tsx b/examples/chat-room-reactions/react/src/app/layout.tsx deleted file mode 100644 index 0100c3020a..0000000000 --- a/examples/chat-room-reactions/react/src/app/layout.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled } from '@ably/chat'; -import { Realtime } from 'ably'; -import '../../styles/styles.css' -import { useEffect, useState } from "react"; -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; -const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; - -const realtimeClient = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: mockName()}); -const chatClient = new ChatClient(realtimeClient); - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const [roomName, setRoomName] = useState('chat-room-reactions'); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const name = urlParams.get('name'); - - if (name !== null) { - setRoomName(name); - } - }, []); - - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/chat-room-reactions/react/src/index.tsx b/examples/chat-room-reactions/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/chat-room-reactions/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/pub-sub-channel-messages/react/styles/styles.css b/examples/chat-room-reactions/react/src/styles/styles.css similarity index 97% rename from examples/pub-sub-channel-messages/react/styles/styles.css rename to examples/chat-room-reactions/react/src/styles/styles.css index bf0c5da581..20fdae0384 100644 --- a/examples/pub-sub-channel-messages/react/styles/styles.css +++ b/examples/chat-room-reactions/react/src/styles/styles.css @@ -105,7 +105,9 @@ input { bottom: 0; left: 50%; transform: translateX(-50%); - animation: moveUp 5s linear, waveSideToSide 2s infinite; + animation: + moveUp 5s linear, + waveSideToSide 2s infinite; } .emoji-btn { @@ -130,7 +132,8 @@ input { } @keyframes waveSideToSide { - 0%, 100% { + 0%, + 100% { transform: translateX(-50%); } 25% { diff --git a/examples/chat-room-reactions/react/tailwind.config.ts b/examples/chat-room-reactions/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-room-reactions/react/tailwind.config.ts +++ b/examples/chat-room-reactions/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-room-reactions/react/tsconfig.json b/examples/chat-room-reactions/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/chat-room-reactions/react/tsconfig.json +++ b/examples/chat-room-reactions/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/chat-room-reactions/react/vite.config.ts b/examples/chat-room-reactions/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/chat-room-reactions/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/chat-typing-indicator/javascript/README.md b/examples/chat-typing-indicator/javascript/README.md index 7e285b1562..e8e82ca05a 100644 --- a/examples/chat-typing-indicator/javascript/README.md +++ b/examples/chat-typing-indicator/javascript/README.md @@ -12,10 +12,10 @@ Typing indicators are implemented using [Ably Chat](/docs/products/chat). The Ch Use the following methods to add typing indicators into a chat application: -* [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. -* [`rooms.typing.subscribe()`](/docs/chat/rooms/typing#subscribe): subscribes to typing events by registering a listener. Typing events are emitted when a user starts typing, or when they stop typing. -* [`room.typing.get()`](/docs/chat/rooms/typing?lang=javascript#retrieve): retrieves the list of users currently typing by their clientId. -* [`room.typing.start()`](/docs/chat/rooms/typing?lang=javascript#set): emits a typing event that the user is currently typing, initialising the timeout which will call `room.typing.stop()` if no further events are emitted by the user. +- [`rooms.get()`](/docs/chat/rooms?lang=javascript#create): creates a new or retrieves an existing `room`. +- [`rooms.typing.subscribe()`](/docs/chat/rooms/typing#subscribe): subscribes to typing events by registering a listener. Typing events are emitted when a user starts typing, or when they stop typing. +- [`room.typing.get()`](/docs/chat/rooms/typing?lang=javascript#retrieve): retrieves the list of users currently typing by their clientId. +- [`room.typing.start()`](/docs/chat/rooms/typing?lang=javascript#set): emits a typing event that the user is currently typing, initialising the timeout which will call `room.typing.stop()` if no further events are emitted by the user. Find out more about [typing indicators](/docs/chat/rooms/typing). @@ -39,7 +39,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -57,4 +57,4 @@ yarn run chat-typing-indicator-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-typing-indicator/javascript/index.html b/examples/chat-typing-indicator/javascript/index.html index 7f75bda423..76d1ace58c 100644 --- a/examples/chat-typing-indicator/javascript/index.html +++ b/examples/chat-typing-indicator/javascript/index.html @@ -1,18 +1,23 @@ - - - - - - User Typing Demo - - -
-
- - -
+ + + + + + + + User Typing Demo + + + +
+
+ +
- - +
+ + + \ No newline at end of file diff --git a/examples/chat-typing-indicator/javascript/postcss.config.ts b/examples/chat-typing-indicator/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/chat-typing-indicator/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-typing-indicator/javascript/src/script.ts b/examples/chat-typing-indicator/javascript/src/script.ts index a165f47adc..ba43efebd2 100644 --- a/examples/chat-typing-indicator/javascript/src/script.ts +++ b/examples/chat-typing-indicator/javascript/src/script.ts @@ -5,7 +5,7 @@ import { nanoid } from 'nanoid'; const names = ['Bob', 'Jane', 'John', 'Sammy']; const realtimeClient = new Ably.Realtime({ clientId: names[Math.floor(Math.random() * names.length)] ?? nanoid(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + key: import.meta.env.VITE_ABLY_KEY as string, }); const chatClient = new ChatClient(realtimeClient); diff --git a/examples/chat-typing-indicator/javascript/tailwind.config.ts b/examples/chat-typing-indicator/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-typing-indicator/javascript/tailwind.config.ts +++ b/examples/chat-typing-indicator/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-typing-indicator/javascript/vite-env.d.ts b/examples/chat-typing-indicator/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/chat-typing-indicator/javascript/vite-env.d.ts +++ b/examples/chat-typing-indicator/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/chat-typing-indicator/javascript/vite.config.ts b/examples/chat-typing-indicator/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/chat-typing-indicator/javascript/vite.config.ts +++ b/examples/chat-typing-indicator/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/chat-typing-indicator/react/README.md b/examples/chat-typing-indicator/react/README.md index dded787315..0e723c370d 100644 --- a/examples/chat-typing-indicator/react/README.md +++ b/examples/chat-typing-indicator/react/README.md @@ -12,10 +12,10 @@ Typing indicators are implemented using [Ably Chat](/docs/products/chat). The Ch Use the following components to add typing indicators into a chat application: -* [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. -* [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. -* [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. -* [`useTyping()`](/docs/chat/rooms/typing?lang=react#subscribe) hook: a hook to manage and track the typing status of users within a chat room. +- [`ChatClientProvider`](/docs/chat/setup?lang=react#instantiate): initializes and manages a shared chat client instance, passing it down through React context to enable realtime chat functionality across the application. +- [`ChatRoomProvider`](/docs/chat/rooms?lang=react#create): manages the state and functionality of a specific chat room, providing access to messages, participants, and realtime interactions within that room via React context. +- [`useRoom()`](/docs/chat/rooms?lang=react#create) hook: a hook to manage the state and interaction for a chat “room”, allowing users to join, send messages, listen for messages, and use the other chat functions such as typing indicators. +- [`useTyping()`](/docs/chat/rooms/typing?lang=react#subscribe) hook: a hook to manage and track the typing status of users within a chat room. Find out more about [typing indicators](/docs/chat/rooms/typing). @@ -39,7 +39,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -57,4 +57,4 @@ yarn run chat-typing-indicator-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/chat-typing-indicator/react/index.html b/examples/chat-typing-indicator/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/chat-typing-indicator/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/chat-typing-indicator/react/next.config.ts b/examples/chat-typing-indicator/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/chat-typing-indicator/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-typing-indicator/react/package.json b/examples/chat-typing-indicator/react/package.json index 5c75325477..f526c34533 100644 --- a/examples/chat-typing-indicator/react/package.json +++ b/examples/chat-typing-indicator/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/chat-typing-indicator/react/postcss.config.ts b/examples/chat-typing-indicator/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/chat-typing-indicator/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/chat-typing-indicator/react/src/App.tsx b/examples/chat-typing-indicator/react/src/App.tsx new file mode 100644 index 0000000000..b78e799157 --- /dev/null +++ b/examples/chat-typing-indicator/react/src/App.tsx @@ -0,0 +1,101 @@ +import { useEffect, useState } from 'react'; +import { + TypingEvent, + useChatClient, + useRoom, + useTyping, + ChatClient, + ChatClientProvider, + ChatRoomProvider, + AllFeaturesEnabled, + TypingOptions, +} from '@ably/chat'; +import { Realtime } from 'ably'; +import './styles/styles.css'; + +const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; +const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; + +const realtimeClient = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: mockName() }); +const chatClient = new ChatClient(realtimeClient); + +const typingOptions: TypingOptions = { + timeoutMs: 5000, +}; + +const Loading = () =>
Loading...
; + +const ChatInput = () => { + const { start, currentlyTyping } = useTyping({ + listener: (typingEvent: TypingEvent) => { + console.log('Typing event received: ', typingEvent); + }, + }); + const { clientId } = useChatClient(); + + const typingClientIds = Array.from(currentlyTyping).filter((id) => id !== clientId); + const clientsTyping = typingClientIds.join(' and '); + const typingIndicatorText = clientsTyping + ? `${clientsTyping} ${typingClientIds.length === 1 ? 'is' : 'are'} typing` + : ''; + + return ( +
+
+ + +
+
+ ); +}; + +function ChatApp() { + const { roomStatus, connectionStatus } = useRoom(); + + if (roomStatus !== 'attached' || connectionStatus !== 'connected') { + return ; + } + + return ( +
+ +
+ ); +} + +export default function App() { + const [roomName, setRoomName] = useState('chat-typing-indicator'); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const name = urlParams.get('name'); + + if (name !== null) { + setRoomName(name); + } + }, []); + + return ( +
+ + + + + +
+ ); +} diff --git a/examples/chat-typing-indicator/react/src/app/layout.tsx b/examples/chat-typing-indicator/react/src/app/layout.tsx deleted file mode 100644 index 710d850e76..0000000000 --- a/examples/chat-typing-indicator/react/src/app/layout.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { Realtime } from 'ably'; -import { ChatClient, ChatClientProvider, ChatRoomProvider, AllFeaturesEnabled, TypingOptions } from '@ably/chat'; -import { useEffect, useState } from "react"; -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const mockNames = ['Bob', 'Jane', 'John', 'Sammy']; -const mockName = () => mockNames[Math.floor(Math.random() * mockNames.length)]; - -const realtimeClient = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: mockName()}); -const chatClient = new ChatClient(realtimeClient); - -const typingOptions: TypingOptions = { - timeoutMs: 5000, -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const [roomName, setRoomName] = useState('chat-typing-indicator'); - - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const name = urlParams.get('name'); - - if (name !== null) { - setRoomName(name); - } - }, []); - - - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/chat-typing-indicator/react/src/app/page.tsx b/examples/chat-typing-indicator/react/src/app/page.tsx deleted file mode 100644 index c3178fbdf0..0000000000 --- a/examples/chat-typing-indicator/react/src/app/page.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; - -import { TypingEvent, useChatClient, useRoom, useTyping } from '@ably/chat'; -import '../../styles/styles.css' - -const Loading = () =>
Loading...
; - -const ChatInput = () => { - const {start, currentlyTyping } = useTyping({ - listener: (typingEvent: TypingEvent) => { - console.log('Typing event received: ', typingEvent); - }, - }); - const { clientId } = useChatClient(); - - const typingClientIds = Array.from(currentlyTyping).filter((id) => id !== clientId); - const clientsTyping = typingClientIds.join(' and '); - const typingIndicatorText = clientsTyping ? `${clientsTyping} ${typingClientIds.length === 1 ? 'is' : 'are'} typing` : ''; - - return ( -
-
- - -
-
- ); -} - -export default function ChatApp() { - const { roomStatus, connectionStatus } = useRoom(); - - if (roomStatus !== 'attached' || connectionStatus !== 'connected') { - return ; - } - - return ( -
- -
- ); -} diff --git a/examples/chat-typing-indicator/react/src/env.d.ts b/examples/chat-typing-indicator/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/chat-typing-indicator/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/chat-typing-indicator/react/src/index.tsx b/examples/chat-typing-indicator/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/chat-typing-indicator/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/chat-typing-indicator/react/styles/styles.css b/examples/chat-typing-indicator/react/src/styles/styles.css similarity index 100% rename from examples/chat-typing-indicator/react/styles/styles.css rename to examples/chat-typing-indicator/react/src/styles/styles.css index 946d43bde8..ae5fd8f541 100644 --- a/examples/chat-typing-indicator/react/styles/styles.css +++ b/examples/chat-typing-indicator/react/src/styles/styles.css @@ -69,6 +69,16 @@ height: 100vh; } +input { + padding: 0.5rem; + width: 100%; + height: 2.5rem; + font-size: 0.875rem; + border-radius: 0.375rem; + outline: none; + transition: all 0.2s ease-in-out; +} + .inner { width: 100%; max-width: 320px; @@ -103,13 +113,3 @@ label { font-weight: 500; color: black; } - -input { - padding: 0.5rem; - width: 100%; - height: 2.5rem; - font-size: 0.875rem; - border-radius: 0.375rem; - outline: none; - transition: all 0.2s ease-in-out; -} diff --git a/examples/chat-typing-indicator/react/tailwind.config.ts b/examples/chat-typing-indicator/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/chat-typing-indicator/react/tailwind.config.ts +++ b/examples/chat-typing-indicator/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/chat-typing-indicator/react/tsconfig.json b/examples/chat-typing-indicator/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/chat-typing-indicator/react/tsconfig.json +++ b/examples/chat-typing-indicator/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/chat-typing-indicator/react/vite.config.ts b/examples/chat-typing-indicator/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/chat-typing-indicator/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/next.config.ts b/examples/next.config.ts deleted file mode 100644 index fb0d97d3bb..0000000000 --- a/examples/next.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { NextConfig } from 'next'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(process.cwd(), '../../.env.local') }); - -const baseConfig: NextConfig = { - reactStrictMode: false, - env: { - NEXT_PUBLIC_ABLY_KEY: process.env.NEXT_PUBLIC_ABLY_KEY, - }, -}; - -export default baseConfig; diff --git a/examples/package.json b/examples/package.json index 94d8d3bc7f..14e87eaae9 100644 --- a/examples/package.json +++ b/examples/package.json @@ -88,36 +88,36 @@ "dependencies": { "@ably/chat": "^0.5.0", "@ably/spaces": "^0.4.0", - "@tailwindcss/postcss": "^4.0.14", "ably": "^2.5.0", "cors": "^2.8.5", - "franken-ui": "^2.0.0-internal.42", + "franken-ui": "^2.0.0", + "minifaker": "^1.34.1", "nanoid": "^5.0.7", - "next": "^15", - "postcss-combine-duplicated-selectors": "^10.0.3", - "postcss-sort-media-queries": "^5.2.0", "react": "^18", "react-dom": "^18", "react-icons": "^5.4.0", - "ts-node": "^10.9.2", + "react-router-dom": "^6.22.2", "uikit": "^3.7.0", - "usehooks-ts": "^3.1.0", - "vite": "^5.4.2" + "usehooks-ts": "^3.1.0" }, "devDependencies": { - "@faker-js/faker": "^9.2.0", + "@tailwindcss/postcss": "^4.0.14", "@types/express": "^5.0.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "@types/uikit": "^3.7.0", - "autoprefixer": "^10.4.20", + "@vitejs/plugin-react": "^3.0.0", + "autoprefixer": "^10.4.21", "dotenv": "^16.4.5", "eslint": "^8", - "eslint-config-next": "15.0.3", "express": "^4.21.1", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" + "postcss": "^8.5.3", + "postcss-combine-duplicated-selectors": "^10.0.3", + "postcss-load-config": "^6.0.1", + "tailwindcss": "3", + "ts-node": "^10.9.2", + "typescript": "^5", + "vite": "4" } } diff --git a/examples/postcss.config.ts b/examples/postcss.config.mts similarity index 52% rename from examples/postcss.config.ts rename to examples/postcss.config.mts index 8347fed650..e25555e645 100644 --- a/examples/postcss.config.ts +++ b/examples/postcss.config.mts @@ -2,6 +2,9 @@ const baseConfig = { plugins: { tailwindcss: {}, autoprefixer: {}, + 'franken-ui/postcss/combine-duplicated-selectors': { + removeDuplicatedProperties: true, + }, }, }; diff --git a/examples/pub-sub-channel-messages/javascript/README.md b/examples/pub-sub-channel-messages/javascript/README.md index 8aedcdfb3e..720f14cf34 100644 --- a/examples/pub-sub-channel-messages/javascript/README.md +++ b/examples/pub-sub-channel-messages/javascript/README.md @@ -12,9 +12,9 @@ Messaging is implemented using [Ably Pub/Sub](/docs/channels/messages). The Pub/ Use the following methods to send and receive messages in a pub/sub application: -* [`channel.get()`](/docs/channels#create): creates a new or retrieves an existing `channel`. -* [`channel.subscribe()`](/docs/channels#subscribe): subscribes to channel messages events by registering a listener. Message events are emitted when a user publishes a message. -* [`channel.publish`](/docs/channels#publish): emits a message event when the user publishes a message to the channel. +- [`channel.get()`](/docs/channels#create): creates a new or retrieves an existing `channel`. +- [`channel.subscribe()`](/docs/channels#subscribe): subscribes to channel messages events by registering a listener. Message events are emitted when a user publishes a message. +- [`channel.publish`](/docs/channels#publish): emits a message event when the user publishes a message to the channel. Find out more about [channels](/docs/channels) and [messages](/docs/channels/messages). @@ -38,7 +38,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -56,4 +56,4 @@ yarn run pub-sub-channel-messages-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/pub-sub-channel-messages/javascript/postcss.config.ts b/examples/pub-sub-channel-messages/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/pub-sub-channel-messages/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-messages/javascript/src/script.ts b/examples/pub-sub-channel-messages/javascript/src/script.ts index 79f5d6e739..901c6c83bc 100644 --- a/examples/pub-sub-channel-messages/javascript/src/script.ts +++ b/examples/pub-sub-channel-messages/javascript/src/script.ts @@ -4,7 +4,7 @@ import './styles.css'; const client = new Ably.Realtime({ clientId: nanoid(), - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + key: import.meta.env.VITE_ABLY_KEY as string, }); let headlines = [ diff --git a/examples/pub-sub-channel-messages/javascript/tailwind.config.ts b/examples/pub-sub-channel-messages/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/pub-sub-channel-messages/javascript/tailwind.config.ts +++ b/examples/pub-sub-channel-messages/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/pub-sub-channel-messages/javascript/vite-env.d.ts b/examples/pub-sub-channel-messages/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/pub-sub-channel-messages/javascript/vite-env.d.ts +++ b/examples/pub-sub-channel-messages/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/pub-sub-channel-messages/javascript/vite.config.ts b/examples/pub-sub-channel-messages/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/pub-sub-channel-messages/javascript/vite.config.ts +++ b/examples/pub-sub-channel-messages/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/pub-sub-channel-messages/react/README.md b/examples/pub-sub-channel-messages/react/README.md index 19044ee442..315c8237b5 100644 --- a/examples/pub-sub-channel-messages/react/README.md +++ b/examples/pub-sub-channel-messages/react/README.md @@ -12,10 +12,10 @@ Messaging is implemented using [Ably Pub/Sub](/docs/channels/messages). The Pub/ Use the following components to send and receive messages in a pub/sub application: -* [`AblyProvider`](/docs/getting-started/react#ably-provider): initializes and manages a shared pub/sub client instance, passing it down through React context to enable realtime pub/sub functionality across the application. -* [`ChannelProvider`](/docs/getting-started/react#channel-provider): manages the state and functionality of a specific channel, providing access to messages, members, and realtime interactions within that channel via React context. -* [`useChannel()`](/docs/getting-started/react#useChannel) hook: a hook to manage the state and interaction for a channel, allowing users to join, send messages, and listen for messages. -* [`publish()`](/docs/getting-started/react#useChannel) function: a function to publish messages to the specified channel. +- [`AblyProvider`](/docs/getting-started/react#ably-provider): initializes and manages a shared pub/sub client instance, passing it down through React context to enable realtime pub/sub functionality across the application. +- [`ChannelProvider`](/docs/getting-started/react#channel-provider): manages the state and functionality of a specific channel, providing access to messages, members, and realtime interactions within that channel via React context. +- [`useChannel()`](/docs/getting-started/react#useChannel) hook: a hook to manage the state and interaction for a channel, allowing users to join, send messages, and listen for messages. +- [`publish()`](/docs/getting-started/react#useChannel) function: a function to publish messages to the specified channel. Find out more about [channels](/docs/channels) and [messages](/docs/channels/messages). @@ -39,7 +39,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -57,4 +57,4 @@ yarn run pub-sub-channel-messages-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/pub-sub-channel-messages/react/index.html b/examples/pub-sub-channel-messages/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/pub-sub-channel-messages/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/pub-sub-channel-messages/react/next.config.ts b/examples/pub-sub-channel-messages/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/pub-sub-channel-messages/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-messages/react/package.json b/examples/pub-sub-channel-messages/react/package.json index 856f0a4574..5ee5cfa01a 100644 --- a/examples/pub-sub-channel-messages/react/package.json +++ b/examples/pub-sub-channel-messages/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/pub-sub-channel-messages/react/postcss.config.ts b/examples/pub-sub-channel-messages/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/pub-sub-channel-messages/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-messages/react/src/app/page.tsx b/examples/pub-sub-channel-messages/react/src/App.tsx similarity index 50% rename from examples/pub-sub-channel-messages/react/src/app/page.tsx rename to examples/pub-sub-channel-messages/react/src/App.tsx index d9bd2822d3..fc22a2fabb 100644 --- a/examples/pub-sub-channel-messages/react/src/app/page.tsx +++ b/examples/pub-sub-channel-messages/react/src/App.tsx @@ -1,7 +1,9 @@ -'use client' - import { useState } from 'react'; import { useChannel } from 'ably/react'; +import { Realtime } from 'ably'; +import { AblyProvider, ChannelProvider } from 'ably/react'; +import { nanoid } from 'nanoid'; +import './styles/styles.css'; interface Message { text: string; @@ -9,36 +11,34 @@ interface Message { } const urlParams = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : {}); +const channelName = urlParams.get('name') || 'pub-sub-channel-messages'; + +const client = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: nanoid() }); -export default function Home() { +function ChannelMessages() { const [messages, setMessages] = useState([]); const [headlines, setHeadlines] = useState([ - "AI Breakthrough: New Model Achieves Human-Level Understanding in Complex Tasks", - "SpaceX Successfully Launches 100th Mission to Mars", - "Quantum Computing Milestone: 1000 Qubit Processor Unveiled", - "Revolutionary Battery Technology Promises Week-Long Phone Charge", - "Web4 Protocol Introduces Decentralized Neural Networks", - "Flying Cars Get FAA Approval for Urban Transportation", - "Scientists Develop Self-Healing Smartphone Screens", - "Blockchain Technology Revolutionizes Global Supply Chain", - "New Chip Architecture Doubles Computing Power While Halving Energy Use", - "Virtual Reality Breakthrough: Neural Interface Allows Direct Brain Connection" + 'AI Breakthrough: New Model Achieves Human-Level Understanding in Complex Tasks', + 'SpaceX Successfully Launches 100th Mission to Mars', + 'Quantum Computing Milestone: 1000 Qubit Processor Unveiled', + 'Revolutionary Battery Technology Promises Week-Long Phone Charge', + 'Web4 Protocol Introduces Decentralized Neural Networks', + 'Flying Cars Get FAA Approval for Urban Transportation', + 'Scientists Develop Self-Healing Smartphone Screens', + 'Blockchain Technology Revolutionizes Global Supply Chain', + 'New Chip Architecture Doubles Computing Power While Halving Energy Use', + 'Virtual Reality Breakthrough: Neural Interface Allows Direct Brain Connection', ]); const [isButtonDisabled, setIsButtonDisabled] = useState(false); - const { publish } = useChannel(urlParams.get('name') || 'pub-sub-channel-messages', (message) => { - setMessages(prev => [{ text: message.data, isNew: true }, ...prev]); + const { publish } = useChannel(channelName, (message) => { + setMessages((prev) => [{ text: message.data, isNew: true }, ...prev]); setTimeout(() => { - setMessages(prev => - prev.map(msg => - msg.text === message.data ? { ...msg, isNew: false } : msg - ) - ); + setMessages((prev) => prev.map((msg) => (msg.text === message.data ? { ...msg, isNew: false } : msg))); }, 5000); }); - const publishHeadline = async () => { if (headlines.length === 0) { setIsButtonDisabled(true); @@ -46,13 +46,13 @@ export default function Home() { publishButton.className = 'bg-gray-500 text-white px-4 py-2 rounded'; return; - }; + } const randomIndex = Math.floor(Math.random() * headlines.length); const selectedHeadline = headlines[randomIndex]; await publish('headline', selectedHeadline); - setHeadlines(prev => prev.filter((_, index) => index !== randomIndex)); + setHeadlines((prev) => prev.filter((_, index) => index !== randomIndex)); }; return ( @@ -71,9 +71,7 @@ export default function Home() { {messages.map((msg, index) => (
{msg.isNew && ( - - NEW - + NEW )} {msg.text}
@@ -81,9 +79,17 @@ export default function Home() {
-
- Headlines remaining: {headlines.length} -
+
Headlines remaining: {headlines.length}
); } + +export default function App() { + return ( + + + + + + ); +} diff --git a/examples/pub-sub-channel-messages/react/src/app/layout.tsx b/examples/pub-sub-channel-messages/react/src/app/layout.tsx deleted file mode 100644 index 63cf18a0e0..0000000000 --- a/examples/pub-sub-channel-messages/react/src/app/layout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client"; - -import { Inter } from "next/font/google"; -import { Realtime } from 'ably'; -import { AblyProvider, ChannelProvider } from 'ably/react'; -import { nanoid } from 'nanoid'; -import '../../styles/styles.css'; -import dynamic from 'next/dynamic'; - -// @ts-ignore -dynamic(() => import('franken-ui/js/core.iife'), { - ssr: false, -}); - -// @ts-ignore -dynamic(() => import('franken-ui/js/icon.iife'), { - ssr: false, -}); - -const inter = Inter({ subsets: ["latin"] }); - -const client = new Realtime({key: process.env.NEXT_PUBLIC_ABLY_KEY, clientId: nanoid()}); -const urlParams = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : {}); -const channelName = urlParams.get('name') || 'pub-sub-channel-messages'; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - - - {children} - - - - - ); -} diff --git a/examples/pub-sub-channel-messages/react/src/env.d.ts b/examples/pub-sub-channel-messages/react/src/env.d.ts new file mode 100644 index 0000000000..7421e8e743 --- /dev/null +++ b/examples/pub-sub-channel-messages/react/src/env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ABLY_KEY: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/examples/pub-sub-channel-messages/react/src/index.tsx b/examples/pub-sub-channel-messages/react/src/index.tsx new file mode 100644 index 0000000000..c018515cd7 --- /dev/null +++ b/examples/pub-sub-channel-messages/react/src/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/chat-room-reactions/react/styles/styles.css b/examples/pub-sub-channel-messages/react/src/styles/styles.css similarity index 97% rename from examples/chat-room-reactions/react/styles/styles.css rename to examples/pub-sub-channel-messages/react/src/styles/styles.css index bf0c5da581..20fdae0384 100644 --- a/examples/chat-room-reactions/react/styles/styles.css +++ b/examples/pub-sub-channel-messages/react/src/styles/styles.css @@ -105,7 +105,9 @@ input { bottom: 0; left: 50%; transform: translateX(-50%); - animation: moveUp 5s linear, waveSideToSide 2s infinite; + animation: + moveUp 5s linear, + waveSideToSide 2s infinite; } .emoji-btn { @@ -130,7 +132,8 @@ input { } @keyframes waveSideToSide { - 0%, 100% { + 0%, + 100% { transform: translateX(-50%); } 25% { diff --git a/examples/pub-sub-channel-messages/react/tailwind.config.ts b/examples/pub-sub-channel-messages/react/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/pub-sub-channel-messages/react/tailwind.config.ts +++ b/examples/pub-sub-channel-messages/react/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/pub-sub-channel-messages/react/tsconfig.json b/examples/pub-sub-channel-messages/react/tsconfig.json index c83c1542a1..4082f16a5d 100644 --- a/examples/pub-sub-channel-messages/react/tsconfig.json +++ b/examples/pub-sub-channel-messages/react/tsconfig.json @@ -1,19 +1,3 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "extends": "../../tsconfig.json" } diff --git a/examples/pub-sub-channel-messages/react/vite.config.ts b/examples/pub-sub-channel-messages/react/vite.config.ts new file mode 100644 index 0000000000..3b1cf13b4f --- /dev/null +++ b/examples/pub-sub-channel-messages/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import baseConfig from '../../vite.config'; + +export default defineConfig({ + ...baseConfig, + envDir: '../../', +}); diff --git a/examples/pub-sub-channel-state/javascript/README.md b/examples/pub-sub-channel-state/javascript/README.md index a9f4d5a2b2..9cc50e055b 100644 --- a/examples/pub-sub-channel-state/javascript/README.md +++ b/examples/pub-sub-channel-state/javascript/README.md @@ -10,8 +10,8 @@ Channel states is implemented using [Ably Pub/Sub](/docs/). The Pub/Sub SDK prov Use the following components to manage the channel state in a pub/sub application: -* [`channel.get()`](/docs/channels#create): creates a new or retrieves an existing `channel`. -* [`channel.on()`](/docs/channels/states?lang=javascript): subscribes to the channel state events of your client by registering a listener. +- [`channel.get()`](/docs/channels#create): creates a new or retrieves an existing `channel`. +- [`channel.on()`](/docs/channels/states?lang=javascript): subscribes to the channel state events of your client by registering a listener. Find out more about [channel states](/docs/channels/states). @@ -35,7 +35,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `VITE_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -53,4 +53,4 @@ yarn run pub-sub-channel-state-javascript ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/pub-sub-channel-state/javascript/postcss.config.ts b/examples/pub-sub-channel-state/javascript/postcss.config.ts deleted file mode 100644 index 5dd35ae633..0000000000 --- a/examples/pub-sub-channel-state/javascript/postcss.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import baseConfig from '../../postcss.config'; - -const config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-state/javascript/src/script.ts b/examples/pub-sub-channel-state/javascript/src/script.ts index 01629b185f..4fd76d73d6 100644 --- a/examples/pub-sub-channel-state/javascript/src/script.ts +++ b/examples/pub-sub-channel-state/javascript/src/script.ts @@ -9,7 +9,7 @@ const urlParams = new URLSearchParams(window.location.search); const connect = () => { // Initialises a new client instance with the Ably key and client ID. const newClient = new Ably.Realtime({ - key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, + key: import.meta.env.VITE_ABLY_KEY as string, clientId: nanoid(), }); diff --git a/examples/pub-sub-channel-state/javascript/tailwind.config.ts b/examples/pub-sub-channel-state/javascript/tailwind.config.ts index 238d7ed443..1c86e1c371 100644 --- a/examples/pub-sub-channel-state/javascript/tailwind.config.ts +++ b/examples/pub-sub-channel-state/javascript/tailwind.config.ts @@ -3,6 +3,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, + content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config; diff --git a/examples/pub-sub-channel-state/javascript/vite-env.d.ts b/examples/pub-sub-channel-state/javascript/vite-env.d.ts index 9454b92dea..449e61aa75 100644 --- a/examples/pub-sub-channel-state/javascript/vite-env.d.ts +++ b/examples/pub-sub-channel-state/javascript/vite-env.d.ts @@ -1,5 +1,5 @@ interface ImportMetaEnv { - readonly VITE_PUBLIC_ABLY_KEY: string; + readonly VITE_ABLY_KEY: string; // Add other environment variables here if needed } diff --git a/examples/pub-sub-channel-state/javascript/vite.config.ts b/examples/pub-sub-channel-state/javascript/vite.config.ts index b7961c3d30..3b1cf13b4f 100644 --- a/examples/pub-sub-channel-state/javascript/vite.config.ts +++ b/examples/pub-sub-channel-state/javascript/vite.config.ts @@ -1,10 +1,7 @@ -import baseConfig from '../../vite.config'; import { defineConfig } from 'vite'; -import dotenv from 'dotenv'; -import path from 'path'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.local') }); +import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, + envDir: '../../', }); diff --git a/examples/pub-sub-channel-state/react/README.md b/examples/pub-sub-channel-state/react/README.md index fc311835c2..22818de749 100644 --- a/examples/pub-sub-channel-state/react/README.md +++ b/examples/pub-sub-channel-state/react/README.md @@ -10,9 +10,9 @@ Channel states is implemented using [Ably Pub/Sub](/docs/). The Pub/Sub SDK prov Use the following components to manage the channel state in a pub/sub application: -* [`AblyProvider`](/docs/getting-started/react#ably-provider): initializes and manages a shared pub/sub client instance, passing it down through React context to enable realtime pub/sub functionality across the application. -* [`ChannelProvider`](/docs/getting-started/react#channel-provider): initializes and manages a shared channel instance, passing it down through React context to enable realtime pub/sub functionality across the application. -* [`useChannelStateListener`](/docs/getting-started/react#useChannelStateListener): creates a subscriber to be notified of channel state changes. +- [`AblyProvider`](/docs/getting-started/react#ably-provider): initializes and manages a shared pub/sub client instance, passing it down through React context to enable realtime pub/sub functionality across the application. +- [`ChannelProvider`](/docs/getting-started/react#channel-provider): initializes and manages a shared channel instance, passing it down through React context to enable realtime pub/sub functionality across the application. +- [`useChannelStateListener`](/docs/getting-started/react#useChannelStateListener): creates a subscriber to be notified of channel state changes. Find out more about [channel states](/docs/channels/states). @@ -36,7 +36,7 @@ cd /examples/ mv .env.example .env.local ``` -4. In `.env.local` update the value of `NEXT_PUBLIC_ABLY_KEY` to be your Ably API key. +4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. 5. Install dependencies: @@ -54,4 +54,4 @@ yarn run pub-sub-channel-state-react ## Open in CodeSandbox -In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `NEXT_PUBLIC_ABLY_KEY` variable to use your Ably API key. +In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. diff --git a/examples/pub-sub-channel-state/react/index.html b/examples/pub-sub-channel-state/react/index.html new file mode 100644 index 0000000000..231a299f0e --- /dev/null +++ b/examples/pub-sub-channel-state/react/index.html @@ -0,0 +1,12 @@ + + + + + + Ably Example + + +
+ + + diff --git a/examples/pub-sub-channel-state/react/next.config.ts b/examples/pub-sub-channel-state/react/next.config.ts deleted file mode 100644 index 7b42b36c42..0000000000 --- a/examples/pub-sub-channel-state/react/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../next.config'; -import type { NextConfig } from 'next'; - -const config: NextConfig = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-state/react/package.json b/examples/pub-sub-channel-state/react/package.json index 4dc1c88c50..395b60fb85 100644 --- a/examples/pub-sub-channel-state/react/package.json +++ b/examples/pub-sub-channel-state/react/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "next lint" } } diff --git a/examples/pub-sub-channel-state/react/postcss.config.ts b/examples/pub-sub-channel-state/react/postcss.config.ts deleted file mode 100644 index 0077e40b27..0000000000 --- a/examples/pub-sub-channel-state/react/postcss.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import baseConfig from '../../postcss.config'; -import { Config } from 'postcss-load-config'; - -const config: Config = { - ...baseConfig, -}; - -export default config; diff --git a/examples/pub-sub-channel-state/react/src/app/page.tsx b/examples/pub-sub-channel-state/react/src/App.tsx similarity index 51% rename from examples/pub-sub-channel-state/react/src/app/page.tsx rename to examples/pub-sub-channel-state/react/src/App.tsx index 1def2c0c51..fd67317af7 100644 --- a/examples/pub-sub-channel-state/react/src/app/page.tsx +++ b/examples/pub-sub-channel-state/react/src/App.tsx @@ -1,9 +1,13 @@ -'use client' - import { useEffect, useState } from 'react'; import { useChannel, useChannelStateListener } from 'ably/react'; +import { Realtime } from 'ably'; +import { AblyProvider, ChannelProvider } from 'ably/react'; +import { nanoid } from 'nanoid'; +import './styles/styles.css'; + +const client = new Realtime({ key: import.meta.env.VITE_ABLY_KEY, clientId: nanoid() }); -export default function Home() { +function ChannelState() { const [isAttached, setIsAttached] = useState(false); const [channelStates, setChannelStates] = useState([]); const [channelName, setChannelName] = useState('pub-sub-channel-state'); @@ -17,9 +21,12 @@ export default function Home() { } }, []); - useChannelStateListener({channelName}, (stateChange) => { + useChannelStateListener({ channelName }, (stateChange) => { const timestamp = new Date().toLocaleTimeString(); - setChannelStates((prevStates) => [`[${timestamp}] State changed: ${stateChange.previous} to ${stateChange.current}`, ...prevStates]); + setChannelStates((prevStates) => [ + `[${timestamp}] State changed: ${stateChange.previous} to ${stateChange.current}`, + ...prevStates, + ]); if (stateChange.current === 'attached') { setIsAttached(true); } else { @@ -42,28 +49,41 @@ export default function Home() {

Controlling the channel state

{isAttached ? (
-
) : (
-
)} -