Skip to content

Instantly share code, notes, and snippets.

@sudkumar
Last active January 29, 2025 07:39
Show Gist options
  • Save sudkumar/c84be58dd644730fd3ce0ebae98a56db to your computer and use it in GitHub Desktop.
Save sudkumar/c84be58dd644730fd3ce0ebae98a56db to your computer and use it in GitHub Desktop.

Revisions

  1. sudkumar revised this gist May 22, 2020. No changes.
  2. sudkumar revised this gist May 22, 2020. 2 changed files with 11 additions and 8 deletions.
    4 changes: 3 additions & 1 deletion channels.tsx
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,9 @@ import Pusher from "pusher-js"
    const pusherConfig = {
    key: '<your_pusher_key_here>',
    cluster: '<pusher_cluster>',
    authEndpoint: '<auth_endpoint_for_private_channels>' // for example https://example.com/api/broadcasting/auth
    // auth endpoint for private channels
    // e.g. for Laravel https://example.com/api/broadcasting/auth
    authEndpoint: '<auth_endpoint_for_private_channels>'
    }


    15 changes: 8 additions & 7 deletions usage_1_notifications_with_private_channels.tsx
    Original file line number Diff line number Diff line change
    @@ -4,9 +4,9 @@ import { ChannelsProvider, usePrivateChannels } from "./channels"
    // FIRST: we will wrap our application with the `ChannelsProvider`

    function App () {
    // get the user and the access token
    const user = getUser()
    const token = getToken()
    // get the user and the access token SOMEHOW!
    const user = getUser() // via context or any other way
    const token = getToken() // via local storage or any other ways
    return <ChannelsProvider authUser={user} authToken={token}>
    <Notifications authUserId={user.id} />
    </ChannelsProvider>
    @@ -29,14 +29,15 @@ interface INotification {
    /**
    * Our notification channel which notif
    */
    function useNotificationChannel (authUserId: integer, onChange: (notification: INotification) => void) {
    function useNotificationChannel (
    authUserId: integer,
    onChange: (notification: INotification) => void
    ) {
    const channels = usePrivateChannels(authUserId)
    useEffect(() => {
    if (channels) {
    channels.listen(NOTIFICATION_EVENT, onChange)
    // NOTE: Laravel echo provides .notification method to listen to notification
    // which internally calls the listen method with the notification event
    // e.g notificationsChannel.notification(onChange)
    // same as channels.notification(onChange)
    return () => {
    channels.stopListening(NOTIFICATION_EVENT)
    }
  3. sudkumar revised this gist May 22, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion channels.tsx
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@ export function usePrivateChannels (authUserId: any) {
    }

    /**
    * Get the pusher channels
    * Get the channels
    */
    function getChannels(pusherConfig: typeof pusherConfig, authToken?: string) {
    const client = new Pusher(pusherConfig.key, {
  4. sudkumar revised this gist May 22, 2020. 2 changed files with 89 additions and 16 deletions.
    47 changes: 31 additions & 16 deletions channels.tsx
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    import React, { useEffect, useState } from "react"
    import React, { useEffect, useState, useMemo } from "react"
    import Echo from "laravel-echo"
    import Pusher from "pusher-js"

    @@ -10,32 +10,36 @@ const pusherConfig = {
    cluster: '<pusher_cluster>',
    authEndpoint: '<auth_endpoint_for_private_channels>' // for example https://example.com/api/broadcasting/auth
    }

    type TChannels = Echo | undefined


    /**
    * Context for Channels
    */
    export const ChannelContext = React.createContext<TChannels>(undefined)

    export type ChannelProps = { channels: TChannels }
    type TChannels = Echo | undefined
    const ChannelsContext = React.createContext<TChannels>(undefined)

    /**
    * Channel Context Provider
    */
    export function ChannelContextProvider({
    export function ChannelsProvider({
    children,
    authUser,
    authToken
    }: {
    children: React.ReactNode,
    authUser?: any
    authToken?: string
    }) {
    const [channels, setChannels] = useState<TChannels>(undefined)
    useEffect(() => {
    if (config.appEnv === "production" && authUser) {
    setChannels(getChannels())
    }
    }, [user])
    const channels = getChannels(pusherConfig, authToken);
    setChannels(channels)
    return () => {
    // disconnect from server and reset the channels
    channels.disconnect()
    setChannels(undefined)
    }
    }, [authUser, authToken])
    return (
    <ChannelContext.Provider value={channels}>
    {children}
    @@ -47,24 +51,35 @@ export function ChannelContextProvider({
    * Hook to use the channels provided via context
    */
    export function useChannels() {
    const channels = React.useContext(ChannelContext)
    const channels = React.useContext(ChannelsContext)
    return channels
    }

    /**
    * Use private channels
    * It simple return the useChannels with authenticated user bindings
    */
    export function usePrivateChannels (authUserId: any) {
    const channels = useChannels()
    return useMemo(() => {
    return channels && channels.private("users." + authUserId)
    }, [channels, authUserId])
    }

    /**
    * Get the pusher channels
    */
    export function getChannels() {
    function getChannels(pusherConfig: typeof pusherConfig, authToken?: string) {
    const client = new Pusher(pusherConfig.key, {
    cluster: pusherConfig.cluster,
    forceTLS: true,
    authEndpoint: pusherConfig.authEndpoint,
    auth: {
    auth: authToken ? {
    headers: {
    // pass the authorization token when using private channels
    Authorization: `Bearer ${window.localStorage.getItem("access_token")}`,
    Authorization: `Bearer ${authToken}`,
    },
    },
    }: undefined,
    })

    const channels = new Echo({
    58 changes: 58 additions & 0 deletions usage_1_notifications_with_private_channels.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    import React, { useEffect } from 'react'
    import { ChannelsProvider, usePrivateChannels } from "./channels"

    // FIRST: we will wrap our application with the `ChannelsProvider`

    function App () {
    // get the user and the access token
    const user = getUser()
    const token = getToken()
    return <ChannelsProvider authUser={user} authToken={token}>
    <Notifications authUserId={user.id} />
    </ChannelsProvider>
    }


    // Next, we will listen to a private channel for an event
    // and update the data on events

    // A channel listens to a particular event
    // Here is an example event from Laravel when sending notifications
    const NOTIFICATION_EVENT =
    ".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"

    interface INotification {
    id: number,
    content: string
    }

    /**
    * Our notification channel which notif
    */
    function useNotificationChannel (authUserId: integer, onChange: (notification: INotification) => void) {
    const channels = usePrivateChannels(authUserId)
    useEffect(() => {
    if (channels) {
    channels.listen(NOTIFICATION_EVENT, onChange)
    // NOTE: Laravel echo provides .notification method to listen to notification
    // which internally calls the listen method with the notification event
    // e.g notificationsChannel.notification(onChange)
    return () => {
    channels.stopListening(NOTIFICATION_EVENT)
    }
    }
    }, [channels, onChange])
    }

    export function Notifications({ authUserId }: { authUserId: integer }) {
    const [notifications, setNotifications] = useState<Array<INotification>>([])
    const handleNotificationsEvent = useCallback((notification: INotification) => {
    setNotifications(
    existingNotifications => ([notification].concat(existingNotifications))
    )
    })
    useNotificationChannel(authUserId, handleNotificationsEvent)
    return <ol>
    {notifications.map(n => <li key={n.id}>{n.content}</li>}
    </ol>
    }
  5. sudkumar revised this gist May 22, 2020. 1 changed file with 38 additions and 42 deletions.
    80 changes: 38 additions & 42 deletions channels.tsx
    Original file line number Diff line number Diff line change
    @@ -1,57 +1,38 @@
    import React, { useEffect, useState } from "react"
    import Echo from "laravel-echo"
    import Pusher from "pusher-js"
    import { $PropertyType } from "utility-types"

    import config from "./config"
    import { withContext } from "./utils"
    import { useAuthUser } from "./Auth"

    export function getChannels() {
    const client = new Pusher(config.pusher.key, {
    cluster: config.pusher.cluster,
    forceTLS: true,
    authEndpoint: config.pusher.authEndpoint,
    auth: {
    headers: {
    Authorization: `Bearer ${getAuthorizationToken()}`,
    },
    },
    })

    const channels = new Echo({
    broadcaster: "pusher",
    client: client,
    })
    return channels
    /**
    * Pusher configuration
    */
    const pusherConfig = {
    key: '<your_pusher_key_here>',
    cluster: '<pusher_cluster>',
    authEndpoint: '<auth_endpoint_for_private_channels>' // for example https://example.com/api/broadcasting/auth
    }

    type TChannels = Echo | undefined

    /**
    * Context for Channels
    *
    * Usage: Wrap a component with withXHR (dont forget to extends the XHRProps in the swapped components)
    */
    export const ChannelContext = React.createContext<Echo | undefined>(undefined)
    export const withChannels = withContext<Echo | undefined, "channels">(
    ChannelContext,
    "channels"
    )
    export type ChannelProps = { channels: Echo | undefined }
    export const ChannelContext = React.createContext<TChannels>(undefined)

    export type ChannelProps = { channels: TChannels }

    /**
    * Channel Context Provider
    */
    export function ChannelContextProvider({
    children,
    authUser,
    }: {
    children: React.ReactNode
    children: React.ReactNode,
    authUser?: any
    }) {
    const { user } = useAuthUser()
    const [channels, setChannels] = useState<
    $PropertyType<ChannelProps, "channels">
    >(undefined)
    const [channels, setChannels] = useState<TChannels>(undefined)
    useEffect(() => {
    if (config.appEnv === "production" && user) {
    if (config.appEnv === "production" && authUser) {
    setChannels(getChannels())
    }
    }, [user])
    @@ -69,11 +50,26 @@ export function useChannels() {
    const channels = React.useContext(ChannelContext)
    return channels
    }

    /**
    * Assuming we are storing the auth token in local storage
    */
    export function getAuthorizationToken() {
    return localStorage.getItem("access_token")
    }
    * Get the pusher channels
    */
    export function getChannels() {
    const client = new Pusher(pusherConfig.key, {
    cluster: pusherConfig.cluster,
    forceTLS: true,
    authEndpoint: pusherConfig.authEndpoint,
    auth: {
    headers: {
    // pass the authorization token when using private channels
    Authorization: `Bearer ${window.localStorage.getItem("access_token")}`,
    },
    },
    })

    const channels = new Echo({
    broadcaster: "pusher",
    client: client,
    })
    return channels
    }
  6. sudkumar created this gist May 22, 2020.
    79 changes: 79 additions & 0 deletions channels.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    import React, { useEffect, useState } from "react"
    import Echo from "laravel-echo"
    import Pusher from "pusher-js"
    import { $PropertyType } from "utility-types"

    import config from "./config"
    import { withContext } from "./utils"
    import { useAuthUser } from "./Auth"

    export function getChannels() {
    const client = new Pusher(config.pusher.key, {
    cluster: config.pusher.cluster,
    forceTLS: true,
    authEndpoint: config.pusher.authEndpoint,
    auth: {
    headers: {
    Authorization: `Bearer ${getAuthorizationToken()}`,
    },
    },
    })

    const channels = new Echo({
    broadcaster: "pusher",
    client: client,
    })
    return channels
    }

    /**
    * Context for Channels
    *
    * Usage: Wrap a component with withXHR (dont forget to extends the XHRProps in the swapped components)
    */
    export const ChannelContext = React.createContext<Echo | undefined>(undefined)
    export const withChannels = withContext<Echo | undefined, "channels">(
    ChannelContext,
    "channels"
    )
    export type ChannelProps = { channels: Echo | undefined }

    /**
    * Channel Context Provider
    */
    export function ChannelContextProvider({
    children,
    }: {
    children: React.ReactNode
    }) {
    const { user } = useAuthUser()
    const [channels, setChannels] = useState<
    $PropertyType<ChannelProps, "channels">
    >(undefined)
    useEffect(() => {
    if (config.appEnv === "production" && user) {
    setChannels(getChannels())
    }
    }, [user])
    return (
    <ChannelContext.Provider value={channels}>
    {children}
    </ChannelContext.Provider>
    )
    }

    /**
    * Hook to use the channels provided via context
    */
    export function useChannels() {
    const channels = React.useContext(ChannelContext)
    return channels
    }

    /**
    * Assuming we are storing the auth token in local storage
    */
    export function getAuthorizationToken() {
    return localStorage.getItem("access_token")
    }