@harelpls/use-pusher
Advanced tools
Comparing version
/// <reference types="prop-types" /> | ||
/// <reference types="react" /> | ||
import { AuthOptions, Channel, Options, PresenceChannel } from 'pusher-js'; | ||
import Pusher, { Channel, Options, PresenceChannel } from 'pusher-js'; | ||
export interface PusherContextValues { | ||
client?: any | undefined; | ||
triggerEndpoint?: string; | ||
client?: React.MutableRefObject<Pusher | undefined>; | ||
triggerEndpoint?: string; | ||
} | ||
export interface PusherProviderProps extends Options { | ||
clientKey: string; | ||
cluster: string; | ||
authEndpoint?: string; | ||
auth?: AuthOptions; | ||
triggerEndpoint?: string; | ||
defer?: boolean; | ||
children: React.ReactNode; | ||
value?: any; | ||
clientKey: string; | ||
cluster: 'mt1' | 'us2' | 'us3' | 'eu' | 'ap1' | 'ap2' | 'ap3' | 'ap4'; | ||
triggerEndpoint?: string; | ||
defer?: boolean; | ||
value?: PusherContextValues; | ||
} | ||
export declare const __PusherContext: React.Context<PusherContextValues>; | ||
/** | ||
* Provider for the pusher service in an app | ||
* Provider that creates your pusher instance and provides it to child hooks throughout your app. | ||
* Note, you can pass in value={{}} as a prop if you'd like to override the pusher client passed. | ||
* This is handy when simulating pusher locally, or for testing. | ||
* @param props Config for Pusher client | ||
*/ | ||
export declare function PusherProvider({ clientKey, cluster, triggerEndpoint, authEndpoint, auth, defer, ...props }: PusherProviderProps): JSX.Element; | ||
export declare const PusherProvider: React.FC<PusherProviderProps>; | ||
/** | ||
* Subscribe to channel | ||
* Provides access to the pusher client instance. | ||
* | ||
* @returns a `MutableRefObject<Pusher|undefined>`. The instance is held by a `useRef()` hook. | ||
* @example | ||
* useChannel("my-channel") | ||
* ```javscript | ||
* const { client } = usePusher(); | ||
* client.current.subscribe('my-channel'); | ||
* ``` | ||
*/ | ||
export declare function useChannel(channelName: string): any; | ||
export declare function usePusher(): PusherContextValues; | ||
export declare const NOT_IN_CONTEXT_WARNING = | ||
'No Pusher context. Did you forget to wrap your app in a <PusherProvider />?'; | ||
/** | ||
* Subscribe to a channel | ||
* | ||
* @param channelName The name of the channel you want to subscribe to. | ||
* @typeparam Type of channel you're subscribing to. Can be one of Channel or PresenceChannel. | ||
* @returns Instance of the channel you just subscribed to. | ||
* | ||
* @example | ||
* ```javascript | ||
* const channel = useChannel("my-channel") | ||
* channel.bind('some-event', () => {}) | ||
* ``` | ||
*/ | ||
export declare function useChannel<T extends Channel & PresenceChannel>( | ||
channelName: string | ||
): T | undefined; | ||
/** | ||
* Subscribe to presence channel events and get members back | ||
* | ||
* @param channelName name of presence channel. Should have presence- suffix. | ||
* @param eventName name of event to bind to | ||
* @param onEvent callback to fire when event is called | ||
* @param dependencies dependencies array that onEvent uses | ||
* @param options optional argument to skip events | ||
* @param channelName name of presence the channel. Should have `presence-` suffix. | ||
* @returns Object with `channel`, `members` and `myID` properties. | ||
* | ||
* @example | ||
* const {members} = usePresenceChannel( | ||
* "my-channel", | ||
* "my-event", | ||
* (message) => console.log(message), | ||
* ) | ||
* ```javascript | ||
* const { channel, members, myID } = usePresenceChannel("presence-my-channel"); | ||
* ``` | ||
*/ | ||
export declare function usePresenceChannel(channelName: string): { | ||
channel: PresenceChannel; | ||
members: {}; | ||
myID: any; | ||
export declare function usePresenceChannel( | ||
channelName: string | ||
): { | ||
channel: PresenceChannel | undefined; | ||
members: {}; | ||
myID: any; | ||
}; | ||
@@ -59,84 +77,102 @@ /** | ||
* @param callback Callback to call on a new event | ||
* @param dependencies Dependencies the callback uses. | ||
*/ | ||
export declare function useEvent<D>(channel: Channel | PresenceChannel | undefined, eventName: string, callback: (data?: D) => void, dependencies?: unknown[] | undefined): void; | ||
export declare function useEvent<D>( | ||
channel: Channel | PresenceChannel | undefined, | ||
eventName: string, | ||
callback: (data?: D) => void, | ||
dependencies?: unknown[] | undefined | ||
): void; | ||
/** | ||
* Trigger events hook | ||
* | ||
* @param channel the channel you'd like to trigger clientEvents on. Get this from [[useChannel]] or [[usePresenceChannel]]. | ||
* @typeparam TData shape of the data you're sending with the event. | ||
* @returns A memoized trigger function that will perform client events on the channel. | ||
* @example | ||
* ```javascript | ||
* const channel = useChannel('my-channel'); | ||
* const trigger = useClientTrigger(channel) | ||
* | ||
* const trigger = useTrigger('my-channel'); | ||
* trigger('my-event', {message: 'hello'}); | ||
* const handleClick = () => trigger('some-client-event', {}); | ||
* ``` | ||
*/ | ||
export declare function useTrigger(channelName: string): (eventName: string, data?: any) => Promise<Response>; | ||
export declare function useClientTrigger(channel: Channel | PresenceChannel): (eventName: string, data?: any) => void; | ||
export declare function useClientTrigger<TData = {}>( | ||
channel: Channel | PresenceChannel | ||
): (eventName: string, data: TData) => void; | ||
/** | ||
* Provides access to the pusher client | ||
* Hook to provide a trigger function that calls the server defined in `PusherProviderProps.triggerEndpoint` using `fetch`. | ||
* Any `auth?.headers` in the config object will be passed with the `fetch` call. | ||
* | ||
* @param channelName name of channel to call trigger on | ||
* @typeparam TData shape of the data you're sending with the event | ||
* | ||
* @example | ||
* const {client} = usePusher(); | ||
* client.current.subscribe('my-channel'); | ||
* ```typescript | ||
* const trigger = useTrigger<{message: string}>('my-channel'); | ||
* trigger('my-event', {message: 'hello'}); | ||
* ``` | ||
*/ | ||
export declare function usePusher(): PusherContextValues; | ||
export declare const NOT_IN_CONTEXT_WARNING = "No Pusher context. Did you forget to wrap your app in a <PusherProvider />?"; | ||
export declare function useTrigger<TData = {}>( | ||
channelName: string | ||
): (eventName: string, data?: TData | undefined) => Promise<Response>; | ||
export declare type CallbackSignature = (data: any, metadata?: any) => void; | ||
export declare class PusherChannelMock { | ||
/** Initialize PusherChannelMock with callbacks object. */ | ||
callbacks: { | ||
[name: string]: Function[]; | ||
}; | ||
name: string; | ||
constructor(name?: string); | ||
/** | ||
* Bind callback to an event name. | ||
* @param {String} name - name of the event. | ||
* @param {Function} callback - callback to be called on event. | ||
*/ | ||
bind(name: string, callback: Function): void; | ||
/** | ||
* Unbind callback from an event name. | ||
* @param {String} name - name of the event. | ||
* @param {Function} callback - callback to be called on event. | ||
*/ | ||
unbind(name: string, callback: Function): void; | ||
/** | ||
* Emit event with data. | ||
* @param {String} name - name of the event. | ||
* @param {*} data - data you want to pass in to callback function that gets * called. | ||
*/ | ||
emit(name: string, data?: any, metadata?: any): void; | ||
trigger(): void; | ||
/** Initialize PusherChannelMock with callbacks object. */ | ||
callbacks: { | ||
[name: string]: CallbackSignature[]; | ||
}; | ||
name: string; | ||
constructor(name?: string); | ||
/** | ||
* Bind callback to an event name. | ||
* @param {String} name - name of the event. | ||
* @param {Function} callback - callback to be called on event. | ||
*/ | ||
bind(name: string, callback: CallbackSignature): void; | ||
/** | ||
* Unbind callback from an event name. | ||
* @param {String} name - name of the event. | ||
* @param {Function} callback - callback to be called on event. | ||
*/ | ||
unbind(name: string, callback: CallbackSignature): void; | ||
/** | ||
* Emit event with data. | ||
* @param {String} name - name of the event. | ||
* @param {*} data - data you want to pass in to callback function that gets * called. | ||
*/ | ||
emit(name: string, data?: any, metadata?: any): void; | ||
trigger(): void; | ||
} | ||
export declare class PusherPresenceChannelMock extends PusherChannelMock { | ||
members: any; | ||
myID: any; | ||
constructor(name?: string); | ||
members: any; | ||
myID: any; | ||
constructor(name?: string); | ||
} | ||
export declare class PusherMock { | ||
key: string; | ||
config: Options; | ||
channels: { | ||
[name: string]: PusherChannelMock; | ||
}; | ||
/** Initialize PusherMock with empty channels object. */ | ||
constructor(key: string, config: Options); | ||
/** | ||
* Get channel by its name. | ||
* @param {String} name - name of the channel. | ||
* @returns {PusherChannelMock} PusherChannelMock object that represents channel | ||
*/ | ||
channel(name: string): PusherChannelMock; | ||
/** | ||
* Mock subscribing to a channel. | ||
* @param {String} name - name of the channel. | ||
* @returns {PusherChannelMock} PusherChannelMock object that represents channel | ||
*/ | ||
subscribe(name: string): PusherChannelMock; | ||
/** | ||
* Unsubscribe from a mocked channel. | ||
* @param {String} name - name of the channel. | ||
*/ | ||
unsubscribe(name: string): void; | ||
disconnect(): void; | ||
key: string; | ||
config: Options; | ||
channels: { | ||
[name: string]: PusherChannelMock; | ||
}; | ||
/** Initialize PusherMock with empty channels object. */ | ||
constructor(key: string, config: Options); | ||
/** | ||
* Get channel by its name. | ||
* @param {String} name - name of the channel. | ||
* @returns {PusherChannelMock} PusherChannelMock object that represents channel | ||
*/ | ||
channel(name: string): PusherChannelMock; | ||
/** | ||
* Mock subscribing to a channel. | ||
* @param {String} name - name of the channel. | ||
* @returns {PusherChannelMock} PusherChannelMock object that represents channel | ||
*/ | ||
subscribe(name: string): PusherChannelMock; | ||
/** | ||
* Unsubscribe from a mocked channel. | ||
* @param {String} name - name of the channel. | ||
*/ | ||
unsubscribe(name: string): void; | ||
disconnect(): void; | ||
} | ||
export {}; |
@@ -146,15 +146,16 @@ import React, { useRef, useEffect, useContext, useState, useCallback } from 'react'; | ||
/** | ||
* Provider for the pusher service in an app | ||
* Provider that creates your pusher instance and provides it to child hooks throughout your app. | ||
* Note, you can pass in value={{}} as a prop if you'd like to override the pusher client passed. | ||
* This is handy when simulating pusher locally, or for testing. | ||
* @param props Config for Pusher client | ||
*/ | ||
function PusherProvider(_a) { | ||
var PusherProvider = function PusherProvider(_a) { | ||
var clientKey = _a.clientKey, | ||
cluster = _a.cluster, | ||
triggerEndpoint = _a.triggerEndpoint, | ||
authEndpoint = _a.authEndpoint, | ||
auth = _a.auth, | ||
_b = _a.defer, | ||
defer = _b === void 0 ? false : _b, | ||
props = __rest(_a, ["clientKey", "cluster", "triggerEndpoint", "authEndpoint", "auth", "defer"]); // errors when required props are not passed. | ||
children = _a.children, | ||
props = __rest(_a, ["clientKey", "cluster", "triggerEndpoint", "defer", "children"]); // errors when required props are not passed. | ||
@@ -165,34 +166,25 @@ | ||
var children = props.children, | ||
additionalConfig = __rest(props, ["children"]); | ||
var config = __assign({ | ||
cluster: cluster | ||
}, additionalConfig); | ||
}, props); | ||
if (authEndpoint) config.authEndpoint = authEndpoint; | ||
if (auth) config.auth = auth; | ||
var pusherClientRef = useRef(); // track config for comparison | ||
var previousConfig = useRef(); | ||
var previousConfig = useRef(props); | ||
useEffect(function () { | ||
previousConfig.current = config; | ||
previousConfig.current = props; | ||
}); | ||
useEffect(function () { | ||
// if client exists and options are the same, skip. | ||
if (dequal(previousConfig.current, config) && pusherClientRef.current !== undefined) { | ||
// Skip creation of client if deferring, a value prop is passed, or config props are the same. | ||
if (defer || props.value || dequal(previousConfig.current, props) && pusherClientRef.current !== undefined) { | ||
return; | ||
} // optional defer parameter skips creating the class. | ||
// handy if you want to wait for something async like | ||
// a JWT before creating it. | ||
} // create the client and assign it to the ref | ||
if (!defer) { | ||
pusherClientRef.current = new Pusher(clientKey, config); | ||
} | ||
pusherClientRef.current = new Pusher(clientKey, config); // cleanup | ||
return function () { | ||
return pusherClientRef.current && pusherClientRef.current.disconnect(); | ||
pusherClientRef.current && pusherClientRef.current.disconnect(); | ||
}; | ||
}, [clientKey, config, defer, pusherClientRef]); | ||
}, [clientKey, props, defer, pusherClientRef]); | ||
return React.createElement(PusherContext.Provider, _extends({ | ||
@@ -205,10 +197,13 @@ value: { | ||
}, props)); | ||
} | ||
}; | ||
/** | ||
* Provides access to the pusher client | ||
* Provides access to the pusher client instance. | ||
* | ||
* @returns a `MutableRefObject<Pusher|undefined>`. The instance is held by a `useRef()` hook. | ||
* @example | ||
* const {client} = usePusher(); | ||
* ```javscript | ||
* const { client } = usePusher(); | ||
* client.current.subscribe('my-channel'); | ||
* ``` | ||
*/ | ||
@@ -226,6 +221,13 @@ function usePusher() { | ||
/** | ||
* Subscribe to channel | ||
* Subscribe to a channel | ||
* | ||
* @param channelName The name of the channel you want to subscribe to. | ||
* @typeparam Type of channel you're subscribing to. Can be one of Channel or PresenceChannel. | ||
* @returns Instance of the channel you just subscribed to. | ||
* | ||
* @example | ||
* useChannel("my-channel") | ||
* ```javascript | ||
* const channel = useChannel("my-channel") | ||
* channel.bind('some-event', () => {}) | ||
* ``` | ||
*/ | ||
@@ -241,4 +243,4 @@ function useChannel(channelName) { | ||
return; | ||
var channel = pusherClient.subscribe(channelName); | ||
setChannel(channel); | ||
var pusherChannel = pusherClient.subscribe(channelName); | ||
setChannel(pusherChannel); | ||
}, [channelName, pusherClient]); | ||
@@ -251,18 +253,12 @@ return channel; | ||
* | ||
* @param channelName name of presence channel. Should have presence- suffix. | ||
* @param eventName name of event to bind to | ||
* @param onEvent callback to fire when event is called | ||
* @param dependencies dependencies array that onEvent uses | ||
* @param options optional argument to skip events | ||
* @param channelName name of presence the channel. Should have `presence-` suffix. | ||
* @returns Object with `channel`, `members` and `myID` properties. | ||
* | ||
* @example | ||
* const {members} = usePresenceChannel( | ||
* "my-channel", | ||
* "my-event", | ||
* (message) => console.log(message), | ||
* ) | ||
* ```javascript | ||
* const { channel, members, myID } = usePresenceChannel("presence-my-channel"); | ||
* ``` | ||
*/ | ||
function usePresenceChannel(channelName) { | ||
// errors for missing arguments | ||
invariant_1(channelName, 'channelName required to subscribe to a channel'); | ||
invariant_1(channelName.includes('presence-'), "Presence channels should use prefix 'presence-' in their name. Use the useChannel hook instead."); | ||
@@ -272,41 +268,30 @@ // Get regular channel functionality | ||
var _b = useState(), myID = _b[0], setMyID = _b[1]; | ||
/** | ||
* Get members info on subscription success | ||
*/ | ||
var handleSubscriptionSuccess = useCallback(function (members) { | ||
setMembers(members.members); | ||
setMyID(members.myID); | ||
}, []); | ||
/** | ||
* Add or update member in object. | ||
* @note not using a new Map() here to match pusher-js library. | ||
* @param member member being added | ||
*/ | ||
var handleAdd = useCallback(function (member) { | ||
setMembers(function (previousMembers) { | ||
var _a; | ||
return (__assign(__assign({}, previousMembers), (_a = {}, _a[member.id] = member.info, _a))); | ||
}); | ||
}, []); | ||
/** | ||
* Remove member from the state object. | ||
* @param member Member being removed | ||
*/ | ||
var handleRemove = useCallback(function (member) { | ||
setMembers(function (previousMembers) { | ||
var nextMembers = __assign({}, previousMembers); | ||
delete nextMembers[member.id]; | ||
return nextMembers; | ||
}); | ||
}, []); | ||
/** | ||
* Bind and unbind to membership events | ||
*/ | ||
// bind and unbind member events events on our channel | ||
var channel = useChannel(channelName); | ||
useEffect(function () { | ||
if (channel) { | ||
// Get membership info on successful subscription | ||
var handleSubscriptionSuccess_1 = function (members) { | ||
setMembers(members.members); | ||
setMyID(members.myID); | ||
}; | ||
// add a member to the members object | ||
var handleAdd_1 = function (member) { | ||
setMembers(function (previousMembers) { | ||
var _a; | ||
return (__assign(__assign({}, previousMembers), (_a = {}, _a[member.id] = member.info, _a))); | ||
}); | ||
}; | ||
// remove a member from the members object | ||
var handleRemove_1 = function (member) { | ||
setMembers(function (previousMembers) { | ||
var nextMembers = __assign({}, previousMembers); | ||
delete nextMembers[member.id]; | ||
return nextMembers; | ||
}); | ||
}; | ||
// bind to all member addition/removal events | ||
channel.bind('pusher:subscription_succeeded', handleSubscriptionSuccess); | ||
channel.bind('pusher:member_added', handleAdd); | ||
channel.bind('pusher:member_removed', handleRemove); | ||
channel.bind('pusher:subscription_succeeded', handleSubscriptionSuccess_1); | ||
channel.bind('pusher:member_added', handleAdd_1); | ||
channel.bind('pusher:member_removed', handleRemove_1); | ||
// set any members that already existed on the channel | ||
@@ -317,15 +302,14 @@ if (channel.members) { | ||
} | ||
// cleanup | ||
return function () { | ||
channel.unbind('pusher:subscription_succeeded', handleSubscriptionSuccess_1); | ||
channel.unbind('pusher:member_added', handleAdd_1); | ||
channel.unbind('pusher:member_removed', handleRemove_1); | ||
}; | ||
} | ||
// cleanup | ||
return function () { | ||
if (channel) { | ||
channel.unbind('pusher:subscription_succeeded', handleSubscriptionSuccess); | ||
channel.unbind('pusher:member_added', handleAdd); | ||
channel.unbind('pusher:member_removed', handleRemove); | ||
} | ||
}; | ||
}, [channel, handleSubscriptionSuccess, handleAdd, handleRemove]); | ||
var presenceChannel = channel; | ||
// to make typescript happy. | ||
return function () { }; | ||
}, [channel]); | ||
return { | ||
channel: presenceChannel, | ||
channel: channel, | ||
members: members, | ||
@@ -341,3 +325,2 @@ myID: myID, | ||
* @param callback Callback to call on a new event | ||
* @param dependencies Dependencies the callback uses. | ||
*/ | ||
@@ -359,3 +342,4 @@ function useEvent(channel, eventName, callback, dependencies) { | ||
} | ||
channel.bind(eventName, callback); | ||
else | ||
channel.bind(eventName, callback); | ||
return function () { | ||
@@ -368,31 +352,56 @@ channel.unbind(eventName, callback); | ||
/** | ||
* Trigger events hook | ||
* | ||
* @param channel the channel you'd like to trigger clientEvents on. Get this from [[useChannel]] or [[usePresenceChannel]]. | ||
* @typeparam TData shape of the data you're sending with the event. | ||
* @returns A memoized trigger function that will perform client events on the channel. | ||
* @example | ||
* ```javascript | ||
* const channel = useChannel('my-channel'); | ||
* const trigger = useClientTrigger(channel) | ||
* | ||
* const trigger = useTrigger('my-channel'); | ||
* const handleClick = () => trigger('some-client-event', {}); | ||
* ``` | ||
*/ | ||
function useClientTrigger(channel) { | ||
channel && | ||
invariant_1(channel.name.match(/(private-|presence-)/gi), "Channel provided to useClientTrigger wasn't private or presence channel. Client events only work on these types of channels."); | ||
// memoize trigger so it's not being created every render | ||
var trigger = useCallback(function (eventName, data) { | ||
invariant_1(eventName, 'Must pass event name to trigger a client event.'); | ||
channel && channel.trigger(eventName, data); | ||
}, [channel]); | ||
return trigger; | ||
} | ||
/** | ||
* Hook to provide a trigger function that calls the server defined in `PusherProviderProps.triggerEndpoint` using `fetch`. | ||
* Any `auth?.headers` in the config object will be passed with the `fetch` call. | ||
* | ||
* @param channelName name of channel to call trigger on | ||
* @typeparam TData shape of the data you're sending with the event | ||
* | ||
* @example | ||
* ```typescript | ||
* const trigger = useTrigger<{message: string}>('my-channel'); | ||
* trigger('my-event', {message: 'hello'}); | ||
* ``` | ||
*/ | ||
function useTrigger(channelName) { | ||
var _a = usePusher(), client = _a.client, triggerEndpoint = _a.triggerEndpoint; | ||
// you can't use this if you haven't supplied a triggerEndpoint. | ||
invariant_1(triggerEndpoint, 'No trigger endpoint specified to <PusherProvider />. Cannot trigger an event.'); | ||
// subscribe to the channel we'll be triggering to. | ||
useChannel(channelName); | ||
invariant_1(channelName, "No channel specified to trigger to."); | ||
invariant_1(triggerEndpoint, "No trigger endpoint specified to <PusherProvider />. Cannot trigger an event."); | ||
/** | ||
* Memoized trigger function | ||
*/ | ||
// memoized trigger function to return | ||
var trigger = useCallback(function (eventName, data) { | ||
var fetchOptions = { | ||
method: "POST", | ||
body: JSON.stringify({ channelName: channelName, eventName: eventName, data: data }) | ||
method: 'POST', | ||
body: JSON.stringify({ channelName: channelName, eventName: eventName, data: data }), | ||
}; | ||
if (client.current && client.current.config.auth) { | ||
if (client && client.current && client.current.config.auth) { | ||
fetchOptions.headers = client.current.config.auth.headers; | ||
} | ||
else { | ||
console.warn("No auth parameters supplied to <PusherProvider />. Your events will be unauthenticated."); | ||
console.warn('No auth parameters supplied to <PusherProvider />. Your events will be unauthenticated.'); | ||
} | ||
// forcing triggerEndpoint to exist for TS here | ||
// because invariant will throw during dev | ||
return fetch(triggerEndpoint, fetchOptions); | ||
@@ -403,14 +412,2 @@ }, [client, triggerEndpoint, channelName]); | ||
function useClientTrigger(channel) { | ||
channel && | ||
invariant_1(channel.name.match(/(private-|presence-)/gi), "Channel provided to useClientTrigger wasn't private or presence channel. Client events only work on these types of channels."); | ||
// memoize trigger so it's not being created every render | ||
var trigger = useCallback(function (eventName, data) { | ||
if (data === void 0) { data = {}; } | ||
invariant_1(eventName, 'Must pass event name to trigger a client event.'); | ||
channel && channel.trigger(eventName, data); | ||
}, [channel]); | ||
return trigger; | ||
} | ||
var PusherChannelMock = /** @class */ (function () { | ||
@@ -417,0 +414,0 @@ function PusherChannelMock(name) { |
@@ -1,11 +0,4 @@ | ||
'use strict'; | ||
import React, { useRef, useEffect, useContext, useState, useCallback } from 'react'; | ||
import Pusher from 'pusher-js'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var React = require('react'); | ||
var React__default = _interopDefault(React); | ||
var Pusher = _interopDefault(require('pusher-js')); | ||
function _extends() { | ||
@@ -150,18 +143,19 @@ _extends = Object.assign || function (target) { | ||
var PusherContext = React__default.createContext({}); | ||
var PusherContext = React.createContext({}); | ||
var __PusherContext = PusherContext; | ||
/** | ||
* Provider for the pusher service in an app | ||
* Provider that creates your pusher instance and provides it to child hooks throughout your app. | ||
* Note, you can pass in value={{}} as a prop if you'd like to override the pusher client passed. | ||
* This is handy when simulating pusher locally, or for testing. | ||
* @param props Config for Pusher client | ||
*/ | ||
function PusherProvider(_a) { | ||
var PusherProvider = function PusherProvider(_a) { | ||
var clientKey = _a.clientKey, | ||
cluster = _a.cluster, | ||
triggerEndpoint = _a.triggerEndpoint, | ||
authEndpoint = _a.authEndpoint, | ||
auth = _a.auth, | ||
_b = _a.defer, | ||
defer = _b === void 0 ? false : _b, | ||
props = __rest(_a, ["clientKey", "cluster", "triggerEndpoint", "authEndpoint", "auth", "defer"]); // errors when required props are not passed. | ||
children = _a.children, | ||
props = __rest(_a, ["clientKey", "cluster", "triggerEndpoint", "defer", "children"]); // errors when required props are not passed. | ||
@@ -172,35 +166,26 @@ | ||
var children = props.children, | ||
additionalConfig = __rest(props, ["children"]); | ||
var config = __assign({ | ||
cluster: cluster | ||
}, additionalConfig); | ||
}, props); | ||
if (authEndpoint) config.authEndpoint = authEndpoint; | ||
if (auth) config.auth = auth; | ||
var pusherClientRef = React.useRef(); // track config for comparison | ||
var pusherClientRef = useRef(); // track config for comparison | ||
var previousConfig = React.useRef(); | ||
React.useEffect(function () { | ||
previousConfig.current = config; | ||
var previousConfig = useRef(props); | ||
useEffect(function () { | ||
previousConfig.current = props; | ||
}); | ||
React.useEffect(function () { | ||
// if client exists and options are the same, skip. | ||
if (dequal(previousConfig.current, config) && pusherClientRef.current !== undefined) { | ||
useEffect(function () { | ||
// Skip creation of client if deferring, a value prop is passed, or config props are the same. | ||
if (defer || props.value || dequal(previousConfig.current, props) && pusherClientRef.current !== undefined) { | ||
return; | ||
} // optional defer parameter skips creating the class. | ||
// handy if you want to wait for something async like | ||
// a JWT before creating it. | ||
} // create the client and assign it to the ref | ||
if (!defer) { | ||
pusherClientRef.current = new Pusher(clientKey, config); | ||
} | ||
pusherClientRef.current = new Pusher(clientKey, config); // cleanup | ||
return function () { | ||
return pusherClientRef.current && pusherClientRef.current.disconnect(); | ||
pusherClientRef.current && pusherClientRef.current.disconnect(); | ||
}; | ||
}, [clientKey, config, defer, pusherClientRef]); | ||
return React__default.createElement(PusherContext.Provider, _extends({ | ||
}, [clientKey, props, defer, pusherClientRef]); | ||
return React.createElement(PusherContext.Provider, _extends({ | ||
value: { | ||
@@ -212,14 +197,17 @@ client: pusherClientRef, | ||
}, props)); | ||
} | ||
}; | ||
/** | ||
* Provides access to the pusher client | ||
* Provides access to the pusher client instance. | ||
* | ||
* @returns a `MutableRefObject<Pusher|undefined>`. The instance is held by a `useRef()` hook. | ||
* @example | ||
* const {client} = usePusher(); | ||
* ```javscript | ||
* const { client } = usePusher(); | ||
* client.current.subscribe('my-channel'); | ||
* ``` | ||
*/ | ||
function usePusher() { | ||
var context = React.useContext(__PusherContext); | ||
React.useEffect(function () { | ||
var context = useContext(__PusherContext); | ||
useEffect(function () { | ||
if (!context) | ||
@@ -233,6 +221,13 @@ console.warn(NOT_IN_CONTEXT_WARNING); | ||
/** | ||
* Subscribe to channel | ||
* Subscribe to a channel | ||
* | ||
* @param channelName The name of the channel you want to subscribe to. | ||
* @typeparam Type of channel you're subscribing to. Can be one of Channel or PresenceChannel. | ||
* @returns Instance of the channel you just subscribed to. | ||
* | ||
* @example | ||
* useChannel("my-channel") | ||
* ```javascript | ||
* const channel = useChannel("my-channel") | ||
* channel.bind('some-event', () => {}) | ||
* ``` | ||
*/ | ||
@@ -244,8 +239,8 @@ function useChannel(channelName) { | ||
var pusherClient = client && client.current; | ||
var _a = React.useState(), channel = _a[0], setChannel = _a[1]; | ||
React.useEffect(function () { | ||
var _a = useState(), channel = _a[0], setChannel = _a[1]; | ||
useEffect(function () { | ||
if (!pusherClient) | ||
return; | ||
var channel = pusherClient.subscribe(channelName); | ||
setChannel(channel); | ||
var pusherChannel = pusherClient.subscribe(channelName); | ||
setChannel(pusherChannel); | ||
}, [channelName, pusherClient]); | ||
@@ -258,61 +253,44 @@ return channel; | ||
* | ||
* @param channelName name of presence channel. Should have presence- suffix. | ||
* @param eventName name of event to bind to | ||
* @param onEvent callback to fire when event is called | ||
* @param dependencies dependencies array that onEvent uses | ||
* @param options optional argument to skip events | ||
* @param channelName name of presence the channel. Should have `presence-` suffix. | ||
* @returns Object with `channel`, `members` and `myID` properties. | ||
* | ||
* @example | ||
* const {members} = usePresenceChannel( | ||
* "my-channel", | ||
* "my-event", | ||
* (message) => console.log(message), | ||
* ) | ||
* ```javascript | ||
* const { channel, members, myID } = usePresenceChannel("presence-my-channel"); | ||
* ``` | ||
*/ | ||
function usePresenceChannel(channelName) { | ||
// errors for missing arguments | ||
invariant_1(channelName, 'channelName required to subscribe to a channel'); | ||
invariant_1(channelName.includes('presence-'), "Presence channels should use prefix 'presence-' in their name. Use the useChannel hook instead."); | ||
// Get regular channel functionality | ||
var _a = React.useState({}), members = _a[0], setMembers = _a[1]; | ||
var _b = React.useState(), myID = _b[0], setMyID = _b[1]; | ||
/** | ||
* Get members info on subscription success | ||
*/ | ||
var handleSubscriptionSuccess = React.useCallback(function (members) { | ||
setMembers(members.members); | ||
setMyID(members.myID); | ||
}, []); | ||
/** | ||
* Add or update member in object. | ||
* @note not using a new Map() here to match pusher-js library. | ||
* @param member member being added | ||
*/ | ||
var handleAdd = React.useCallback(function (member) { | ||
setMembers(function (previousMembers) { | ||
var _a; | ||
return (__assign(__assign({}, previousMembers), (_a = {}, _a[member.id] = member.info, _a))); | ||
}); | ||
}, []); | ||
/** | ||
* Remove member from the state object. | ||
* @param member Member being removed | ||
*/ | ||
var handleRemove = React.useCallback(function (member) { | ||
setMembers(function (previousMembers) { | ||
var nextMembers = __assign({}, previousMembers); | ||
delete nextMembers[member.id]; | ||
return nextMembers; | ||
}); | ||
}, []); | ||
/** | ||
* Bind and unbind to membership events | ||
*/ | ||
var _a = useState({}), members = _a[0], setMembers = _a[1]; | ||
var _b = useState(), myID = _b[0], setMyID = _b[1]; | ||
// bind and unbind member events events on our channel | ||
var channel = useChannel(channelName); | ||
React.useEffect(function () { | ||
useEffect(function () { | ||
if (channel) { | ||
// Get membership info on successful subscription | ||
var handleSubscriptionSuccess_1 = function (members) { | ||
setMembers(members.members); | ||
setMyID(members.myID); | ||
}; | ||
// add a member to the members object | ||
var handleAdd_1 = function (member) { | ||
setMembers(function (previousMembers) { | ||
var _a; | ||
return (__assign(__assign({}, previousMembers), (_a = {}, _a[member.id] = member.info, _a))); | ||
}); | ||
}; | ||
// remove a member from the members object | ||
var handleRemove_1 = function (member) { | ||
setMembers(function (previousMembers) { | ||
var nextMembers = __assign({}, previousMembers); | ||
delete nextMembers[member.id]; | ||
return nextMembers; | ||
}); | ||
}; | ||
// bind to all member addition/removal events | ||
channel.bind('pusher:subscription_succeeded', handleSubscriptionSuccess); | ||
channel.bind('pusher:member_added', handleAdd); | ||
channel.bind('pusher:member_removed', handleRemove); | ||
channel.bind('pusher:subscription_succeeded', handleSubscriptionSuccess_1); | ||
channel.bind('pusher:member_added', handleAdd_1); | ||
channel.bind('pusher:member_removed', handleRemove_1); | ||
// set any members that already existed on the channel | ||
@@ -323,15 +301,14 @@ if (channel.members) { | ||
} | ||
// cleanup | ||
return function () { | ||
channel.unbind('pusher:subscription_succeeded', handleSubscriptionSuccess_1); | ||
channel.unbind('pusher:member_added', handleAdd_1); | ||
channel.unbind('pusher:member_removed', handleRemove_1); | ||
}; | ||
} | ||
// cleanup | ||
return function () { | ||
if (channel) { | ||
channel.unbind('pusher:subscription_succeeded', handleSubscriptionSuccess); | ||
channel.unbind('pusher:member_added', handleAdd); | ||
channel.unbind('pusher:member_removed', handleRemove); | ||
} | ||
}; | ||
}, [channel, handleSubscriptionSuccess, handleAdd, handleRemove]); | ||
var presenceChannel = channel; | ||
// to make typescript happy. | ||
return function () { }; | ||
}, [channel]); | ||
return { | ||
channel: presenceChannel, | ||
channel: channel, | ||
members: members, | ||
@@ -347,3 +324,2 @@ myID: myID, | ||
* @param callback Callback to call on a new event | ||
* @param dependencies Dependencies the callback uses. | ||
*/ | ||
@@ -355,3 +331,3 @@ function useEvent(channel, eventName, callback, dependencies) { | ||
// deprecate dependencies | ||
React.useEffect(function () { | ||
useEffect(function () { | ||
if (dependencies) { | ||
@@ -362,7 +338,8 @@ console.warn('The useEvent callback is no longer memoized - dependencies are deprecated and its up to you to memoize the callback if you want to.'); | ||
// bind and unbind events whenever the channel, eventName or callback changes. | ||
React.useEffect(function () { | ||
useEffect(function () { | ||
if (channel === undefined) { | ||
return; | ||
} | ||
channel.bind(eventName, callback); | ||
else | ||
channel.bind(eventName, callback); | ||
return function () { | ||
@@ -375,31 +352,56 @@ channel.unbind(eventName, callback); | ||
/** | ||
* Trigger events hook | ||
* | ||
* @param channel the channel you'd like to trigger clientEvents on. Get this from [[useChannel]] or [[usePresenceChannel]]. | ||
* @typeparam TData shape of the data you're sending with the event. | ||
* @returns A memoized trigger function that will perform client events on the channel. | ||
* @example | ||
* ```javascript | ||
* const channel = useChannel('my-channel'); | ||
* const trigger = useClientTrigger(channel) | ||
* | ||
* const trigger = useTrigger('my-channel'); | ||
* const handleClick = () => trigger('some-client-event', {}); | ||
* ``` | ||
*/ | ||
function useClientTrigger(channel) { | ||
channel && | ||
invariant_1(channel.name.match(/(private-|presence-)/gi), "Channel provided to useClientTrigger wasn't private or presence channel. Client events only work on these types of channels."); | ||
// memoize trigger so it's not being created every render | ||
var trigger = useCallback(function (eventName, data) { | ||
invariant_1(eventName, 'Must pass event name to trigger a client event.'); | ||
channel && channel.trigger(eventName, data); | ||
}, [channel]); | ||
return trigger; | ||
} | ||
/** | ||
* Hook to provide a trigger function that calls the server defined in `PusherProviderProps.triggerEndpoint` using `fetch`. | ||
* Any `auth?.headers` in the config object will be passed with the `fetch` call. | ||
* | ||
* @param channelName name of channel to call trigger on | ||
* @typeparam TData shape of the data you're sending with the event | ||
* | ||
* @example | ||
* ```typescript | ||
* const trigger = useTrigger<{message: string}>('my-channel'); | ||
* trigger('my-event', {message: 'hello'}); | ||
* ``` | ||
*/ | ||
function useTrigger(channelName) { | ||
var _a = usePusher(), client = _a.client, triggerEndpoint = _a.triggerEndpoint; | ||
// you can't use this if you haven't supplied a triggerEndpoint. | ||
invariant_1(triggerEndpoint, 'No trigger endpoint specified to <PusherProvider />. Cannot trigger an event.'); | ||
// subscribe to the channel we'll be triggering to. | ||
useChannel(channelName); | ||
invariant_1(channelName, "No channel specified to trigger to."); | ||
invariant_1(triggerEndpoint, "No trigger endpoint specified to <PusherProvider />. Cannot trigger an event."); | ||
/** | ||
* Memoized trigger function | ||
*/ | ||
var trigger = React.useCallback(function (eventName, data) { | ||
// memoized trigger function to return | ||
var trigger = useCallback(function (eventName, data) { | ||
var fetchOptions = { | ||
method: "POST", | ||
body: JSON.stringify({ channelName: channelName, eventName: eventName, data: data }) | ||
method: 'POST', | ||
body: JSON.stringify({ channelName: channelName, eventName: eventName, data: data }), | ||
}; | ||
if (client.current && client.current.config.auth) { | ||
if (client && client.current && client.current.config.auth) { | ||
fetchOptions.headers = client.current.config.auth.headers; | ||
} | ||
else { | ||
console.warn("No auth parameters supplied to <PusherProvider />. Your events will be unauthenticated."); | ||
console.warn('No auth parameters supplied to <PusherProvider />. Your events will be unauthenticated.'); | ||
} | ||
// forcing triggerEndpoint to exist for TS here | ||
// because invariant will throw during dev | ||
return fetch(triggerEndpoint, fetchOptions); | ||
@@ -410,14 +412,2 @@ }, [client, triggerEndpoint, channelName]); | ||
function useClientTrigger(channel) { | ||
channel && | ||
invariant_1(channel.name.match(/(private-|presence-)/gi), "Channel provided to useClientTrigger wasn't private or presence channel. Client events only work on these types of channels."); | ||
// memoize trigger so it's not being created every render | ||
var trigger = React.useCallback(function (eventName, data) { | ||
if (data === void 0) { data = {}; } | ||
invariant_1(eventName, 'Must pass event name to trigger a client event.'); | ||
channel && channel.trigger(eventName, data); | ||
}, [channel]); | ||
return trigger; | ||
} | ||
var PusherChannelMock = /** @class */ (function () { | ||
@@ -510,14 +500,3 @@ function PusherChannelMock(name) { | ||
exports.NOT_IN_CONTEXT_WARNING = NOT_IN_CONTEXT_WARNING; | ||
exports.PusherChannelMock = PusherChannelMock; | ||
exports.PusherMock = PusherMock; | ||
exports.PusherPresenceChannelMock = PusherPresenceChannelMock; | ||
exports.PusherProvider = PusherProvider; | ||
exports.__PusherContext = __PusherContext; | ||
exports.useChannel = useChannel; | ||
exports.useClientTrigger = useClientTrigger; | ||
exports.useEvent = useEvent; | ||
exports.usePresenceChannel = usePresenceChannel; | ||
exports.usePusher = usePusher; | ||
exports.useTrigger = useTrigger; | ||
export { NOT_IN_CONTEXT_WARNING, PusherChannelMock, PusherMock, PusherPresenceChannelMock, PusherProvider, __PusherContext, useChannel, useClientTrigger, useEvent, usePresenceChannel, usePusher, useTrigger }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@harelpls/use-pusher", | ||
"version": "3.1.3", | ||
"version": "3.2.0", | ||
"description": "A wrapper around pusher-js for easy-as hooks in React.", | ||
@@ -24,3 +24,3 @@ "author": "@mayteio", | ||
"jsnext:main": "dist/index.es.js", | ||
"typings": "dist/index.d.ts", | ||
"types": "dist/index.d.ts", | ||
"engines": { | ||
@@ -33,7 +33,7 @@ "node": ">=8", | ||
"test:watch": "react-scripts test --env=jsdom", | ||
"build": "rollup -c", | ||
"build": "rimraf dist && rollup -c", | ||
"start": "rollup -c -w", | ||
"types": "dts-bundle-generator -o ./dist/index.d.ts ./src/index.ts --external-imports pusher-js", | ||
"docs": "typedoc --options ./typedoc.js ./src", | ||
"release": "yarn test && yarn build && yarn types && yarn publish" | ||
"release": "yarn test && yarn build && yarn types && yarn publish && ntl deploy" | ||
}, | ||
@@ -69,2 +69,3 @@ "dependencies": { | ||
"react-test-renderer": "^16.9.0", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^1.20.0", | ||
@@ -71,0 +72,0 @@ "rollup-plugin-babel": "^4.3.3", |
@@ -21,3 +21,2 @@ # use-pusher | ||
- [`useTrigger`](#usetrigger) | ||
- [`useClientTrigger`](#useclienttrigger) | ||
- [`usePusher`](#usepusher) | ||
@@ -162,4 +161,2 @@ | ||
## `useClientTrigger` | ||
> _I don't want a server though_ | ||
@@ -191,3 +188,3 @@ | ||
Testing emitted events with jest can be achieved using `jest.mock` and `@testing-library/react` (or `enzyme`, though your tests should reflect what the user should see **NOT** how the component handles events internally): | ||
Testing emitted events with jest can be achieved using `jest.mock` and `react-testing-library` (or `enzyme`, though your tests should reflect what the user should see **NOT** how the component handles events internally): | ||
@@ -194,0 +191,0 @@ ```typescript |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1078
1.6%51983
-57.99%31
3.33%247
-1.2%