From 86125ee4c658c87982255d174f9a85997f99aea8 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Wed, 19 Mar 2025 11:18:49 +0000 Subject: [PATCH 01/14] temp: test mdx work --- .../{connect.textile => connectold.textile} | 0 content/chat/index.mdx | 43 +++ .../chat/{index.textile => indexold.textile} | 0 content/chat/setup.mdx | 262 ++++++++++++++++++ .../chat/{setup.textile => setupold.textile} | 0 data/createPages/index.ts | 66 +++++ gatsby-config.ts | 11 +- gatsby-ssr.tsx | 1 - package.json | 2 +- src/components/Layout/MDXWrapper.tsx | 25 ++ src/components/Markdown/LanguageBlock.tsx | 19 ++ yarn.lock | 90 +++++- 12 files changed, 509 insertions(+), 10 deletions(-) rename content/chat/{connect.textile => connectold.textile} (100%) create mode 100644 content/chat/index.mdx rename content/chat/{index.textile => indexold.textile} (100%) create mode 100644 content/chat/setup.mdx rename content/chat/{setup.textile => setupold.textile} (100%) create mode 100644 src/components/Layout/MDXWrapper.tsx create mode 100644 src/components/Markdown/LanguageBlock.tsx diff --git a/content/chat/connect.textile b/content/chat/connectold.textile similarity index 100% rename from content/chat/connect.textile rename to content/chat/connectold.textile diff --git a/content/chat/index.mdx b/content/chat/index.mdx new file mode 100644 index 0000000000..955b936ce3 --- /dev/null +++ b/content/chat/index.mdx @@ -0,0 +1,43 @@ +--- +title: About Chat +slug: /docs/chat +meta_description: 'Learn more about Ably Chat and the features that enable you to quickly build functionality into new and existing applications.' +redirect_from: /docs/products/chat +--- + +Ably Chat is a product dedicated to making it quick and easy to build chat functionality into new and existing applications. Use Ably Chat to build things such as a 1:1 customer support feature, or add a chat component to a livestreaming platform that serves hundreds of thousands of users. + +The Chat SDK contains a set of purpose-built APIs that abstract away the complexities involved in how you would architect chat features. It utilizes Ably's platform to benefit from all of the same performance guarantees and scaling potential. + +## Chat features + +Ably Chat provides the following key features: + +- [Rooms and messages](#rooms) +- [Online status](#online) +- [Typing indicators](#typing) +- [Room reactions](#reactions) + +### Rooms and messages + +[Rooms](/docs/chat/rooms) are used to organize and separate your users and chat messages into 'chat rooms'. They are the entry object into chat and provide access to all other chat features, such as messages, online status, and typing indicators. + +Each room can represent a 1:1 chat between an agent and a customer, a private message between two users in a chat application, a group conversation, or the chat section of a livestream with thousands of users. + +[Messages](/docs/chat/rooms/messages) enable users to communicate with one another in the room. Messages sent by users are received by all those who have subscribed to receive them within that room. + +### Online status + +[Online status](/docs/chat/rooms/presence) enables you to display the status of every user in the room, such as whether a user is online or offline. Users can also set additional information about their profile or set a custom status, such as 'Away'. + +### Typing indicators + +[Typing indicators](/docs/chat/rooms/typing) let users see when others start and stop typing a message. They enable you to display a message such as _John is typing..._ or when too many users are typing, something like _Multiple people are typing..._ or _12 people are typing..._ + +### Room reactions + +[Room reactions](/docs/chat/rooms/reactions) enable users to broadcast ephemeral sentiments using emojis, such as 👍 or ❤. Room reactions are used to broadcast a general sentiment to the entire room rather than reacting to a single message. A common use case is sports fans all sending a heart when their team scores. + +## Demo + +Take a look at a [livestream basketball game](https://ably-livestream-chat-demo.vercel.app) with some simulated users chatting built using the Chat SDK. The [source code](https://github.com/ably/ably-chat-js/tree/main/demo) is available in GitHub. diff --git a/content/chat/index.textile b/content/chat/indexold.textile similarity index 100% rename from content/chat/index.textile rename to content/chat/indexold.textile diff --git a/content/chat/setup.mdx b/content/chat/setup.mdx new file mode 100644 index 0000000000..55ceff296b --- /dev/null +++ b/content/chat/setup.mdx @@ -0,0 +1,262 @@ +--- +title: SDK setup +meta_description: 'Install, authenticate and instantiate the Chat SDK.' +product: chat +languages: + - javascript + - react + - swift + - kotlin +--- + +Use these instructions to install, authenticate and instantiate the Chat SDK. + + + +h2(#authentication). Authentication + +An API key is required to authenticate with Ably. API keys are used either to authenticate directly with Ably using basic authentication, or to generate tokens for untrusted clients using "token authentication":/docs/auth/token. + + + +"Sign up":https://ably.com/sign-up to Ably to create an API key in the dashboard or use the "Control API":/docs/account/control-api to create an API key programmatically. + +API keys and tokens have a set of "capabilities":/docs/auth/capabilities assigned to them that specify which operations, such as subscribe or publish can be performed on which resources. To use the Chat SDK, the API key requires the following capabilities depending on which features are being used: + +|_. Feature |_. Capabilities | +| Send and receive messages | Publish, Subscribe | +| Message history | Subscribe, History | +| Online status | Subscribe, Presence | +| Room occupancy | Subscribe, Channel Metadata | +| Typing indicators | Publish, Subscribe, Presence | +| Room reactions | Publish, Subscribe | + +When setting the capabilities for Chat, you need to apply them to either a wildcard resource, or a wildcard resource prefixed with the chat namespace, for example: + +- @[chat]_@ and @_@, or +- @[*]\*@ + +h2(#install). Install + +The Chat SDK is built on top of the Ably Pub/Sub SDK and uses that to establish a connection with Ably. + +blang[javascript,react]. +h3(#npm). NPM + +Install the Pub/Sub SDK and the Chat SDK: + +```[sh] +npm install @ably/chat +``` + +Import the SDKs into your project: + +```[javascript] +import * as Ably from 'ably'; +import { ChatClient } from '@ably/chat'; +``` + +```[react] +import * as Ably from 'ably'; +import { ChatClient, ChatClientProvider} from '@ably/chat'; +``` + +blang[swift,kotlin]. + +blang[javascript]. +h3(#cdn). CDN + +Reference the Pub/Sub SDK and the Chat SDK within your HTML file: + +```[html] + + + +``` + +blang[swift]. +h3(#spm). Swift Package Manager + +The SDK is distributed as a Swift Package and can be installed using Xcode or by adding it as a dependency in your package’s @Package.swift@. + +To install the @ably-chat-swift@ package in your Xcode Project: +_ Paste @https://github.com/ably/ably-chat-swift@ in the Swift Packages search box (Xcode project → Swift Packages.. . → @+@ button). +_ Select the Ably Chat SDK for your target. + +To install the @ably-chat-swift@ package in another Swift Package, add the following to your @Package.swift@: + +```[swift] +.package(url: "https://github.com/ably/ably-chat-swift", from: "0.1.0"), +``` + +Import the SDK: + +```[swift] +import AblyChat +``` + +blang[kotlin]. +h3(#gradle). Gradle + +The Ably Chat SDK is available on the Maven Central Repository. To include the dependency in your project, add the following to your @build.gradle.kts@ file: + +```[kotlin] + implementation("com.ably.chat:chat-android:0.2.0") +``` + +For groovy: + +```[kotlin] + implementation 'com.ably.chat:chat-android:0.2.0' +``` + +h2(#instantiate). Instantiate a client + +Instantiate a realtime client using the Pub/Sub SDK and pass the generated client into the Chat constructor. + +blang[react]. +Pass the @ChatClient@ into the "@ChatClientProvider@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat-react.ChatClientProvider.html. The @ChatClient@ instance will be available to all child components in your React component tree. + +blang[javascript,swift,kotlin]. + +```[javascript] +import { LogLevel } from '@ably/chat' + +const realtimeClient = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); +const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Error }); +``` + +```[react] +import { LogLevel } from '@ably/chat' + +const realtimeClient = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); +const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Error }); + +const App = () => { + return ( + + + + ); +}; +``` + +```[swift] +let realtimeOptions = ARTClientOptions() +realtimeOptions.key = "{{API_KEY}}" +realtimeOptions.clientId = "" +let realtime = ARTRealtime(options: realtimeOptions) +let chatClient = DefaultChatClient(realtime: realtime, clientOptions: nil) +``` + +```[kotlin] +import com.ably.chat.ChatClient +import io.ably.lib.realtime.AblyRealtime +import io.ably.lib.types.ClientOptions + +val realtimeClient = AblyRealtime( + ClientOptions().apply { + key = "{{API_KEY}}" + clientId = "" + }, +) + +val chatClient = ChatClient(realtimeClient) +``` + +A "@ClientOptions@":/docs/api/realtime-sdk#client-options object may be passed to the Pub/Sub SDK instance to further customize the connection, however at a minimum you must set an API key and provide a @clientId@ to ensure that the client is "identified":/docs/auth/identified-clients. + +In many cases, a users unique application-specific identifier may be used as the `clientId` to provide consistent identification for clients across your application. + +Additional options can also be passed to the Chat client to customize the following properties: + +blang[javascript,react]. +|_. Property |_. Description | +| logHandler | The function to call for each line of "log output":#logging. The default is @console.log@. | +| logLevel | The verbosity of the "log output":#logging. Options are; @trace@, @debug@, @info@, @warn@, @error@ or @silent@. The default is @error@. | + +blang[swift]. +|_. Property |_. Description | +| logHandler | This is your own custom log handler conforming to the @LogHandler@ protocol. A single @log@ function is called for each line of "log output":#logging. The default implementation uses Swift's @Logger@. | +| logLevel | The verbosity of the "log output":#logging. Options are; @.trace@, @.debug@, @.info@, @.warn@, @.error@ or @.silent@. The default is @.error@. | + +blang[kotlin]. +|_. Property |_. Description | +| logHandler | This is your own custom log handler conforming to the @LogHandler@ interface. A single @log@ function is called for each line of "log output":#logging. The default implementation uses Android's @Log@. | +| logLevel | The verbosity of the "log output":#logging. Options are; @Trace@, @Debug@, @Info@, @Warn@, @Error@ or @Silent@. The default is @Error@. | + +h2(#logging). Logging + +Set the @logHandler@ and @logLevel@ properties when "instantiating a client":#instantiate to configure your log handler: + +```[javascript] +const ably = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); +const chatClient = new ChatClient(ably, {logHandler: logWriteFunc, logLevel: 'debug' }); +``` + +```[react] +import * as Ably from 'ably'; +import { ChatClientProvider, LogLevel } from '@ably/chat'; + +const ably = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); +const chatClient = new ChatClient(ably, {logHandler: logWriteFunc, logLevel: 'debug' }); + +const App = => { + return ( + + + + ); +}; +``` + +```[swift] +let realtimeOptions = ARTClientOptions() +realtimeOptions.key = "{{API_KEY}}" +realtimeOptions.clientId = "" +let realtime = ARTRealtime(options: realtimeOptions) +let clientOptions = ClientOptions(logHandler: SomeLogHandler(), logLevel: .debug) +return DefaultChatClient(realtime: realtime, clientOptions: clientOptions) +``` + +```[kotlin] +val realtimeClient = AblyRealtime( + ClientOptions().apply { + key = "{{API_KEY}}" + clientId = "" + }, +) +val chatOptions = com.ably.chat.ClientOptions( + logHandler = CustomLogHandler(), // Implements com.ably.chat.LogHandler interface + logLevel = LogLevel.Debug, +) +val chatClient = ChatClient(realtimeClient, chatOptions) +``` + +blang[javascript,react]. +The @logHandler@ property is your own function that will be called for each line of log output generated by the Chat SDK. + +blang[swift,kotlin]. +The @logHandler@ property is your custom @LogHandler@ implementation that will be called for each line of log output generated by the Chat SDK. + +The @logLevel@ sets the verbosity of logs that will be output by the SDK. The following log levels are available to set: + +|_. Level |_. Description | +| trace | Something routine and expected has occurred. This level will provide logs for the vast majority of operations and function calls. | +| debug | Development information, messages that are useful when trying to debug library behavior, but superfluous to normal operation. | +| info | Informational messages. Operationally significant to the library but not out of the ordinary. | +| warn | Anything that is not immediately an error, but could cause unexpected behavior in the future. For example, passing an invalid value to an option. Indicates that some action should be taken to prevent future errors. | +| error | A given operation has failed and cannot be automatically recovered. The error may threaten the continuity of operation. | +| silent | No logging will be performed. | diff --git a/content/chat/setup.textile b/content/chat/setupold.textile similarity index 100% rename from content/chat/setup.textile rename to content/chat/setupold.textile diff --git a/data/createPages/index.ts b/data/createPages/index.ts index b20f250c24..3e23442f2e 100644 --- a/data/createPages/index.ts +++ b/data/createPages/index.ts @@ -70,6 +70,25 @@ interface ApiReferenceQueryResult { }; } +interface MdxEdge { + node: { + id: string; + frontmatter: { + slug: string; + title: string; + }; + internal: { + contentFilePath: string; + }; + }; +} + +interface MdxQueryResult { + allMdx: { + edges: MdxEdge[]; + }; +} + export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: { createPage, createRedirect } }) => { /** * It's not ideal to have: @@ -104,6 +123,26 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: } } `); + + const mdxResult = await graphql(` + query { + allMdx { + edges { + node { + id + frontmatter { + slug + title + } + internal { + contentFilePath + } + } + } + } + } + `); + /** * We could log here, the reason we don't right now is that the error should already have been caught and logged. * Because Gatsby spawns a bunch of async processes during the onCreateNode step, though, and errors don't prevent @@ -207,6 +246,30 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: return slug; }; + const mdxCreator = async (edge: MdxEdge): Promise => { + const slug = edge.node.frontmatter.slug; + + if (!slug) { + return slug; + } + + const mdxWrapper = path.resolve('src/components/Layout/MDXWrapper.tsx'); + + console.log('cat', edge.node.frontmatter); + + createPage({ + path: slug, + component: `${mdxWrapper}?__contentFilePath=${edge.node.internal.contentFilePath}`, + context: { + slug, + title: edge.node.frontmatter.title, + layout: { sidebar: true, searchBar: true, template: 'base' }, + }, + }); + + return slug; + }; + createRedirect({ fromPath: '/', toPath: '/docs', @@ -230,8 +293,11 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: throw new Error('API reference result is undefined'); } + console.log('lucie', mdxResult.data); + await Promise.all([ ...documentResult.data.allFileHtml.edges.map(documentCreator(documentTemplate)), ...apiReferenceResult.data.allFileHtml.edges.map(documentCreator(apiReferenceTemplate)), + ...(mdxResult.data?.allMdx.edges.map(mdxCreator) ?? []), ]); }; diff --git a/gatsby-config.ts b/gatsby-config.ts index 3fc16a2060..19abdb7e77 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -50,7 +50,16 @@ export const plugins = [ HowTos: `${__dirname}/how-tos`, }, }, - 'gatsby-plugin-mdx', + { + resolve: 'gatsby-plugin-mdx', + }, + { + resolve: `gatsby-source-filesystem`, + options: { + name: `pages`, + path: `${__dirname}/src/pages`, + }, + }, // Images { resolve: 'gatsby-source-filesystem', diff --git a/gatsby-ssr.tsx b/gatsby-ssr.tsx index 8da52b1e28..50d152a36b 100644 --- a/gatsby-ssr.tsx +++ b/gatsby-ssr.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { getSandpackCssText } from '@codesandbox/sandpack-react'; - const onRenderBody = ({ setHeadComponents }: { setHeadComponents: (components: React.ReactNode[]) => void }) => { const inlineScripts: React.ReactNode[] = []; diff --git a/package.json b/package.json index 8a63fa4123..1855640657 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "gatsby-plugin-image": "^3.3.0", "gatsby-plugin-layout": "^4.14.0", "gatsby-plugin-manifest": "^5.3.0", - "gatsby-plugin-mdx": "^5.12.0", + "gatsby-plugin-mdx": "^5.14.0", "gatsby-plugin-react-helmet": "^6.3.0", "gatsby-plugin-root-import": "^2.0.9", "gatsby-plugin-sharp": "^5.8.1", diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx new file mode 100644 index 0000000000..c45ed96cb7 --- /dev/null +++ b/src/components/Layout/MDXWrapper.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { PageProps } from 'gatsby'; + +import '../../styles/global.css'; +import PageTitle from '../PageTitle'; +import { MarkdownProvider } from '../Markdown'; + +type PageContextType = { + title: string; +}; + +type MDXWrapperProps = PageProps; + +const MDXWrapper: React.FC = ({ children, pageContext }) => { + const { title } = pageContext; + + return ( + + {title && {title}} + {children} + + ); +}; + +export default MDXWrapper; diff --git a/src/components/Markdown/LanguageBlock.tsx b/src/components/Markdown/LanguageBlock.tsx new file mode 100644 index 0000000000..97a388eeb7 --- /dev/null +++ b/src/components/Markdown/LanguageBlock.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useLayoutContext } from 'src/contexts/layout-context'; + +interface LanguageBlockProps { + language: 'react' | 'javascript'; + children: React.ReactNode; +} + +export const LanguageBlock: React.FC = ({ language, children }) => { + const { + activePage: { language: currentLanguage }, + } = useLayoutContext(); + + if (currentLanguage !== language) { + return null; + } + + return <>{children}; +}; diff --git a/yarn.lock b/yarn.lock index a2d3d0c012..a1307bc693 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7418,6 +7418,13 @@ fastq@^1.15.0, fastq@^1.6.0: dependencies: reusify "^1.0.4" +fastq@^1.16.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + fb-watchman@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" @@ -7690,6 +7697,15 @@ fs-extra@^11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d" + integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -7825,6 +7841,28 @@ gatsby-core-utils@^4.13.1: tmp "^0.2.1" xdg-basedir "^4.0.0" +gatsby-core-utils@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-4.14.0.tgz#d044327b9399f0449674f8abf1037d1a3ab4e84e" + integrity sha512-h0v20gB213PmhKjioCJ93SrUb7Hihnqxd6X6Iur4u1eiWTUDsGeV9g1bkquiuDl2qovUnjj7mOoHdWiu/Ax/9Q== + dependencies: + "@babel/runtime" "^7.20.13" + ci-info "2.0.0" + configstore "^5.0.1" + fastq "^1.15.0" + file-type "^16.5.4" + fs-extra "^11.2.0" + got "^11.8.6" + hash-wasm "^4.11.0" + import-from "^4.0.0" + lmdb "2.5.3" + lock "^1.1.0" + node-object-hash "^2.3.10" + proper-lockfile "^4.1.2" + resolve-from "^5.0.0" + tmp "^0.2.1" + xdg-basedir "^4.0.0" + gatsby-graphiql-explorer@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-3.13.1.tgz#906f5b75f9c01ca5cd836de6cda60e376126e3e2" @@ -7932,10 +7970,10 @@ gatsby-plugin-manifest@^5.3.0: semver "^7.5.3" sharp "^0.32.6" -gatsby-plugin-mdx@^5.12.0: - version "5.13.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-mdx/-/gatsby-plugin-mdx-5.13.1.tgz#9fd9d7612daefd372cb9c0f0b9ba8045f490bf3d" - integrity sha512-ZL/z1j8zBzQSqFTEoVdC+jPNpN/CXse2h87wUz78V+BMjp40ccR0DCo62KgF40HVz4iCEYVufqfjQNc0nLoSow== +gatsby-plugin-mdx@^5.14.0: + version "5.14.0" + resolved "https://registry.yarnpkg.com/gatsby-plugin-mdx/-/gatsby-plugin-mdx-5.14.0.tgz#5f95124cae148bd5e91559198d983c562a88b0a4" + integrity sha512-tJJmR+sRrWFRuUKUq5bJLWcLXo6FTP06tfE+7yN2foKcW6+SphY3EVvqiTnCzNAkpvUCP3EASLMg2WGWIVzi4A== dependencies: "@mdx-js/mdx" "^2.3.0" acorn "^8.8.2" @@ -7943,9 +7981,9 @@ gatsby-plugin-mdx@^5.12.0: astring "^1.8.5" deepmerge "^4.3.1" estree-util-build-jsx "^2.2.2" - fs-extra "^11.1.1" - gatsby-core-utils "^4.13.1" - gatsby-plugin-utils "^4.13.1" + fs-extra "^11.2.0" + gatsby-core-utils "^4.14.0" + gatsby-plugin-utils "^4.14.0" gray-matter "^4.0.3" mdast-util-mdx "^2.0.1" mdast-util-to-hast "^10.2.0" @@ -8051,6 +8089,21 @@ gatsby-plugin-utils@^4.13.1: joi "^17.9.2" mime "^3.0.0" +gatsby-plugin-utils@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/gatsby-plugin-utils/-/gatsby-plugin-utils-4.14.0.tgz#5ba6f5a92ba3a79d5b66ab59c8243d4504418782" + integrity sha512-w7EZ0C7JA9sG3JiBS2ffGsrZplAbtNk0Junb3UeUFj66CY0MU8UV0rZIzBkz+EMbQvPkxvVJNQu4/tA9ohCvfA== + dependencies: + "@babel/runtime" "^7.20.13" + fastq "^1.16.0" + fs-extra "^11.2.0" + gatsby-core-utils "^4.14.0" + gatsby-sharp "^1.14.0" + graphql-compose "^9.0.10" + import-from "^4.0.0" + joi "^17.11.0" + mime "^3.0.0" + gatsby-react-router-scroll@^6.13.1: version "6.13.1" resolved "https://registry.yarnpkg.com/gatsby-react-router-scroll/-/gatsby-react-router-scroll-6.13.1.tgz#b20d492a1fc0ac4f31a0435452b06e22e8cb6c41" @@ -8071,6 +8124,13 @@ gatsby-sharp@^1.13.0: dependencies: sharp "^0.32.6" +gatsby-sharp@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/gatsby-sharp/-/gatsby-sharp-1.14.0.tgz#85ee5b60999d68c508199b74f445a1d369e84254" + integrity sha512-2lZg8NEg5M8jzkMYZouf0I5e1TVpwjtEiKg48R4dGOhYqDKGfENVJWRnvYtw12zNfgBgQ/gUryG7Zj7qMLVANA== + dependencies: + sharp "^0.32.6" + gatsby-source-filesystem@^5.12.0: version "5.13.1" resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-5.13.1.tgz#1aad8f969fbfd4efe6f7cfcd8542253f54fdbd8a" @@ -8701,6 +8761,11 @@ has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== +hash-wasm@^4.11.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.12.0.tgz#f9f1a9f9121e027a9acbf6db5d59452ace1ef9bb" + integrity sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ== + hash-wasm@^4.9.0: version "4.11.0" resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.11.0.tgz#7d1479b114c82e48498fdb1d2462a687d00386d5" @@ -10231,6 +10296,17 @@ jiti@^1.20.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +joi@^17.11.0: + version "17.13.3" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" + integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== + dependencies: + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.5" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + joi@^17.9.2: version "17.12.2" resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.2.tgz#283a664dabb80c7e52943c557aab82faea09f521" From a953b41a411a83932ba70e338a21fce8e14a1910 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Fri, 11 Apr 2025 12:09:55 +0100 Subject: [PATCH 02/14] wrap in article and add copy links --- gatsby-config.ts | 7 +++++++ package.json | 1 + src/components/Layout/MDXWrapper.tsx | 11 +++++++---- yarn.lock | 13 ++++++++++++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/gatsby-config.ts b/gatsby-config.ts index 19abdb7e77..08f271ee28 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -52,6 +52,13 @@ export const plugins = [ }, { resolve: 'gatsby-plugin-mdx', + options: { + gatsbyRemarkPlugins: [ + { + resolve: `gatsby-remark-autolink-headers`, + }, + ], + }, }, { resolve: `gatsby-source-filesystem`, diff --git a/package.json b/package.json index 1855640657..b9eef13bff 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "gatsby-plugin-root-import": "^2.0.9", "gatsby-plugin-sharp": "^5.8.1", "gatsby-plugin-sitemap": "^6.12.1", + "gatsby-remark-autolink-headers": "^6.14.0", "gatsby-source-filesystem": "^5.12.0", "gatsby-transformer-remark": "^6.12.0", "gatsby-transformer-sharp": "^5.3.0", diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index c45ed96cb7..7a4bde6403 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -4,6 +4,7 @@ import { PageProps } from 'gatsby'; import '../../styles/global.css'; import PageTitle from '../PageTitle'; import { MarkdownProvider } from '../Markdown'; +import Article from '../Article'; type PageContextType = { title: string; @@ -15,10 +16,12 @@ const MDXWrapper: React.FC = ({ children, pageContext }) => { const { title } = pageContext; return ( - - {title && {title}} - {children} - +
+ + {title && {title}} + {children} + +
); }; diff --git a/yarn.lock b/yarn.lock index a1307bc693..6852a4c734 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8112,6 +8112,17 @@ gatsby-react-router-scroll@^6.13.1: "@babel/runtime" "^7.20.13" prop-types "^15.8.1" +gatsby-remark-autolink-headers@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/gatsby-remark-autolink-headers/-/gatsby-remark-autolink-headers-6.14.0.tgz#7a37104b30c55eb666fbd4824294d90b22dc3743" + integrity sha512-hQkhnj1mMLR+Nd3VjCvgJjGGnrNZcNh+dnFyi9qv5nKtbo24yf2vS99v5c+0KLw9d90kSmdpiEkvA+Bhbh9Kzg== + dependencies: + "@babel/runtime" "^7.20.13" + github-slugger "^1.5.0" + lodash "^4.17.21" + mdast-util-to-string "^2.0.0" + unist-util-visit "^2.0.3" + gatsby-script@^2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/gatsby-script/-/gatsby-script-2.13.0.tgz#0fa7316739ebc31da217091e54db8ef86f41dbb3" @@ -8499,7 +8510,7 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== -github-slugger@^1.2.1: +github-slugger@^1.2.1, github-slugger@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== From b05b329876a54ba35fe5957569133a60f9e50277 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Wed, 16 Apr 2025 18:09:30 +0100 Subject: [PATCH 03/14] mdx fixup --- .../{connectold.textile => connect.textile} | 0 .../{messages.textile => messagesold.textile} | 0 content/chat/setup.mdx | 262 -------- .../chat/{setupold.textile => setup.textile} | 0 data/createPages/index.ts | 65 -- data/onCreatePage.ts | 21 +- src/components/Layout/MDXWrapper.tsx | 11 +- src/components/Layout/mdx/CodeBlock.tsx | 0 src/components/Layout/mdx/Conditional.tsx | 18 + src/components/Markdown/MarkdownProvider.tsx | 11 +- {content => src/pages/docs}/chat/index.mdx | 0 src/pages/docs/chat/rooms/messages.mdx | 589 ++++++++++++++++++ 12 files changed, 636 insertions(+), 341 deletions(-) rename content/chat/{connectold.textile => connect.textile} (100%) rename content/chat/rooms/{messages.textile => messagesold.textile} (100%) delete mode 100644 content/chat/setup.mdx rename content/chat/{setupold.textile => setup.textile} (100%) create mode 100644 src/components/Layout/mdx/CodeBlock.tsx create mode 100644 src/components/Layout/mdx/Conditional.tsx rename {content => src/pages/docs}/chat/index.mdx (100%) create mode 100644 src/pages/docs/chat/rooms/messages.mdx diff --git a/content/chat/connectold.textile b/content/chat/connect.textile similarity index 100% rename from content/chat/connectold.textile rename to content/chat/connect.textile diff --git a/content/chat/rooms/messages.textile b/content/chat/rooms/messagesold.textile similarity index 100% rename from content/chat/rooms/messages.textile rename to content/chat/rooms/messagesold.textile diff --git a/content/chat/setup.mdx b/content/chat/setup.mdx deleted file mode 100644 index 55ceff296b..0000000000 --- a/content/chat/setup.mdx +++ /dev/null @@ -1,262 +0,0 @@ ---- -title: SDK setup -meta_description: 'Install, authenticate and instantiate the Chat SDK.' -product: chat -languages: - - javascript - - react - - swift - - kotlin ---- - -Use these instructions to install, authenticate and instantiate the Chat SDK. - - - -h2(#authentication). Authentication - -An API key is required to authenticate with Ably. API keys are used either to authenticate directly with Ably using basic authentication, or to generate tokens for untrusted clients using "token authentication":/docs/auth/token. - - - -"Sign up":https://ably.com/sign-up to Ably to create an API key in the dashboard or use the "Control API":/docs/account/control-api to create an API key programmatically. - -API keys and tokens have a set of "capabilities":/docs/auth/capabilities assigned to them that specify which operations, such as subscribe or publish can be performed on which resources. To use the Chat SDK, the API key requires the following capabilities depending on which features are being used: - -|_. Feature |_. Capabilities | -| Send and receive messages | Publish, Subscribe | -| Message history | Subscribe, History | -| Online status | Subscribe, Presence | -| Room occupancy | Subscribe, Channel Metadata | -| Typing indicators | Publish, Subscribe, Presence | -| Room reactions | Publish, Subscribe | - -When setting the capabilities for Chat, you need to apply them to either a wildcard resource, or a wildcard resource prefixed with the chat namespace, for example: - -- @[chat]_@ and @_@, or -- @[*]\*@ - -h2(#install). Install - -The Chat SDK is built on top of the Ably Pub/Sub SDK and uses that to establish a connection with Ably. - -blang[javascript,react]. -h3(#npm). NPM - -Install the Pub/Sub SDK and the Chat SDK: - -```[sh] -npm install @ably/chat -``` - -Import the SDKs into your project: - -```[javascript] -import * as Ably from 'ably'; -import { ChatClient } from '@ably/chat'; -``` - -```[react] -import * as Ably from 'ably'; -import { ChatClient, ChatClientProvider} from '@ably/chat'; -``` - -blang[swift,kotlin]. - -blang[javascript]. -h3(#cdn). CDN - -Reference the Pub/Sub SDK and the Chat SDK within your HTML file: - -```[html] - - - -``` - -blang[swift]. -h3(#spm). Swift Package Manager - -The SDK is distributed as a Swift Package and can be installed using Xcode or by adding it as a dependency in your package’s @Package.swift@. - -To install the @ably-chat-swift@ package in your Xcode Project: -_ Paste @https://github.com/ably/ably-chat-swift@ in the Swift Packages search box (Xcode project → Swift Packages.. . → @+@ button). -_ Select the Ably Chat SDK for your target. - -To install the @ably-chat-swift@ package in another Swift Package, add the following to your @Package.swift@: - -```[swift] -.package(url: "https://github.com/ably/ably-chat-swift", from: "0.1.0"), -``` - -Import the SDK: - -```[swift] -import AblyChat -``` - -blang[kotlin]. -h3(#gradle). Gradle - -The Ably Chat SDK is available on the Maven Central Repository. To include the dependency in your project, add the following to your @build.gradle.kts@ file: - -```[kotlin] - implementation("com.ably.chat:chat-android:0.2.0") -``` - -For groovy: - -```[kotlin] - implementation 'com.ably.chat:chat-android:0.2.0' -``` - -h2(#instantiate). Instantiate a client - -Instantiate a realtime client using the Pub/Sub SDK and pass the generated client into the Chat constructor. - -blang[react]. -Pass the @ChatClient@ into the "@ChatClientProvider@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat-react.ChatClientProvider.html. The @ChatClient@ instance will be available to all child components in your React component tree. - -blang[javascript,swift,kotlin]. - -```[javascript] -import { LogLevel } from '@ably/chat' - -const realtimeClient = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); -const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Error }); -``` - -```[react] -import { LogLevel } from '@ably/chat' - -const realtimeClient = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); -const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Error }); - -const App = () => { - return ( - - - - ); -}; -``` - -```[swift] -let realtimeOptions = ARTClientOptions() -realtimeOptions.key = "{{API_KEY}}" -realtimeOptions.clientId = "" -let realtime = ARTRealtime(options: realtimeOptions) -let chatClient = DefaultChatClient(realtime: realtime, clientOptions: nil) -``` - -```[kotlin] -import com.ably.chat.ChatClient -import io.ably.lib.realtime.AblyRealtime -import io.ably.lib.types.ClientOptions - -val realtimeClient = AblyRealtime( - ClientOptions().apply { - key = "{{API_KEY}}" - clientId = "" - }, -) - -val chatClient = ChatClient(realtimeClient) -``` - -A "@ClientOptions@":/docs/api/realtime-sdk#client-options object may be passed to the Pub/Sub SDK instance to further customize the connection, however at a minimum you must set an API key and provide a @clientId@ to ensure that the client is "identified":/docs/auth/identified-clients. - -In many cases, a users unique application-specific identifier may be used as the `clientId` to provide consistent identification for clients across your application. - -Additional options can also be passed to the Chat client to customize the following properties: - -blang[javascript,react]. -|_. Property |_. Description | -| logHandler | The function to call for each line of "log output":#logging. The default is @console.log@. | -| logLevel | The verbosity of the "log output":#logging. Options are; @trace@, @debug@, @info@, @warn@, @error@ or @silent@. The default is @error@. | - -blang[swift]. -|_. Property |_. Description | -| logHandler | This is your own custom log handler conforming to the @LogHandler@ protocol. A single @log@ function is called for each line of "log output":#logging. The default implementation uses Swift's @Logger@. | -| logLevel | The verbosity of the "log output":#logging. Options are; @.trace@, @.debug@, @.info@, @.warn@, @.error@ or @.silent@. The default is @.error@. | - -blang[kotlin]. -|_. Property |_. Description | -| logHandler | This is your own custom log handler conforming to the @LogHandler@ interface. A single @log@ function is called for each line of "log output":#logging. The default implementation uses Android's @Log@. | -| logLevel | The verbosity of the "log output":#logging. Options are; @Trace@, @Debug@, @Info@, @Warn@, @Error@ or @Silent@. The default is @Error@. | - -h2(#logging). Logging - -Set the @logHandler@ and @logLevel@ properties when "instantiating a client":#instantiate to configure your log handler: - -```[javascript] -const ably = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); -const chatClient = new ChatClient(ably, {logHandler: logWriteFunc, logLevel: 'debug' }); -``` - -```[react] -import * as Ably from 'ably'; -import { ChatClientProvider, LogLevel } from '@ably/chat'; - -const ably = new Ably.Realtime({ key: '{{API_KEY}}', clientId: ''}); -const chatClient = new ChatClient(ably, {logHandler: logWriteFunc, logLevel: 'debug' }); - -const App = => { - return ( - - - - ); -}; -``` - -```[swift] -let realtimeOptions = ARTClientOptions() -realtimeOptions.key = "{{API_KEY}}" -realtimeOptions.clientId = "" -let realtime = ARTRealtime(options: realtimeOptions) -let clientOptions = ClientOptions(logHandler: SomeLogHandler(), logLevel: .debug) -return DefaultChatClient(realtime: realtime, clientOptions: clientOptions) -``` - -```[kotlin] -val realtimeClient = AblyRealtime( - ClientOptions().apply { - key = "{{API_KEY}}" - clientId = "" - }, -) -val chatOptions = com.ably.chat.ClientOptions( - logHandler = CustomLogHandler(), // Implements com.ably.chat.LogHandler interface - logLevel = LogLevel.Debug, -) -val chatClient = ChatClient(realtimeClient, chatOptions) -``` - -blang[javascript,react]. -The @logHandler@ property is your own function that will be called for each line of log output generated by the Chat SDK. - -blang[swift,kotlin]. -The @logHandler@ property is your custom @LogHandler@ implementation that will be called for each line of log output generated by the Chat SDK. - -The @logLevel@ sets the verbosity of logs that will be output by the SDK. The following log levels are available to set: - -|_. Level |_. Description | -| trace | Something routine and expected has occurred. This level will provide logs for the vast majority of operations and function calls. | -| debug | Development information, messages that are useful when trying to debug library behavior, but superfluous to normal operation. | -| info | Informational messages. Operationally significant to the library but not out of the ordinary. | -| warn | Anything that is not immediately an error, but could cause unexpected behavior in the future. For example, passing an invalid value to an option. Indicates that some action should be taken to prevent future errors. | -| error | A given operation has failed and cannot be automatically recovered. The error may threaten the continuity of operation. | -| silent | No logging will be performed. | diff --git a/content/chat/setupold.textile b/content/chat/setup.textile similarity index 100% rename from content/chat/setupold.textile rename to content/chat/setup.textile diff --git a/data/createPages/index.ts b/data/createPages/index.ts index 3e23442f2e..3034259962 100644 --- a/data/createPages/index.ts +++ b/data/createPages/index.ts @@ -70,25 +70,6 @@ interface ApiReferenceQueryResult { }; } -interface MdxEdge { - node: { - id: string; - frontmatter: { - slug: string; - title: string; - }; - internal: { - contentFilePath: string; - }; - }; -} - -interface MdxQueryResult { - allMdx: { - edges: MdxEdge[]; - }; -} - export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: { createPage, createRedirect } }) => { /** * It's not ideal to have: @@ -124,25 +105,6 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: } `); - const mdxResult = await graphql(` - query { - allMdx { - edges { - node { - id - frontmatter { - slug - title - } - internal { - contentFilePath - } - } - } - } - } - `); - /** * We could log here, the reason we don't right now is that the error should already have been caught and logged. * Because Gatsby spawns a bunch of async processes during the onCreateNode step, though, and errors don't prevent @@ -246,30 +208,6 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: return slug; }; - const mdxCreator = async (edge: MdxEdge): Promise => { - const slug = edge.node.frontmatter.slug; - - if (!slug) { - return slug; - } - - const mdxWrapper = path.resolve('src/components/Layout/MDXWrapper.tsx'); - - console.log('cat', edge.node.frontmatter); - - createPage({ - path: slug, - component: `${mdxWrapper}?__contentFilePath=${edge.node.internal.contentFilePath}`, - context: { - slug, - title: edge.node.frontmatter.title, - layout: { sidebar: true, searchBar: true, template: 'base' }, - }, - }); - - return slug; - }; - createRedirect({ fromPath: '/', toPath: '/docs', @@ -293,11 +231,8 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions: throw new Error('API reference result is undefined'); } - console.log('lucie', mdxResult.data); - await Promise.all([ ...documentResult.data.allFileHtml.edges.map(documentCreator(documentTemplate)), ...apiReferenceResult.data.allFileHtml.edges.map(documentCreator(apiReferenceTemplate)), - ...(mdxResult.data?.allMdx.edges.map(mdxCreator) ?? []), ]); }; diff --git a/data/onCreatePage.ts b/data/onCreatePage.ts index b82aedb854..31e75fff26 100644 --- a/data/onCreatePage.ts +++ b/data/onCreatePage.ts @@ -1,7 +1,10 @@ import { GatsbyNode } from 'gatsby'; +import path from 'path'; export type LayoutOptions = { sidebar: boolean; searchBar: boolean; template: string }; +const mdxWrapper = path.resolve('src/components/Layout/MDXWrapper.tsx'); + const pageLayoutOptions: Record = { '/docs': { sidebar: false, searchBar: false, template: 'index' }, '/docs/api/control-api': { sidebar: false, searchBar: true, template: 'control-api' }, @@ -13,15 +16,17 @@ const pageLayoutOptions: Record = { export const onCreatePage: GatsbyNode['onCreatePage'] = ({ page, actions }) => { const { createPage } = actions; - const pathOptions = Object.entries(pageLayoutOptions).find(([path]) => page.path === path); + const isMDX = page.component.endsWith('.mdx'); - if (pathOptions) { - page.context = { - ...page.context, - layout: pathOptions[1], - }; - - createPage(page); + if (pathOptions || isMDX) { + createPage({ + ...page, + context: { + ...page.context, + layout: pathOptions ? pathOptions[1] : { sidebar: true, searchBar: true, template: 'base' }, + }, + component: isMDX ? `${mdxWrapper}?__contentFilePath=${page.component}` : page.component, + }); } }; diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index 7a4bde6403..8e225b4720 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -5,19 +5,24 @@ import '../../styles/global.css'; import PageTitle from '../PageTitle'; import { MarkdownProvider } from '../Markdown'; import Article from '../Article'; +import Conditional from 'src/components/Layout/mdx/conditional'; type PageContextType = { - title: string; + frontmatter: { + title: string; + }; }; type MDXWrapperProps = PageProps; const MDXWrapper: React.FC = ({ children, pageContext }) => { - const { title } = pageContext; + const { + frontmatter: { title }, + } = pageContext; return (
- + {title && {title}} {children} diff --git a/src/components/Layout/mdx/CodeBlock.tsx b/src/components/Layout/mdx/CodeBlock.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/components/Layout/mdx/Conditional.tsx b/src/components/Layout/mdx/Conditional.tsx new file mode 100644 index 0000000000..bb39af74ee --- /dev/null +++ b/src/components/Layout/mdx/Conditional.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { useLayoutContext } from 'src/contexts/layout-context'; +import { LanguageKey } from 'src/data/languages/types'; + +interface ConditionalProps { + lang: LanguageKey; + children: React.ReactNode; +} + +const Conditional: React.FC = ({ lang, children }) => { + const { activePage } = useLayoutContext(); + const { language } = activePage; + const splitLang = lang.split(','); + + return splitLang.includes(language) ? children : null; +}; + +export default Conditional; diff --git a/src/components/Markdown/MarkdownProvider.tsx b/src/components/Markdown/MarkdownProvider.tsx index add4adcc7d..855b328504 100644 --- a/src/components/Markdown/MarkdownProvider.tsx +++ b/src/components/Markdown/MarkdownProvider.tsx @@ -101,6 +101,11 @@ const components = { pre: Pre, }; -export const MarkdownProvider = ({ children }: { children: React.ReactNode }) => ( - {children} -); +export const MarkdownProvider = ({ + children, + extraComponents, +}: { + children: React.ReactNode; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + extraComponents?: Record>; +}) => {children}; diff --git a/content/chat/index.mdx b/src/pages/docs/chat/index.mdx similarity index 100% rename from content/chat/index.mdx rename to src/pages/docs/chat/index.mdx diff --git a/src/pages/docs/chat/rooms/messages.mdx b/src/pages/docs/chat/rooms/messages.mdx new file mode 100644 index 0000000000..21e64f37fe --- /dev/null +++ b/src/pages/docs/chat/rooms/messages.mdx @@ -0,0 +1,589 @@ +--- +title: Messages +meta_description: 'Send, update, delete, and receive messages in chat rooms.' +product: chat +languages: + - javascript + - react + - swift + - kotlin +--- + +Send, update, delete, and receive messages in a chat room with any number of participants. Users subscribe to messages by registering a listener, and send messages to all users that are subscribed to receive them. +A user can also update or delete a message, all users that are subscribed to the room will be notified of the changes. + +h2(#subscribe). Subscribe to messages + +blang[javascript,swift,kotlin]. +Subscribe to receive messages in a room by registering a listener. Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-360z1"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method in a room to receive all messages that are sent to it: + +blang[react]. +Subscribe to messages with the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat-react.useMessages.html hook. Supply a listener and the hook will automatically subscribe to message events sent to the room. As long as a defined value is provided, the subscription will persist across renders. If the listener value is undefined, the subscription will be removed until it becomes defined again. + + Providing a listener will also enable you to retrieve messages that have been "previously sent to the room.":/docs/chat/rooms/history + +```[javascript] +const {unsubscribe} = room.messages.subscribe((event) => { + console.log(event.message); +}); +``` + +```[react] +import { useState } from 'react'; +import { useMessages } from '@ably/chat'; + +const MyComponent = () => { + useMessages({ + listener: (event) => { + console.log('Received message: ', event.message); + }, + }); + + return
...
; +}; +``` + +```[swift] +let messagesSubscription = try await room.messages.subscribe() +for await message in messagesSubscription { + print("Message received: \(message)") +} +``` + +```[kotlin] +val subscription = room.messages.subscribe { messageEvent: MessageEvent -> + println(messageEvent.message.toString()) +} +``` + +h3(#structure). Message structure + +The following is the structure of a message: + +```[json] +{ + "serial": "01826232498871-001@abcdefghij:001", + "clientId": "basketLover014", + "roomId": "basketball-stream", + "text": "What a shot!", + "headers": {}, + "metadata": {}, + "createdAt": new Date("2024-06-12T11:37:59.988Z"), + "action": "message.create", + "version": "01826232498871-001@abcdefghij:001", + "timestamp": new Date("2024-06-12T11:37:59.988Z"), + "operation": {}, +} +``` + +The following are the properties of a message: + +|_. Property |_. Description |\_. Type | +| serial | An Ably-generated ID used to uniquely identify the message. By comparing it to others it provides a deterministic global ordering of messages. | String | +| clientId | The client identifier of the user that created the message. | String | +| roomId | The name of the room the message was created in. | String | +| text | The message contents. | String | +| headers | Optional headers for adding additional information to a message, such as the relative timestamp of a livestream video, or flagging a message as important. Do not use the headers for authoritative information. There is no server-side validation. When reading headers treat them like user input. | Object | +| metadata | Optional additional metadata about the message, such as animations, effects or links to other resources such as images. This information is not read by Ably. Do not use metadata for authoritative information. There is no server-side validation. When reading metadata treat it like user input. | Object | +| createdAt | The time the message was created. | Date | +| action | The latest action performed on this message, such as @message.create@, @message.update@ or @message.delete@. | String | +| version | An Ably-generated ID used to uniquely identify the version of the message. It provides a deterministic global ordering of message versions. The @version@ is identical to @serial@ if the action is @message.create@. | String | +| timestamp | The time the action was performed. It will be identical to @createdAt@ if the action is a @message.create@. | Date | +| operation | For updates and deletions, this provides additional details about the action. It may contain the following properties: | Object or undefined | +| | clientId: The client identifier of the user associated with the action. | String or undefined | +| | description: Optional description for the action. | String or undefined | +| | metadata: Optional additional metadata about the action. | Object or undefined | + +See "below":#global-ordering for more information on how to apply deterministic global ordering to the chat messages in your application. + +h3(#unsubscribe). Unsubscribe from messages + +blang[javascript]. +Use the @unsubscribe()@ function returned in the @subscribe()@ response to remove a chat message listener: + +blang[swift]. +You don't need to handle removing listeners, as this is done automatically by the SDK. + +blang[kotlin]. +Use the @unsubscribe()@ method on the returned subscription to remove a chat message listener: + +blang[react]. +When you unmount the component that is using the @useMessages@ hook, it will automatically handle unsubscribing any associated listeners registered to receive messages. + +blang[javascript,kotlin]. + +```[javascript] +// Initial subscription +const { unsubscribe } = room.messages.subscribe((event) => console.log(event.message)); + +// To remove the listener +unsubscribe(); +``` + +```[kotlin] +subscription.unsubscribe() +``` + +blang[react,swift]. + +blang[javascript]. +Use the "@messages.unsubscribeAll()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#unsubscribeAll method to deregister all chat message listeners in a room: + +```[javascript] +await room.messages.unsubscribeAll(); +``` + +blang[react,swift,kotlin]. + +blang[javascript]. + +{' '} + + + +blang[react,swift,kotlin]. + +h2(#send). Send a message + +blang[javascript,swift,kotlin]. +Use the "@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#send"@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/send%28params%3A%29"@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/send.html method to send a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive it: + +blang[react]. +Use the "@send()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-react.UseMessagesResponse.html#send method available from the response of the @useMessages@ hook to send a message to the room: + +```[javascript] +await room.messages.send({text: 'hello'}); +``` + +```[react] +import { useMessages } from '@ably/chat'; + +const MyComponent = () => { + const { send } = useMessages(); + + const handleMessageSend = () => { + send({ text: 'Hello, World!' }); + }; + + return ( +
+ +
+ ); +}; +``` + +```[swift] +let message = try await room.messages.send(params: .init(text: "hello")) +``` + +```[kotlin] +room.messages.send(text = "hello") +``` + +h2(#update). Update a message + + + +blang[javascript,swift,kotlin]. +Use the "@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#update"@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/update%28newmessage:description:metadata:%29"@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/update.html method to update a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive the update: + +blang[react]. +Use the "@update()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat_react.UseMessagesResponse.html#update method available from the response of the @useMessages@ hook to update a message in the room: + +```[javascript] +import { Message } from '@ably/chat'; +const message: Message +const updatedMessage = message.copy({text: "my updated text"}) +await room.messages.update(updatedMessage, { description: "Message update by user" }); +``` + +```[react] +import { Message, useMessages } from '@ably/chat'; + +const MyComponent = () => { + const { update } = useMessages(); + const [message, setMessage] = useState(); + + const handleMessageUpdate = (msg: Message) => { + update(msg.copy({ text: "my updated text" }), { description: "Message update by user" }) + .then((updatedMsg: Message) => { + console.log('Message updated:', updatedMsg); + }) + .catch((error) => { + console.error('Error updating message: ', error); + }); + }; + return ( +
+ +
+ ); +}; +``` + +```[swift] +let originalMessage: Message +let updatedMessage = try await room.messages.update( + newMessage: originalMessage.copy(text: "my updated text"), + description: "Message update by user", + metadata: nil +) +``` + +```[kotlin] +val originalMessage: Message +val updatedMessage = room.messages.update( + originalMessage.copy(text = "my updated text"), + operationDescription = "Message update by user", +) +``` + +h3(#filter-updates). Filter for updates + +blang[javascript,swift,kotlin]. +Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-8jolq"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method to receive messages in a room. To filter for updated messages, provide a listener that checks the @type@@action@@type@ property of the message event: + +blang[react]. +Use the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat_react.useMessages.html hook to subscribe to messages in a room. To filter for updated messages, provide a listener that checks the @type@ property of the message event: + +```[javascript] +import { MessageEvents } from '@ably/chat'; +const {unsubscribe} = room.messages.subscribe((event) => { + switch (event.type) { + case MessageEvents.Created: + console.log('Received message: ', event.message); + break; + case MessageEvents.Updated: + const existing = myMessageList.find(event.message); + if (existing && event.message.versionBefore(existing)) { + // We've already received a more recent update, so this one can be discarded. + return; + } + + console.log('Message updated: ', event.message); + break; + default: + break; + } +}); +``` + +```[react] +import { MessageEvents, useMessages } from '@ably/chat'; + +const MyComponent = () => { + useMessages({ + listener: (event) => { + switch (event.type) { + case MessageEvents.Created: + console.log('Received message: ', event.message); + break; + case MessageEvents.Updated: + const existing = myMessageList.find(event.message); + if (existing && event.message.versionBefore(existing)) { + // We've already received a more recent update, so this one can be discarded. + return; + } + + console.log('Message updated: ', event.message); + break; + default: + break; + } + }, + }); + + return
...
; +}; +``` + +```[swift] +let messagesList: [Message] +let messagesSubscription = try await room().messages.subscribe() +for await message in messagesSubscription { + switch message.action { + case .create: + messagesList.append(message) + case .update: + // compare versions to ensure you are only updating with a newer message + if let index = messagesList.firstIndex(where: { $0.serial == message.serial && message.version > $0.version }) { + messagesList[index] = message + } + default: + break + } +} +``` + +```[kotlin] +val myMessageList: List +val messagesSubscription = room.messages.subscribe { event -> + when (event.type) { + MessageEventType.Created -> println("Received message: ${event.message}") + MessageEventType.Updated -> myMessageList.find { + event.message.serial == it.serial && event.message.version > it.version + }?.let { println("Message updated: ${event.message}") } + else -> {} + } +} +``` + +See "below":#global-ordering for more information on how to deterministically apply ordering to update events in your application. + +h3(#update-structure). Message update structure + +The following is the structure of an updated message: + +```[json] +{ + "serial": "01726232498871-001@abcdefghij:001", + "clientId": "basketLover014", + "roomId": "basketball-stream", + "text": "What a shot! Edit: I meant to say 'What a dunk!'", + "headers": {}, + "metadata": {}, + "createdAt": new Date("2024-06-12T11:37:59.988Z")S, + "action": "message.update", + "version": "01826232498871-001@abcdefghij:001", + "timestamp": new Date("2024-11-21T15:49:25.425Z"), + "operation": { + "clientId": "basketLover014", + "description": "Message updated by client", + "metadata": {} + }, +} +``` + +The updated message response is identical to the structure of a message, with the following differences: + +|_. Property |_. Description | +| action | Set to @message.update@. | +| version | Set to the serial of the update action. | +| timestamp | Set to the time the message was updated. | +| operation | Set to the details the actioning client provided in the request. | + +h2(#delete). Delete a message + + + +blang[javascript,swift,kotlin]. +Use the "@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#delete"@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/delete%28message:params:%29"@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/delete.html method to delete a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive the deletion: + +blang[react]. +Use the "@deleteMessage()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat_react.UseMessagesResponse.html#deleteMessage method available from the response of the @useMessages@ hook to delete a message from the room: + +```[javascript] +import { Message } from '@ably/chat'; +const messageToDelete: Message +await room.messages.delete(messageToDelete, { description: 'Message deleted by user' }); +``` + +```[react] +import { Message, useMessages } from '@ably/chat'; + +const MyComponent = () => { + const { deleteMessage } = useMessages(); + const [message, setMessage] = useState(); + + const handleMessageDelete = (msg: Message) => { + deleteMessage(msg, { description: 'Message deleted by user' }) + .then((deletedMessage: Message) => { + console.log('Message deleted:', deletedMessage); + }) + .catch((error) => { + console.error('Error deleting message: ', error); + }); + }; + + return ( +
+ +
+ ); +}; +``` + +```[swift] +let messageToDelete: Message +let deletedMessage = try await room().messages.delete( + message: messageToDelete, + params: .init(description: "Message deleted by user") +) +``` + +```[kotlin] +val messageToDelete: Message +val deletedMessage = room().messages.delete( + messageToDelete, + operationDescription = "Message deleted by user", +) +``` + +h3(#filter-deletes). Filter for deletes + +blang[javascript,swift,kotlin]. +Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-8jolq"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method to receive messages in a room. To filter for deleted messages, provide a listener that checks the @type@@action@@type@ property of the message event: + +blang[react]. +Use the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat_react.useMessages.html hook to subscribe to messages in a room. To filter for deleted messages, provide a listener that checks the @type@ property of the message event: + +```[javascript] +import { MessageEvents } from '@ably/chat'; + +const {unsubscribe} = room.messages.subscribe((event) => { + switch (event.type) { + case MessageEvents.Created: + console.log('Received message: ', event.message); + break; + case MessageEvents.Deleted: + const existing = myMessageList.find(event.message); + if (existing && event.message.versionBefore(existing)) { + // We've already received a more recent update, so this one can be discarded. + return; + } + + console.log('Message deleted: ', event.message); + break; + default: + break; + } +}); +``` + +```[react] +import { MessageEvents, useMessages } from '@ably/chat'; + +const MyComponent = () => { + useMessages({ + listener: (event) => { + switch (event.type) { + case MessageEvents.Created: + console.log('Received message: ', event.message); + break; + case MessageEvents.Deleted: + const existing = myMessageList.find(event.message); + if (existing && event.message.versionBefore(existing)) { + // We've already received a more recent update, so this one can be discarded. + return; + } + + console.log('Message deleted: ', event.message); + break; + default: + break; + } + }, + }); + + return
...
; +}; +``` + +```[swift] +let messagesList: [Message] +let messagesSubscription = try await room().messages.subscribe() +for await message in messagesSubscription { + switch message.action { + case .create: + messagesList.append(message) + case .delete: + // version check ensures the message you are deleting is older + if let index = messagesList.firstIndex(where: { $0.serial == message.serial && message.version > $0.version }) { + messagesList.remove(at: index) + } + default: + break + } +} +``` + +```[kotlin] +val myMessageList: List +val messagesSubscription = room.messages.subscribe { event -> + when (event.type) { + MessageEventType.Created -> println("Received message: ${event.message}") + MessageEventType.Deleted -> myMessageList.find { + event.message.serial == it.serial && event.message.version > it.version + }?.let { println("Message deleted: ${event.message}") } + else -> {} + } +} +``` + +See "below":#global-ordering for more information on how to deterministically apply ordering to delete events in your application. + +h3(#deletion-structure). Message deletion structure + +The following is the structure of a deleted message: + +```[json] +{ + "serial": "01726232498871-001@abcdefghij:001", + "clientId": "basketLover014", + "roomId": "basketball-stream", + "text": "What a shot!", + "headers": {}, + "metadata": {}, + "createdAt": new Date("2024-06-12T11:37:59.988Z"), + "action": "message.delete", + "version": "01826232498871-001@abcdefghij:001", + "timestamp": new Date("2024-11-21T15:49:25.425Z"), + "operation": { + "clientId": "basketLover014", + "description": "Message deleted by client", + "metadata": {} + }, +} +``` + +The deleted message response is identical to the structure of a message, with the following differences: + +|_. Property |_. Description | +| action | Set to @message.delete@. | +| version | Set to the serial of the deletion action. | +| timestamp | Set to the time the message was deleted. | +| operation | Set to the details the actioning client provided in the request. | + +h2(#global-ordering). Ordering chat message events + +Chat messages and update events are delivered in realtime to clients connected to a particular region in the order in which that region receives them. The order in which a given region receives these events may be different from the "global" order of events, i.e. the true time-based order in which events happened. + +Chat messages are uniquely identified by their @serial@ and may have multiple @versions@ as a result of edit and delete operations. Both @serial@ and @version@ are lexicographically sortable strings. This means they can be used to enforce a deterministic global ordering based on string comparison. + +h3(#ordering-new). Ordering New Messages + +If the @serial@ of one message occurs before another when lexicographically sorted, the first message is considered to have occurred before the other. If the @serial@ values are identical, the messages are the same message. + +The @Message@ object also has convenience methods "@before@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#before, "@after@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#after and "@equal@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#equal which provide the same comparison. + +h3(#ordering-update-delete). Ordering Updates and Deletes + +Applying an action to a message produces a new version, which is uniquely identified by the @version@ property. When two message instances share the same @serial@ they represent the same chat message, but they can represent different versions. Lexicographically sorting the two message instances by the @version@ property gives the global order of the message versions: the message instance with a greater @version@ is newer, the message instance with a lower @version@ is older, and if their @version@ is equal then they are the same version. + +The @Message@ object also has convenience methods "@isOlderVersionOf@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#isolderversionof, "@isNewerVersionOf@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#isnewerversionof and "@isSameVersionAs@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#issameversionas which provide the same comparison. + +Update and Delete events provide the full message payload, so may be used to replace the entire earlier version of the message. From 765d536a791cda14dfb8f19fefa301607d926939 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Tue, 22 Apr 2025 12:21:07 +0100 Subject: [PATCH 04/14] temp codeblock --- src/components/Layout/MDXWrapper.tsx | 5 +++-- src/components/Layout/mdx/CodeBlock.tsx | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index 8e225b4720..d9b6878b06 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -5,7 +5,8 @@ import '../../styles/global.css'; import PageTitle from '../PageTitle'; import { MarkdownProvider } from '../Markdown'; import Article from '../Article'; -import Conditional from 'src/components/Layout/mdx/conditional'; +import Conditional from './mdx/Conditional'; +import CodeBlock from './mdx/CodeBlock'; type PageContextType = { frontmatter: { @@ -22,7 +23,7 @@ const MDXWrapper: React.FC = ({ children, pageContext }) => { return (
- + {title && {title}} {children} diff --git a/src/components/Layout/mdx/CodeBlock.tsx b/src/components/Layout/mdx/CodeBlock.tsx index e69de29bb2..0060fc1c29 100644 --- a/src/components/Layout/mdx/CodeBlock.tsx +++ b/src/components/Layout/mdx/CodeBlock.tsx @@ -0,0 +1,11 @@ +import React, { ReactNode } from 'react'; + +interface CodeBlockProps { + children: ReactNode; +} + +const CodeBlock: React.FC = ({ children }) => { + return
{children}
; +}; + +export default CodeBlock; From 045ef0a5a0010c2ee2c89d2f6b8425a825961773 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Tue, 22 Apr 2025 15:59:01 +0100 Subject: [PATCH 05/14] temp: conditional to if --- src/components/Layout/MDXWrapper.tsx | 4 ++-- src/components/Layout/mdx/{Conditional.tsx => If.tsx} | 8 +++++--- src/components/Markdown/MarkdownProvider.tsx | 8 ++++---- src/pages/docs/chat/index.mdx | 8 ++++++++ 4 files changed, 19 insertions(+), 9 deletions(-) rename src/components/Layout/mdx/{Conditional.tsx => If.tsx} (68%) diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index d9b6878b06..c9e1c2da2f 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -5,7 +5,7 @@ import '../../styles/global.css'; import PageTitle from '../PageTitle'; import { MarkdownProvider } from '../Markdown'; import Article from '../Article'; -import Conditional from './mdx/Conditional'; +import If from './mdx/If'; import CodeBlock from './mdx/CodeBlock'; type PageContextType = { @@ -23,7 +23,7 @@ const MDXWrapper: React.FC = ({ children, pageContext }) => { return (
- + {title && {title}} {children} diff --git a/src/components/Layout/mdx/Conditional.tsx b/src/components/Layout/mdx/If.tsx similarity index 68% rename from src/components/Layout/mdx/Conditional.tsx rename to src/components/Layout/mdx/If.tsx index bb39af74ee..ea71f21b62 100644 --- a/src/components/Layout/mdx/Conditional.tsx +++ b/src/components/Layout/mdx/If.tsx @@ -2,12 +2,14 @@ import React from 'react'; import { useLayoutContext } from 'src/contexts/layout-context'; import { LanguageKey } from 'src/data/languages/types'; -interface ConditionalProps { +interface IfProps { lang: LanguageKey; + className?: string; children: React.ReactNode; + as?: React.ElementType; } -const Conditional: React.FC = ({ lang, children }) => { +const If: React.FC = ({ lang, children, className, as: Component = 'div' }) => { const { activePage } = useLayoutContext(); const { language } = activePage; const splitLang = lang.split(','); @@ -15,4 +17,4 @@ const Conditional: React.FC = ({ lang, children }) => { return splitLang.includes(language) ? children : null; }; -export default Conditional; +export default If; diff --git a/src/components/Markdown/MarkdownProvider.tsx b/src/components/Markdown/MarkdownProvider.tsx index 855b328504..40197d794e 100644 --- a/src/components/Markdown/MarkdownProvider.tsx +++ b/src/components/Markdown/MarkdownProvider.tsx @@ -89,7 +89,7 @@ const Pre: FC = ({ children }) => { ); }; -const components = { +const defaultComponents = { h1: H1, h2: H2, h3: H3, @@ -103,9 +103,9 @@ const components = { export const MarkdownProvider = ({ children, - extraComponents, + components, }: { children: React.ReactNode; // eslint-disable-next-line @typescript-eslint/no-explicit-any - extraComponents?: Record>; -}) => {children}; + components: Record>; +}) => {children}; diff --git a/src/pages/docs/chat/index.mdx b/src/pages/docs/chat/index.mdx index 955b936ce3..ff0f8342d9 100644 --- a/src/pages/docs/chat/index.mdx +++ b/src/pages/docs/chat/index.mdx @@ -18,6 +18,14 @@ Ably Chat provides the following key features: - [Typing indicators](#typing) - [Room reactions](#reactions) + + start + hello matey + hello pal + hello sir + end + + ### Rooms and messages [Rooms](/docs/chat/rooms) are used to organize and separate your users and chat messages into 'chat rooms'. They are the entry object into chat and provide access to all other chat features, such as messages, online status, and typing indicators. From e4715da2fd9f7d277545f5bb82f3cfa6c81d1205 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Mon, 28 Apr 2025 14:38:24 +0100 Subject: [PATCH 06/14] dev package --- package.json | 2 +- yarn.lock | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 299 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b9eef13bff..8235da3b89 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "no-githooks": "git config --unset core.hooksPath" }, "dependencies": { - "@ably/ui": "16.1.1", + "@ably/ui": "16.2.0-dev.9ce2c4c9", "@codesandbox/sandpack-react": "^2.9.0", "@mdx-js/react": "^2.3.0", "@react-hook/media-query": "^1.1.1", diff --git a/yarn.lock b/yarn.lock index 6852a4c734..db01a47f27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,13 +7,14 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@ably/ui@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@ably/ui/-/ui-16.1.1.tgz#bddfe9bb4df2ea06e981fa2901cda18cac63ca4b" - integrity sha512-GrsR/SDmeNg3HbRB3ThefIA2hzuiBvOT+1Xlq5JoIsxm+OKWEioKQuxu7aYtgpPgFP/9fOf2G97Jqnl7CaQKzg== +"@ably/ui@16.2.0-dev.9ce2c4c9": + version "16.2.0-dev.9ce2c4c9" + resolved "https://registry.yarnpkg.com/@ably/ui/-/ui-16.2.0-dev.9ce2c4c9.tgz#840d0c45a8f8be7562c00888a45d6752aa771e2e" + integrity sha512-yt5aoYZLUoXM66MS//NVPkrzAQMS6bnVI4Pzqjgegrie3RKPGscDS+Cq10hCsMgSG3P5qBS2GDLy7VCZtTLa6w== dependencies: "@radix-ui/react-accordion" "^1.2.1" "@radix-ui/react-navigation-menu" "^1.2.4" + "@radix-ui/react-select" "^2.2.2" "@radix-ui/react-switch" "^1.1.1" "@radix-ui/react-tabs" "^1.1.1" addsearch-js-client "^1.0.2" @@ -1558,6 +1559,21 @@ dependencies: "@floating-ui/utils" "^0.2.1" +"@floating-ui/core@^1.6.0": + version "1.6.9" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.9.tgz#64d1da251433019dafa091de9b2886ff35ec14e6" + integrity sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw== + dependencies: + "@floating-ui/utils" "^0.2.9" + +"@floating-ui/dom@^1.0.0": + version "1.6.13" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.13.tgz#a8a938532aea27a95121ec16e667a7cbe8c59e34" + integrity sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.9" + "@floating-ui/dom@^1.0.1": version "1.6.3" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" @@ -1566,11 +1582,23 @@ "@floating-ui/core" "^1.0.0" "@floating-ui/utils" "^0.2.0" +"@floating-ui/react-dom@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31" + integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== + dependencies: + "@floating-ui/dom" "^1.0.0" + "@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== +"@floating-ui/utils@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429" + integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2794,6 +2822,11 @@ "@pnpm/network.ca-file" "^1.0.1" config-chain "^1.1.11" +"@radix-ui/number@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.1.tgz#7b2c9225fbf1b126539551f5985769d0048d9090" + integrity sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g== + "@radix-ui/primitive@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" @@ -2804,6 +2837,11 @@ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA== +"@radix-ui/primitive@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.2.tgz#83f415c4425f21e3d27914c12b3272a32e3dae65" + integrity sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA== + "@radix-ui/react-accordion@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.1.tgz#5c942c42c24267376b26204ec6847b17d15659b3" @@ -2819,6 +2857,13 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-controllable-state" "1.1.0" +"@radix-ui/react-arrow@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz#08e263c692b3a56a3f1c4bdc8405b7f73f070963" + integrity sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw== + dependencies: + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-collapsible@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz#1382cc9ec48f8b473c14f3779d317f0cdf6da5e9" @@ -2853,6 +2898,16 @@ "@radix-ui/react-primitive" "2.0.2" "@radix-ui/react-slot" "1.1.2" +"@radix-ui/react-collection@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.4.tgz#45fb4215ca26a84bd61b9b1337105e4d4e01b686" + integrity sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-slot" "1.2.0" + "@radix-ui/react-compose-refs@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" @@ -2863,6 +2918,11 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== +"@radix-ui/react-compose-refs@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" + integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== + "@radix-ui/react-context@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" @@ -2873,11 +2933,21 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== +"@radix-ui/react-context@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36" + integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA== + "@radix-ui/react-direction@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== +"@radix-ui/react-direction@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14" + integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw== + "@radix-ui/react-dismissable-layer@1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz#96dde2be078c694a621e55e047406c58cd5fe774" @@ -2889,6 +2959,31 @@ "@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-escape-keydown" "1.1.0" +"@radix-ui/react-dismissable-layer@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz#80b5c23a0d29cfe56850399210c603376c27091f" + integrity sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-escape-keydown" "1.1.1" + +"@radix-ui/react-focus-guards@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz#4ec9a7e50925f7fb661394460045b46212a33bed" + integrity sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA== + +"@radix-ui/react-focus-scope@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz#dbe9ed31b36ff9aadadf4b59aa733a4e91799d15" + integrity sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-id@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" @@ -2896,6 +2991,13 @@ dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-id@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7" + integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-navigation-menu@^1.2.4": version "1.2.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.5.tgz#c882e2067f1101a9a5f18e05b73b68b2e158a272" @@ -2916,6 +3018,30 @@ "@radix-ui/react-use-previous" "1.1.0" "@radix-ui/react-visually-hidden" "1.1.2" +"@radix-ui/react-popper@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.4.tgz#8fd6d954fca9e5d1341c7d9153cc88e05d5ed84e" + integrity sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA== + dependencies: + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.1.4" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-rect" "1.1.1" + "@radix-ui/react-use-size" "1.1.1" + "@radix-ui/rect" "1.1.1" + +"@radix-ui/react-portal@1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.6.tgz#4202e1bb34afdac612e4e982eca8efd36cbc611f" + integrity sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw== + dependencies: + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-presence@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1" @@ -2946,6 +3072,13 @@ dependencies: "@radix-ui/react-slot" "1.1.2" +"@radix-ui/react-primitive@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz#9233e17a22d0010195086f8b5eb1808ebbca8437" + integrity sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw== + dependencies: + "@radix-ui/react-slot" "1.2.0" + "@radix-ui/react-roving-focus@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e" @@ -2961,6 +3094,33 @@ "@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-controllable-state" "1.1.0" +"@radix-ui/react-select@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.2.2.tgz#96759b9dcf4e80f6f39c1ad706718f1b2928ba21" + integrity sha512-HjkVHtBkuq+r3zUAZ/CvNWUGKPfuicGDbgtZgiQuFmNcV5F+Tgy24ep2nsAW2nFgvhGPJVqeBZa6KyVN0EyrBA== + dependencies: + "@radix-ui/number" "1.1.1" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collection" "1.1.4" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.7" + "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-focus-scope" "1.1.4" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-popper" "1.2.4" + "@radix-ui/react-portal" "1.1.6" + "@radix-ui/react-primitive" "2.1.0" + "@radix-ui/react-slot" "1.2.0" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-previous" "1.1.1" + "@radix-ui/react-visually-hidden" "1.2.0" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + "@radix-ui/react-slot@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" @@ -2975,6 +3135,13 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.1" +"@radix-ui/react-slot@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz#57727fc186ddb40724ccfbe294e1a351d92462ba" + integrity sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-switch@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.1.tgz#1401658c24d66a18610f18793afbaa7fedf5429a" @@ -3007,6 +3174,11 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== +"@radix-ui/react-use-callback-ref@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40" + integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg== + "@radix-ui/react-use-controllable-state@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" @@ -3014,6 +3186,21 @@ dependencies: "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-use-controllable-state@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190" + integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg== + dependencies: + "@radix-ui/react-use-effect-event" "0.0.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-use-effect-event@0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907" + integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-escape-keydown@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" @@ -3021,16 +3208,40 @@ dependencies: "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-use-escape-keydown@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29" + integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-layout-effect@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== +"@radix-ui/react-use-layout-effect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e" + integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ== + "@radix-ui/react-use-previous@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c" integrity sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og== +"@radix-ui/react-use-previous@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz#1a1ad5568973d24051ed0af687766f6c7cb9b5b5" + integrity sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ== + +"@radix-ui/react-use-rect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz#01443ca8ed071d33023c1113e5173b5ed8769152" + integrity sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w== + dependencies: + "@radix-ui/rect" "1.1.1" + "@radix-ui/react-use-size@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b" @@ -3038,6 +3249,13 @@ dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-use-size@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz#6de276ffbc389a537ffe4316f5b0f24129405b37" + integrity sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-visually-hidden@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz#8f6025507eb5d8b4b3215ebfd2c71a6632323a62" @@ -3045,6 +3263,18 @@ dependencies: "@radix-ui/react-primitive" "2.0.2" +"@radix-ui/react-visually-hidden@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz#7692a590b4789bebf7e02d73e6d1390704a97920" + integrity sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg== + dependencies: + "@radix-ui/react-primitive" "2.1.0" + +"@radix-ui/rect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb" + integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw== + "@react-hook/intersection-observer@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz#6b8fdb80d133c9c28bc8318368ecb3a1f8befc50" @@ -4328,6 +4558,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-hidden@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522" + integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A== + dependencies: + tslib "^2.0.0" + aria-query@5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" @@ -6218,6 +6455,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node-es@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" + integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== + detect-port-alt@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" @@ -8461,6 +8703,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-nonce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" + integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -13445,6 +13692,25 @@ react-refresh@^0.14.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== +react-remove-scroll-bar@^2.3.7: + version "2.3.8" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223" + integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q== + dependencies: + react-style-singleton "^2.2.2" + tslib "^2.0.0" + +react-remove-scroll@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz#df02cde56d5f2731e058531f8ffd7f9adec91ac2" + integrity sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ== + dependencies: + react-remove-scroll-bar "^2.3.7" + react-style-singleton "^2.2.3" + tslib "^2.1.0" + use-callback-ref "^1.3.3" + use-sidecar "^1.1.3" + react-select@^5.7.0: version "5.8.0" resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.0.tgz#bd5c467a4df223f079dd720be9498076a3f085b5" @@ -13482,6 +13748,14 @@ react-side-effect@^2.1.0: resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== +react-style-singleton@^2.2.2, react-style-singleton@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388" + integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ== + dependencies: + get-nonce "^1.0.0" + tslib "^2.0.0" + react-test-renderer@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" @@ -15223,6 +15497,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -15700,6 +15979,13 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-callback-ref@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" + integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg== + dependencies: + tslib "^2.0.0" + use-isomorphic-layout-effect@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" @@ -15710,6 +15996,14 @@ use-keyboard-shortcut@^1.1.6: resolved "https://registry.yarnpkg.com/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.6.tgz#71e1096fba291457307444fd6d0473ab0dce4bcd" integrity sha512-tOKjR7eKXQIfAgFMwhelpWSRTrwE1GaB8CE/47ohtcVCKxUdn7UbfNNhiigTkATpUVoPu+5tRPbHrwjIBgTYoQ== +use-sidecar@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb" + integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ== + dependencies: + detect-node-es "^1.1.0" + tslib "^2.0.0" + use-sync-external-store@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" From f8d25321e60d6f1c6774b1723ce09107bc9b92c8 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Mon, 28 Apr 2025 14:44:31 +0100 Subject: [PATCH 07/14] it renders --- src/components/Layout/MDXWrapper.tsx | 21 +- src/components/Layout/mdx/CodeBlock.tsx | 11 - src/components/Layout/mdx/If.tsx | 2 +- src/pages/docs/chat/rooms/messages.mdx | 845 ++++++++++++++---------- 4 files changed, 524 insertions(+), 355 deletions(-) delete mode 100644 src/components/Layout/mdx/CodeBlock.tsx diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index c9e1c2da2f..46a31295ef 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import { PageProps } from 'gatsby'; +import { navigate, PageProps } from 'gatsby'; +import CodeSnippet, { CodeSnippetProps } from '@ably/ui/core/CodeSnippet'; import '../../styles/global.css'; import PageTitle from '../PageTitle'; import { MarkdownProvider } from '../Markdown'; import Article from '../Article'; import If from './mdx/If'; -import CodeBlock from './mdx/CodeBlock'; +import { useLayoutContext } from 'src/contexts/layout-context'; type PageContextType = { frontmatter: { @@ -21,9 +22,23 @@ const MDXWrapper: React.FC = ({ children, pageContext }) => { frontmatter: { title }, } = pageContext; + const { activePage } = useLayoutContext(); + + const WrappedCodeSnippet: React.FC = (props) => { + return ( + { + navigate(`${location.pathname}?lang=${lang}`); + }} + {...props} + /> + ); + }; + return (
- + {title && {title}} {children} diff --git a/src/components/Layout/mdx/CodeBlock.tsx b/src/components/Layout/mdx/CodeBlock.tsx deleted file mode 100644 index 0060fc1c29..0000000000 --- a/src/components/Layout/mdx/CodeBlock.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { ReactNode } from 'react'; - -interface CodeBlockProps { - children: ReactNode; -} - -const CodeBlock: React.FC = ({ children }) => { - return
{children}
; -}; - -export default CodeBlock; diff --git a/src/components/Layout/mdx/If.tsx b/src/components/Layout/mdx/If.tsx index ea71f21b62..f373546bf9 100644 --- a/src/components/Layout/mdx/If.tsx +++ b/src/components/Layout/mdx/If.tsx @@ -9,7 +9,7 @@ interface IfProps { as?: React.ElementType; } -const If: React.FC = ({ lang, children, className, as: Component = 'div' }) => { +const If: React.FC = ({ lang, children }) => { const { activePage } = useLayoutContext(); const { language } = activePage; const splitLang = lang.split(','); diff --git a/src/pages/docs/chat/rooms/messages.mdx b/src/pages/docs/chat/rooms/messages.mdx index 21e64f37fe..608099ff25 100644 --- a/src/pages/docs/chat/rooms/messages.mdx +++ b/src/pages/docs/chat/rooms/messages.mdx @@ -2,65 +2,87 @@ title: Messages meta_description: 'Send, update, delete, and receive messages in chat rooms.' product: chat -languages: - - javascript - - react - - swift - - kotlin --- Send, update, delete, and receive messages in a chat room with any number of participants. Users subscribe to messages by registering a listener, and send messages to all users that are subscribed to receive them. A user can also update or delete a message, all users that are subscribed to the room will be notified of the changes. -h2(#subscribe). Subscribe to messages - -blang[javascript,swift,kotlin]. -Subscribe to receive messages in a room by registering a listener. Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-360z1"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method in a room to receive all messages that are sent to it: - -blang[react]. -Subscribe to messages with the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat-react.useMessages.html hook. Supply a listener and the hook will automatically subscribe to message events sent to the room. As long as a defined value is provided, the subscription will persist across renders. If the listener value is undefined, the subscription will be removed until it becomes defined again. - - Providing a listener will also enable you to retrieve messages that have been "previously sent to the room.":/docs/chat/rooms/history - -```[javascript] -const {unsubscribe} = room.messages.subscribe((event) => { +## Subscribe to messages + + + Subscribe to receive messages in a room by registering a listener. Use the{' '} + + + messages.subscribe() + + + + + messages.subscribe() + + + + + messages.subscribe() + + + method in a room to receive all messages that are sent to it: + + + + Subscribe to messages with the{' '} + + useMessages + + hook. Supply a listener and the hook will automatically subscribe to message events sent to the room. As long as a defined + value is provided, the subscription will persist across renders. If the listener value is undefined, the subscription will + be removed until it becomes defined again. + + +Providing a listener will also enable you to retrieve messages that have been [previously sent to the room.](/docs/chat/rooms/history) + + +
+{`const {unsubscribe} = room.messages.subscribe((event) => { console.log(event.message); -}); -``` +});`} +
-```[react] -import { useState } from 'react'; +
+{`import { useState } from 'react'; import { useMessages } from '@ably/chat'; const MyComponent = () => { - useMessages({ - listener: (event) => { - console.log('Received message: ', event.message); - }, - }); +useMessages({ +listener: (event) => { +console.log('Received message: ', event.message); +}, +}); - return
...
; -}; -``` +return
...
; +};`} -```[swift] -let messagesSubscription = try await room.messages.subscribe() +
+ +
+ {`let messagesSubscription = try await room.messages.subscribe() for await message in messagesSubscription { - print("Message received: \(message)") -} -``` + print("Message received: \\(message)") +}`} +
-```[kotlin] -val subscription = room.messages.subscribe { messageEvent: MessageEvent -> +
+{`val subscription = room.messages.subscribe { messageEvent: MessageEvent -> println(messageEvent.message.toString()) -} -``` +}`} +
+
-h3(#structure). Message structure +### Message structure The following is the structure of a message: -```[json] +```json { "serial": "01826232498871-001@abcdefghij:001", "clientId": "basketLover014", @@ -78,123 +100,148 @@ The following is the structure of a message: The following are the properties of a message: -|_. Property |_. Description |\_. Type | -| serial | An Ably-generated ID used to uniquely identify the message. By comparing it to others it provides a deterministic global ordering of messages. | String | -| clientId | The client identifier of the user that created the message. | String | -| roomId | The name of the room the message was created in. | String | -| text | The message contents. | String | -| headers | Optional headers for adding additional information to a message, such as the relative timestamp of a livestream video, or flagging a message as important. Do not use the headers for authoritative information. There is no server-side validation. When reading headers treat them like user input. | Object | -| metadata | Optional additional metadata about the message, such as animations, effects or links to other resources such as images. This information is not read by Ably. Do not use metadata for authoritative information. There is no server-side validation. When reading metadata treat it like user input. | Object | -| createdAt | The time the message was created. | Date | -| action | The latest action performed on this message, such as @message.create@, @message.update@ or @message.delete@. | String | -| version | An Ably-generated ID used to uniquely identify the version of the message. It provides a deterministic global ordering of message versions. The @version@ is identical to @serial@ if the action is @message.create@. | String | -| timestamp | The time the action was performed. It will be identical to @createdAt@ if the action is a @message.create@. | Date | -| operation | For updates and deletions, this provides additional details about the action. It may contain the following properties: | Object or undefined | -| | clientId: The client identifier of the user associated with the action. | String or undefined | -| | description: Optional description for the action. | String or undefined | -| | metadata: Optional additional metadata about the action. | Object or undefined | - -See "below":#global-ordering for more information on how to apply deterministic global ordering to the chat messages in your application. - -h3(#unsubscribe). Unsubscribe from messages - -blang[javascript]. -Use the @unsubscribe()@ function returned in the @subscribe()@ response to remove a chat message listener: - -blang[swift]. -You don't need to handle removing listeners, as this is done automatically by the SDK. - -blang[kotlin]. -Use the @unsubscribe()@ method on the returned subscription to remove a chat message listener: - -blang[react]. -When you unmount the component that is using the @useMessages@ hook, it will automatically handle unsubscribing any associated listeners registered to receive messages. - -blang[javascript,kotlin]. - -```[javascript] -// Initial subscription +| Property | Description | Type | +| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| serial | An Ably-generated ID used to uniquely identify the message. By comparing it to others it provides a deterministic global ordering of messages. | String | +| clientId | The client identifier of the user that created the message. | String | +| roomId | The name of the room the message was created in. | String | +| text | The message contents. | String | +| headers | Optional headers for adding additional information to a message, such as the relative timestamp of a livestream video, or flagging a message as important. Do not use the headers for authoritative information. There is no server-side validation. When reading headers treat them like user input. | Object | +| metadata | Optional additional metadata about the message, such as animations, effects or links to other resources such as images. This information is not read by Ably. Do not use metadata for authoritative information. There is no server-side validation. When reading metadata treat it like user input. | Object | +| createdAt | The time the message was created. | Date | +| action | The latest action performed on this message, such as `message.create`, `message.update` or `message.delete`. | String | +| version | An Ably-generated ID used to uniquely identify the version of the message. It provides a deterministic global ordering of message versions. The `version` is identical to `serial` if the action is `message.create`. | String | +| timestamp | The time the action was performed. It will be identical to `createdAt` if the action is a `message.create`. | Date | +| operation | For updates and deletions, this provides additional details about the action. It may contain the following properties: | Object or undefined | +| | clientId: The client identifier of the user associated with the action. | String or undefined | +| | description: Optional description for the action. | String or undefined | +| | metadata: Optional additional metadata about the action. | Object or undefined | + +See [below](#global-ordering) for more information on how to apply deterministic global ordering to the chat messages in your application. + +### Unsubscribe from messages + + + Use the `unsubscribe()` function returned in the `subscribe()` response to remove a chat message listener: + + +You don't need to handle removing listeners, as this is done automatically by the SDK. + +Use the `unsubscribe()` method on the returned subscription to remove a chat message listener: + + + When you unmount the component that is using the `useMessages` hook, it will automatically handle unsubscribing any + associated listeners registered to receive messages. + + + + +
+{`// Initial subscription const { unsubscribe } = room.messages.subscribe((event) => console.log(event.message)); // To remove the listener -unsubscribe(); -``` - -```[kotlin] -subscription.unsubscribe() -``` - -blang[react,swift]. - -blang[javascript]. -Use the "@messages.unsubscribeAll()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#unsubscribeAll method to deregister all chat message listeners in a room: - -```[javascript] -await room.messages.unsubscribeAll(); -``` +unsubscribe();`} + +
+ +
+{`subscription.unsubscribe()`} +
+
+
+ + +Use the messages.unsubscribeAll() method to deregister all chat message listeners in a room: + + +
+{`await room.messages.unsubscribeAll();`} +
+
+
+ + + + + +## Send a message + + + Use the{' '} + + + messages.send() + + + + + messages.send() + + + + + messages.send() + + + method to send a message in a chat room. All users that are [subscribed](#subscribe) to messages on that room will receive + it: + + + + Use the{' '} + + send() + + method available from the response of the `useMessages` hook to send a message to the room: + + + +
+{`await room.messages.send({text: 'hello'});`} +
+ +
+{`import { useMessages } from '@ably/chat'; -blang[react,swift,kotlin]. - -blang[javascript]. - -{' '} - - - -blang[react,swift,kotlin]. - -h2(#send). Send a message - -blang[javascript,swift,kotlin]. -Use the "@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#send"@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/send%28params%3A%29"@messages.send()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/send.html method to send a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive it: - -blang[react]. -Use the "@send()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-react.UseMessagesResponse.html#send method available from the response of the @useMessages@ hook to send a message to the room: - -```[javascript] -await room.messages.send({text: 'hello'}); -``` +const MyComponent = () => { +const { send } = useMessages(); -```[react] -import { useMessages } from '@ably/chat'; +const handleMessageSend = () => { +send({ text: 'Hello, World!' }); +}; -const MyComponent = () => { - const { send } = useMessages(); +return ( - const handleMessageSend = () => { - send({ text: 'Hello, World!' }); - }; +
+ +
+); };`} - return ( -
- -
- ); -}; -``` +
-```[swift] -let message = try await room.messages.send(params: .init(text: "hello")) -``` +
{`let message = try await room.messages.send(params: .init(text: "hello"))`}
-```[kotlin] -room.messages.send(text = "hello") -``` +
+{`room.messages.send(text = "hello")`} +
+
-h2(#update). Update a message +## Update a message -blang[javascript,swift,kotlin]. -Use the "@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#update"@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/update%28newmessage:description:metadata:%29"@messages.update()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/update.html method to update a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive the update: - -blang[react]. -Use the "@update()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat_react.UseMessagesResponse.html#update method available from the response of the @useMessages@ hook to update a message in the room: - -```[javascript] -import { Message } from '@ably/chat'; + + Use the{' '} + + + messages.update() + + + + + messages.update() + + + + + messages.update() + + + method to update a message in a chat room. All users that are [subscribed](#subscribe) to messages on that room will receive + the update: + + + + Use the{' '} + + update() + + method available from the response of the `useMessages` hook to update a message in the room: + + + +
+{`import { Message } from '@ably/chat'; const message: Message const updatedMessage = message.copy({text: "my updated text"}) -await room.messages.update(updatedMessage, { description: "Message update by user" }); -``` +await room.messages.update(updatedMessage, { description: "Message update by user" });`} +
-```[react] -import { Message, useMessages } from '@ably/chat'; +
+{`import { Message, useMessages } from '@ably/chat'; const MyComponent = () => { - const { update } = useMessages(); - const [message, setMessage] = useState(); - - const handleMessageUpdate = (msg: Message) => { - update(msg.copy({ text: "my updated text" }), { description: "Message update by user" }) - .then((updatedMsg: Message) => { - console.log('Message updated:', updatedMsg); - }) - .catch((error) => { - console.error('Error updating message: ', error); - }); - }; - return ( -
- -
- ); +const { update } = useMessages(); +const [message, setMessage] = useState(); + +const handleMessageUpdate = (msg: Message) => { +update(msg.copy({ text: "my updated text" }), { description: "Message update by user" }) +.then((updatedMsg: Message) => { +console.log('Message updated:', updatedMsg); +}) +.catch((error) => { +console.error('Error updating message: ', error); +}); }; -``` +return ( + +
+ +
+); };`} -```[swift] -let originalMessage: Message +
+ +
+ {`let originalMessage: Message let updatedMessage = try await room.messages.update( newMessage: originalMessage.copy(text: "my updated text"), description: "Message update by user", metadata: nil -) -``` +)`} +
-```[kotlin] -val originalMessage: Message +
+{`val originalMessage: Message val updatedMessage = room.messages.update( originalMessage.copy(text = "my updated text"), operationDescription = "Message update by user", -) -``` - -h3(#filter-updates). Filter for updates - -blang[javascript,swift,kotlin]. -Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-8jolq"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method to receive messages in a room. To filter for updated messages, provide a listener that checks the @type@@action@@type@ property of the message event: - -blang[react]. -Use the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat_react.useMessages.html hook to subscribe to messages in a room. To filter for updated messages, provide a listener that checks the @type@ property of the message event: - -```[javascript] -import { MessageEvents } from '@ably/chat'; +)`} +
+
+ +### Filter for updates + + + Use the{' '} + + + messages.subscribe() + + + + + messages.subscribe() + + + + + messages.subscribe() + + + method to receive messages in a room. To filter for updated messages, provide a listener that checks the + `type` + `action` + `type` property of the message event: + + + + Use the{' '} + + useMessages + + hook to subscribe to messages in a room. To filter for updated messages, provide a listener that checks the `type` property + of the message event: + + + +
+{`import { MessageEvents } from '@ably/chat'; const {unsubscribe} = room.messages.subscribe((event) => { switch (event.type) { case MessageEvents.Created: @@ -283,26 +383,28 @@ const {unsubscribe} = room.messages.subscribe((event) => { break; default: break; - } -}); -``` -```[react] -import { MessageEvents, useMessages } from '@ably/chat'; +} +});`} + +
+ +
+{`import { MessageEvents, useMessages } from '@ably/chat'; const MyComponent = () => { - useMessages({ - listener: (event) => { - switch (event.type) { - case MessageEvents.Created: - console.log('Received message: ', event.message); - break; - case MessageEvents.Updated: - const existing = myMessageList.find(event.message); - if (existing && event.message.versionBefore(existing)) { - // We've already received a more recent update, so this one can be discarded. - return; - } +useMessages({ +listener: (event) => { +switch (event.type) { +case MessageEvents.Created: +console.log('Received message: ', event.message); +break; +case MessageEvents.Updated: +const existing = myMessageList.find(event.message); +if (existing && event.message.versionBefore(existing)) { +// We've already received a more recent update, so this one can be discarded. +return; +} console.log('Message updated: ', event.message); break; @@ -310,14 +412,16 @@ const MyComponent = () => { break; } }, - }); - return
...
; -}; -``` +}); -```[swift] -let messagesList: [Message] +return
...
; +};`} + +
+ +
+ {`let messagesList: [Message] let messagesSubscription = try await room().messages.subscribe() for await message in messagesSubscription { switch message.action { @@ -331,29 +435,30 @@ for await message in messagesSubscription { default: break } -} -``` +}`} +
-```[kotlin] -val myMessageList: List +
+{`val myMessageList: List val messagesSubscription = room.messages.subscribe { event -> when (event.type) { - MessageEventType.Created -> println("Received message: ${event.message}") + MessageEventType.Created -> println("Received message: \${event.message}") MessageEventType.Updated -> myMessageList.find { event.message.serial == it.serial && event.message.version > it.version - }?.let { println("Message updated: ${event.message}") } + }?.let { println("Message updated: \${event.message}") } else -> {} } -} -``` +}`} +
+
-See "below":#global-ordering for more information on how to deterministically apply ordering to update events in your application. +See [below](#global-ordering) for more information on how to deterministically apply ordering to update events in your application. -h3(#update-structure). Message update structure +### Message update structure The following is the structure of an updated message: -```[json] +```json { "serial": "01726232498871-001@abcdefghij:001", "clientId": "basketLover014", @@ -375,13 +480,14 @@ The following is the structure of an updated message: The updated message response is identical to the structure of a message, with the following differences: -|_. Property |_. Description | -| action | Set to @message.update@. | -| version | Set to the serial of the update action. | -| timestamp | Set to the time the message was updated. | +| Property | Description | +| --------- | ---------------------------------------------------------------- | +| action | Set to `message.update`. | +| version | Set to the serial of the update action. | +| timestamp | Set to the time the message was updated. | | operation | Set to the details the actioning client provided in the request. | -h2(#delete). Delete a message +## Delete a message -blang[javascript,swift,kotlin]. -Use the "@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#delete"@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/delete%28message:params:%29"@messages.delete()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/delete.html method to delete a message in a chat room. All users that are "subscribed":#subscribe to messages on that room will receive the deletion: - -blang[react]. -Use the "@deleteMessage()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat_react.UseMessagesResponse.html#deleteMessage method available from the response of the @useMessages@ hook to delete a message from the room: - -```[javascript] -import { Message } from '@ably/chat'; + + Use the{' '} + + + messages.delete() + + + + + messages.delete() + + + + + messages.delete() + + + method to delete a message in a chat room. All users that are [subscribed](#subscribe) to messages on that room will receive + the deletion: + + + + Use the{' '} + + deleteMessage() + + method available from the response of the `useMessages` hook to delete a message from the room: + + + +
+{`import { Message } from '@ably/chat'; const messageToDelete: Message -await room.messages.delete(messageToDelete, { description: 'Message deleted by user' }); -``` +await room.messages.delete(messageToDelete, { description: 'Message deleted by user' });`} +
-```[react] -import { Message, useMessages } from '@ably/chat'; +
+{`import { Message, useMessages } from '@ably/chat'; const MyComponent = () => { - const { deleteMessage } = useMessages(); - const [message, setMessage] = useState(); - - const handleMessageDelete = (msg: Message) => { - deleteMessage(msg, { description: 'Message deleted by user' }) - .then((deletedMessage: Message) => { - console.log('Message deleted:', deletedMessage); - }) - .catch((error) => { - console.error('Error deleting message: ', error); - }); - }; - - return ( -
- -
- ); +const { deleteMessage } = useMessages(); +const [message, setMessage] = useState(); + +const handleMessageDelete = (msg: Message) => { +deleteMessage(msg, { description: 'Message deleted by user' }) +.then((deletedMessage: Message) => { +console.log('Message deleted:', deletedMessage); +}) +.catch((error) => { +console.error('Error deleting message: ', error); +}); }; -``` -```[swift] -let messageToDelete: Message +return ( + +
+ +
+); };`} + +
+ +
+ {`let messageToDelete: Message let deletedMessage = try await room().messages.delete( message: messageToDelete, params: .init(description: "Message deleted by user") -) -``` +)`} +
-```[kotlin] -val messageToDelete: Message +
+{`val messageToDelete: Message val deletedMessage = room().messages.delete( messageToDelete, operationDescription = "Message deleted by user", -) -``` - -h3(#filter-deletes). Filter for deletes - -blang[javascript,swift,kotlin]. -Use the "@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Messages.html#subscribe"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-swift/main/AblyChat/documentation/ablychat/messages/subscribe%28%29-8jolq"@messages.subscribe()@":https://sdk.ably.com/builds/ably/ably-chat-kotlin/main/dokka/chat-android/com.ably.chat/-messages/subscribe.html method to receive messages in a room. To filter for deleted messages, provide a listener that checks the @type@@action@@type@ property of the message event: - -blang[react]. -Use the "@useMessages@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/functions/chat_react.useMessages.html hook to subscribe to messages in a room. To filter for deleted messages, provide a listener that checks the @type@ property of the message event: - -```[javascript] -import { MessageEvents } from '@ably/chat'; +)`} +
+
+ +### Filter for deletes + + + Use the{' '} + + + messages.subscribe() + + + + + messages.subscribe() + + + + + messages.subscribe() + + + method to receive messages in a room. To filter for deleted messages, provide a listener that checks the + `type` + `action` + `type` property of the message event: + + + + Use the{' '} + + useMessages + + hook to subscribe to messages in a room. To filter for deleted messages, provide a listener that checks the `type` property + of the message event: + + + +
+{`import { MessageEvents } from '@ably/chat'; const {unsubscribe} = room.messages.subscribe((event) => { - switch (event.type) { - case MessageEvents.Created: - console.log('Received message: ', event.message); - break; - case MessageEvents.Deleted: - const existing = myMessageList.find(event.message); - if (existing && event.message.versionBefore(existing)) { - // We've already received a more recent update, so this one can be discarded. - return; - } +switch (event.type) { +case MessageEvents.Created: +console.log('Received message: ', event.message); +break; +case MessageEvents.Deleted: +const existing = myMessageList.find(event.message); +if (existing && event.message.versionBefore(existing)) { +// We've already received a more recent update, so this one can be discarded. +return; +} console.log('Message deleted: ', event.message); break; default: break; - } -}); -``` -```[react] -import { MessageEvents, useMessages } from '@ably/chat'; +} +});`} + +
+ +
+{`import { MessageEvents, useMessages } from '@ably/chat'; const MyComponent = () => { - useMessages({ - listener: (event) => { - switch (event.type) { - case MessageEvents.Created: - console.log('Received message: ', event.message); - break; - case MessageEvents.Deleted: - const existing = myMessageList.find(event.message); - if (existing && event.message.versionBefore(existing)) { - // We've already received a more recent update, so this one can be discarded. - return; - } +useMessages({ +listener: (event) => { +switch (event.type) { +case MessageEvents.Created: +console.log('Received message: ', event.message); +break; +case MessageEvents.Deleted: +const existing = myMessageList.find(event.message); +if (existing && event.message.versionBefore(existing)) { +// We've already received a more recent update, so this one can be discarded. +return; +} console.log('Message deleted: ', event.message); break; @@ -497,14 +658,16 @@ const MyComponent = () => { break; } }, - }); - return
...
; -}; -``` +}); + +return
...
; +};`} -```[swift] -let messagesList: [Message] +
+ +
+ {`let messagesList: [Message] let messagesSubscription = try await room().messages.subscribe() for await message in messagesSubscription { switch message.action { @@ -518,29 +681,30 @@ for await message in messagesSubscription { default: break } -} -``` +}`} +
-```[kotlin] -val myMessageList: List +
+{`val myMessageList: List val messagesSubscription = room.messages.subscribe { event -> when (event.type) { - MessageEventType.Created -> println("Received message: ${event.message}") + MessageEventType.Created -> println("Received message: \${event.message}") MessageEventType.Deleted -> myMessageList.find { event.message.serial == it.serial && event.message.version > it.version - }?.let { println("Message deleted: ${event.message}") } + }?.let { println("Message deleted: \${event.message}") } else -> {} } -} -``` +}`} +
+
-See "below":#global-ordering for more information on how to deterministically apply ordering to delete events in your application. +See [below](#global-ordering) for more information on how to deterministically apply ordering to delete events in your application. -h3(#deletion-structure). Message deletion structure +### Message deletion structure The following is the structure of a deleted message: -```[json] +```json { "serial": "01726232498871-001@abcdefghij:001", "clientId": "basketLover014", @@ -562,28 +726,29 @@ The following is the structure of a deleted message: The deleted message response is identical to the structure of a message, with the following differences: -|_. Property |_. Description | -| action | Set to @message.delete@. | -| version | Set to the serial of the deletion action. | -| timestamp | Set to the time the message was deleted. | +| Property | Description | +| --------- | ---------------------------------------------------------------- | +| action | Set to `message.delete`. | +| version | Set to the serial of the deletion action. | +| timestamp | Set to the time the message was deleted. | | operation | Set to the details the actioning client provided in the request. | -h2(#global-ordering). Ordering chat message events +## Ordering chat message events Chat messages and update events are delivered in realtime to clients connected to a particular region in the order in which that region receives them. The order in which a given region receives these events may be different from the "global" order of events, i.e. the true time-based order in which events happened. -Chat messages are uniquely identified by their @serial@ and may have multiple @versions@ as a result of edit and delete operations. Both @serial@ and @version@ are lexicographically sortable strings. This means they can be used to enforce a deterministic global ordering based on string comparison. +Chat messages are uniquely identified by their `serial` and may have multiple `versions` as a result of edit and delete operations. Both `serial` and `version` are lexicographically sortable strings. This means they can be used to enforce a deterministic global ordering based on string comparison. -h3(#ordering-new). Ordering New Messages +### Ordering New Messages -If the @serial@ of one message occurs before another when lexicographically sorted, the first message is considered to have occurred before the other. If the @serial@ values are identical, the messages are the same message. +If the `serial` of one message occurs before another when lexicographically sorted, the first message is considered to have occurred before the other. If the `serial` values are identical, the messages are the same message. -The @Message@ object also has convenience methods "@before@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#before, "@after@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#after and "@equal@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#equal which provide the same comparison. +The `Message` object also has convenience methods before, after and equal which provide the same comparison. -h3(#ordering-update-delete). Ordering Updates and Deletes +### Ordering Updates and Deletes -Applying an action to a message produces a new version, which is uniquely identified by the @version@ property. When two message instances share the same @serial@ they represent the same chat message, but they can represent different versions. Lexicographically sorting the two message instances by the @version@ property gives the global order of the message versions: the message instance with a greater @version@ is newer, the message instance with a lower @version@ is older, and if their @version@ is equal then they are the same version. +Applying an action to a message produces a new version, which is uniquely identified by the `version` property. When two message instances share the same `serial` they represent the same chat message, but they can represent different versions. Lexicographically sorting the two message instances by the `version` property gives the global order of the message versions: the message instance with a greater `version` is newer, the message instance with a lower `version` is older, and if their `version` is equal then they are the same version. -The @Message@ object also has convenience methods "@isOlderVersionOf@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#isolderversionof, "@isNewerVersionOf@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#isnewerversionof and "@isSameVersionAs@":https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/interfaces/chat-js.Message.html#issameversionas which provide the same comparison. +The `Message` object also has convenience methods isOlderVersionOf, isNewerVersionOf and isSameVersionAs which provide the same comparison. Update and Delete events provide the full message payload, so may be used to replace the entire earlier version of the message. From da2510a77cda6837e53821251ca9db01eff4617a Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Mon, 28 Apr 2025 17:28:38 +0100 Subject: [PATCH 08/14] more crap --- .prettierignore | 3 + gatsby-config.ts | 7 + package.json | 3 + src/components/Layout/MDXWrapper.tsx | 19 +- src/pages/docs/chat/rooms/messages.mdx | 155 ++------- yarn.lock | 431 ++++++++++++++++++++++++- 6 files changed, 477 insertions(+), 141 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..cdd56bd501 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +# Ignore all mdx files in src/pages - we need to be able to render components on one line for inlining +src/pages/**/*.mdx +src/pages/**/*.md diff --git a/gatsby-config.ts b/gatsby-config.ts index 08f271ee28..3fb0e300ea 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -1,4 +1,5 @@ import dotenv from 'dotenv'; +import remarkGfm from 'remark-gfm'; dotenv.config({ path: `.env.${process.env.NODE_ENV}`, @@ -58,6 +59,12 @@ export const plugins = [ resolve: `gatsby-remark-autolink-headers`, }, ], + mdxOptions: { + remarkPlugins: [ + // Add GitHub Flavored Markdown (GFM) support + remarkGfm, + ], + }, }, }, { diff --git a/package.json b/package.json index 8235da3b89..1732a78f76 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "gatsby-plugin-root-import": "^2.0.9", "gatsby-plugin-sharp": "^5.8.1", "gatsby-plugin-sitemap": "^6.12.1", + "gatsby-remark-admonitions": "^0.1.1", "gatsby-remark-autolink-headers": "^6.14.0", "gatsby-source-filesystem": "^5.12.0", "gatsby-transformer-remark": "^6.12.0", @@ -80,6 +81,8 @@ "react-helmet": "^6.1.0", "react-medium-image-zoom": "^5.1.2", "react-select": "^5.7.0", + "remark-directive": "^4.0.0", + "remark-gfm": "^1.0.0", "textile-js": "^2.1.1", "turndown": "^7.1.1", "typescript": "^4.6.3", diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index 46a31295ef..314266a223 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PropsWithChildren } from 'react'; import { navigate, PageProps } from 'gatsby'; import CodeSnippet, { CodeSnippetProps } from '@ably/ui/core/CodeSnippet'; @@ -8,6 +8,9 @@ import { MarkdownProvider } from '../Markdown'; import Article from '../Article'; import If from './mdx/If'; import { useLayoutContext } from 'src/contexts/layout-context'; +import cn from '@ably/ui/core/utils/cn'; +import Aside from '../blocks/dividers/Aside'; +import { HtmlComponentPropsData } from '../html-component-props'; type PageContextType = { frontmatter: { @@ -29,16 +32,26 @@ const MDXWrapper: React.FC = ({ children, pageContext }) => { { - navigate(`${location.pathname}?lang=${lang}`); + navigate(`${location.pathname}?lang=${lang}`, { replace: true }); }} + className={cn(props.className, 'mb-20')} {...props} /> ); }; + const WrappedAside = (props: PropsWithChildren<{ 'data-type': string }>) => { + return ( +