This repository has been archived by the owner on Oct 30, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #57 from ably-labs/context
feat!: add `<AblyProvider>` and `useAbly`
- Loading branch information
Showing
12 changed files
with
289 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,26 @@ | ||
import React from 'react'; | ||
import { createRoot } from 'react-dom/client'; | ||
import * as Ably from 'ably'; | ||
|
||
import App from './App'; | ||
import { AblyProvider } from '../../src/index'; | ||
|
||
const container = document.getElementById('root')!; | ||
|
||
function generateRandomId() { | ||
return Math.random().toString(36).substr(2, 9); | ||
} | ||
|
||
const client = new Ably.Realtime.Promise({ | ||
key: import.meta.env.VITE_ABLY_API_KEY, | ||
clientId: generateRandomId(), | ||
}); | ||
|
||
const root = createRoot(container); | ||
root.render( | ||
<React.StrictMode> | ||
<App /> | ||
<AblyProvider client={client}> | ||
<App /> | ||
</AblyProvider> | ||
</React.StrictMode> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import Ably from 'ably'; | ||
import { Types } from 'ably'; | ||
import React from 'react'; | ||
|
||
const version = '2.1.1'; | ||
|
||
const canUseSymbol = | ||
typeof Symbol === 'function' && typeof Symbol.for === 'function'; | ||
|
||
/** | ||
* Wrapper around Ably.Realtime.Promise which injects the 'react-hooks' agent | ||
*/ | ||
export class Realtime extends Ably.Realtime.Promise { | ||
constructor(options: string | Types.ClientOptions) { | ||
let opts: Types.ClientOptions; | ||
|
||
if (typeof options === 'string') { | ||
opts = { | ||
key: options, | ||
} as Types.ClientOptions; | ||
} else { | ||
opts = { ...options }; | ||
} | ||
|
||
(opts as any).agents = { 'react-hooks': version }; | ||
|
||
super(opts); | ||
} | ||
} | ||
|
||
interface AblyProviderProps { | ||
children?: React.ReactNode | React.ReactNode[] | null; | ||
client?: Ably.Types.RealtimePromise; | ||
options?: Ably.Types.ClientOptions; | ||
id?: string; | ||
} | ||
|
||
type AblyContextType = React.Context<Realtime>; | ||
|
||
// An object is appended to `React.createContext` which stores all contexts | ||
// indexed by id, which is used by useAbly to find the correct context when an | ||
// id is provided. | ||
type ContextMap = Record<string, AblyContextType>; | ||
export const contextKey = canUseSymbol | ||
? Symbol.for('__ABLY_CONTEXT__') | ||
: '__ABLY_CONTEXT__'; | ||
|
||
const ctxMap: ContextMap = | ||
typeof globalThis !== 'undefined' ? (globalThis[contextKey] = {}) : {}; | ||
|
||
export function getContext(ctxId = 'default'): AblyContextType { | ||
return ctxMap[ctxId]; | ||
} | ||
|
||
export const AblyProvider = ({ | ||
client, | ||
children, | ||
options, | ||
id = 'default', | ||
}: AblyProviderProps) => { | ||
if (!client && !options) { | ||
throw new Error('No client or options'); | ||
} | ||
|
||
if (client && options) { | ||
throw new Error('Provide client or options, not both'); | ||
} | ||
|
||
const realtime: Realtime = | ||
client || new Realtime(options as Ably.Types.ClientOptions); | ||
|
||
let context = getContext(id); | ||
if (!context) { | ||
context = ctxMap[id] = React.createContext(realtime); | ||
} | ||
|
||
// If options have been provided, the client cannot be accessed after the provider has unmounted, so close it | ||
React.useEffect(() => { | ||
if (options) { | ||
return () => { | ||
realtime.close(); | ||
}; | ||
} | ||
}, []); | ||
|
||
return ( | ||
<context.Provider value={client as Ably.Types.RealtimePromise}> | ||
{children} | ||
</context.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,8 @@ | ||
import Ably from 'ably'; | ||
import { Types } from 'ably'; | ||
|
||
export type ChannelNameAndOptions = { | ||
channelName: string; | ||
options?: Types.ChannelOptions; | ||
realtime?: Types.RealtimePromise; | ||
id?: string; | ||
}; | ||
export type ChannelParameters = string | ChannelNameAndOptions; | ||
|
||
const version = '2.1.1'; | ||
|
||
let sdkInstance: Realtime | null = null; | ||
|
||
export class Realtime extends Ably.Realtime.Promise { | ||
constructor(options: string | Types.ClientOptions) { | ||
if (typeof options === 'string') { | ||
options = { | ||
key: options, | ||
} as Types.ClientOptions; | ||
} | ||
|
||
(options as any).agents = { 'react-hooks': version }; | ||
|
||
super(options); | ||
} | ||
} | ||
|
||
export function provideSdkInstance(ablyInstance: Types.RealtimePromise) { | ||
sdkInstance = ablyInstance; | ||
} | ||
|
||
export function configureAbly( | ||
ablyConfigurationObject: string | Types.ClientOptions | ||
) { | ||
return sdkInstance || (sdkInstance = new Realtime(ablyConfigurationObject)); | ||
} | ||
|
||
export function assertConfiguration(): Types.RealtimePromise { | ||
if (!sdkInstance) { | ||
throw new Error( | ||
'Ably not configured - please call configureAbly({ key: "your-api-key", clientId: "someid" });' | ||
); | ||
} | ||
|
||
return sdkInstance; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Types } from 'ably'; | ||
import React from 'react'; | ||
import { contextKey, getContext } from '../AblyProvider'; | ||
|
||
export function useAbly(id: string = 'default'): Types.RealtimePromise { | ||
const client = React.useContext(getContext(id)) as Types.RealtimePromise; | ||
|
||
if (!client) { | ||
throw new Error( | ||
'Could not find ably client in context. ' + | ||
'Make sure your ably hooks are called inside an <AblyProvider>' | ||
); | ||
} | ||
|
||
return client; | ||
} |
Oops, something went wrong.