Skip to content

Commit

Permalink
react: poc of async room initialisation
Browse files Browse the repository at this point in the history
  • Loading branch information
AndyTWF committed Oct 27, 2024
1 parent 86b92bc commit 86a6a79
Show file tree
Hide file tree
Showing 27 changed files with 1,168 additions and 819 deletions.
36 changes: 20 additions & 16 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react';
import { FC, useState } from 'react';
import { Chat } from './containers/Chat';
import { OccupancyComponent } from './components/OccupancyComponent';
import { UserPresenceComponent } from './components/UserPresenceComponent';
Expand All @@ -23,20 +23,24 @@ let roomId: string;

interface AppProps {}

const App: FC<AppProps> = () => (
<ChatRoomProvider
id={roomId}
release={true}
attach={true}
options={RoomOptionsDefaults}
>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '800px', margin: 'auto' }}>
<Chat />
<div style={{ display: 'flex', flexDirection: 'column' }}>
<UserPresenceComponent />
<OccupancyComponent />
const App: FC<AppProps> = () => {
const [roomIdState, setRoomId] = useState(roomId);

return (
<ChatRoomProvider
id={roomIdState}
release={true}
attach={true}
options={RoomOptionsDefaults}
>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '800px', margin: 'auto' }}>
<Chat setRoomId={setRoomId} roomId={roomIdState}/>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<UserPresenceComponent />
<OccupancyComponent />
</div>
</div>
</div>
</ChatRoomProvider>
);
</ChatRoomProvider>
)
};
export default App;
41 changes: 37 additions & 4 deletions demo/src/containers/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ReactionInput } from '../../components/ReactionInput';
import { ConnectionStatusComponent } from '../../components/ConnectionStatusComponent/ConnectionStatusComponent.tsx';
import { ConnectionLifecycle, Message, MessageEventPayload, PaginatedResult, Reaction } from '@ably/chat';

export const Chat = () => {
export const Chat = (props: {roomId: string, setRoomId: (roomId: string) => void }) => {
const chatClient = useChatClient();
const clientId = chatClient.clientId;
const [messages, setMessages] = useState<Message[]>([]);
Expand All @@ -15,7 +15,7 @@ export const Chat = () => {

const isConnected: boolean = currentStatus === ConnectionLifecycle.Connected;

const { send: sendMessage, getPreviousMessages } = useMessages({
const { send: sendMessage, getPreviousMessages, roomStatus } = useMessages({
listener: (message: MessageEventPayload) => {
setMessages((prevMessage) => [...prevMessage, message.message]);
},
Expand Down Expand Up @@ -43,16 +43,22 @@ export const Chat = () => {
const messagesEndRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
chatClient.logger.debug('sendMessage changed')
}, [sendMessage])

useEffect(() => {
chatClient.logger.debug('updating getPreviousMessages useEffect', {getPreviousMessages});
// try and fetch the messages up to attachment of the messages listener
if (getPreviousMessages && loading) {
chatClient.logger.debug('fetching initial messages', {roomStatus});
getPreviousMessages({ limit: 50 })
.then((result: PaginatedResult<Message>) => {
chatClient.logger.debug('getPreviousMessages result', result);
setMessages(result.items.reverse());
setLoading(false);
})
.catch((error: unknown) => {
console.error('Error fetching initial messages', error);
setLoading(false);
chatClient.logger.error('Error fetching initial messages', {err: error.toString()});
});
}
}, [getPreviousMessages, loading]);
Expand Down Expand Up @@ -105,6 +111,19 @@ export const Chat = () => {
window.location.reload();
}

function changeRoomId() {
const newRoomId = prompt('Enter your new roomId');
if (!newRoomId) {
return;
}

// Clear the room messages
setMessages([]);
setLoading(true);
setRoomReactions([]);
props.setRoomId(newRoomId);
}

const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
Expand Down Expand Up @@ -132,6 +151,20 @@ export const Chat = () => {
</a>
.
</div>
<div
className="text-xs p-3"
style={{ backgroundColor: '#333' }}
>
You are in room <strong>{props.roomId}</strong>.{' '}
<a
href="#"
className="text-blue-600 dark:text-blue-500 hover:underline"
onClick={changeRoomId}
>
Change roomId
</a>
.
</div>
{loading && <div className="text-center m-auto">loading...</div>}
{!loading && (
<div
Expand Down
8 changes: 7 additions & 1 deletion demo/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ const realtimeClient = new Ably.Realtime({
clientId,
});

const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Debug });
const chatClient = new ChatClient(realtimeClient, { logLevel: LogLevel.Trace });

const reRender = () => {
console.log('did a render')
return true;
}

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
{reRender() ? <div></div> : null}
<AblyProvider client={realtimeClient}>
<ChatClientProvider client={chatClient}>
<App />
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"build": "npm run build:chat && npm run build:react",
"build:chat": "vite build --config ./src/core/vite.config.ts --emptyOutDir",
"build:react": "vite build --config ./src/react/vite.config.ts --emptyOutDir",
"build:start-demo": "npm run build && (cd demo && npm start)",
"prepare": "npm run build",
"test:typescript": "tsc",
"demo:reload": "npm run build && cd demo && npm i file:../",
Expand Down
2 changes: 1 addition & 1 deletion src/core/discontinuity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface HandlesDiscontinuity {
* A promise of the channel that this object is associated with. The promise
* is resolved when the feature has finished initializing.
*/
get channel(): Promise<Ably.RealtimeChannel>;
get channel(): Ably.RealtimeChannel;

/**
* Called when a discontinuity is detected on the channel.
Expand Down
37 changes: 11 additions & 26 deletions src/core/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export interface Messages extends EmitsDiscontinuities {
*
* @returns A promise of the realtime channel.
*/
get channel(): Promise<Ably.RealtimeChannel>;
get channel(): Ably.RealtimeChannel;
}

/**
Expand All @@ -208,7 +208,7 @@ export class DefaultMessages
implements Messages, HandlesDiscontinuity, ContributesToRoomLifecycle
{
private readonly _roomId: string;
private readonly _channel: Promise<Ably.RealtimeChannel>;
private readonly _channel: Ably.RealtimeChannel;
private readonly _chatApi: ChatApi;
private readonly _clientId: string;
private readonly _listenerSubscriptionPoints: Map<
Expand All @@ -229,23 +229,11 @@ export class DefaultMessages
* @param logger An instance of the Logger.
* @param initAfter A promise that is awaited before creating any channels.
*/
constructor(
roomId: string,
realtime: Ably.Realtime,
chatApi: ChatApi,
clientId: string,
logger: Logger,
initAfter: Promise<void>,
) {
constructor(roomId: string, realtime: Ably.Realtime, chatApi: ChatApi, clientId: string, logger: Logger) {
super();
this._roomId = roomId;

this._channel = initAfter.then(() => this._makeChannel(roomId, realtime));

// Catch this so it won't send unhandledrejection global event
this._channel.catch((error: unknown) => {
logger.debug('Messages: channel initialization canceled', { roomId, error });
});
this._channel = this._makeChannel(roomId, realtime);

this._chatApi = chatApi;
this._clientId = clientId;
Expand Down Expand Up @@ -353,7 +341,7 @@ export class DefaultMessages
private async _resolveSubscriptionStart(): Promise<{
fromSerial: string;
}> {
const channelWithProperties = await this._getChannelProperties();
const channelWithProperties = this._getChannelProperties();

// If we are attached, we can resolve with the channelSerial
if (channelWithProperties.state === 'attached') {
Expand All @@ -367,14 +355,11 @@ export class DefaultMessages
return this._subscribeAtChannelAttach();
}

private async _getChannelProperties(): Promise<
Ably.RealtimeChannel & {
properties: { attachSerial: string | undefined; channelSerial: string | undefined };
}
> {
private _getChannelProperties(): Ably.RealtimeChannel & {
properties: { attachSerial: string | undefined; channelSerial: string | undefined };
} {
// Get the attachSerial from the channel properties
const channel = await this._channel;
return channel as Ably.RealtimeChannel & {
return this._channel as Ably.RealtimeChannel & {
properties: {
attachSerial: string | undefined;
channelSerial: string | undefined;
Expand All @@ -383,7 +368,7 @@ export class DefaultMessages
}

private async _subscribeAtChannelAttach(): Promise<{ fromSerial: string }> {
const channelWithProperties = await this._getChannelProperties();
const channelWithProperties = this._getChannelProperties();
return new Promise((resolve, reject) => {
// Check if the state is now attached
if (channelWithProperties.state === 'attached') {
Expand Down Expand Up @@ -423,7 +408,7 @@ export class DefaultMessages
/**
* @inheritdoc Messages
*/
get channel(): Promise<Ably.RealtimeChannel> {
get channel(): Ably.RealtimeChannel {
return this._channel;
}

Expand Down
18 changes: 6 additions & 12 deletions src/core/occupancy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface Occupancy extends EmitsDiscontinuities {
*
* @returns A promise of the underlying Ably channel for occupancy events.
*/
get channel(): Promise<Ably.RealtimeChannel>;
get channel(): Ably.RealtimeChannel;
}

/**
Expand Down Expand Up @@ -98,7 +98,7 @@ export class DefaultOccupancy
implements Occupancy, HandlesDiscontinuity, ContributesToRoomLifecycle
{
private readonly _roomId: string;
private readonly _channel: Promise<Ably.RealtimeChannel>;
private readonly _channel: Ably.RealtimeChannel;
private readonly _chatApi: ChatApi;
private _logger: Logger;
private _discontinuityEmitter: DiscontinuityEmitter = newDiscontinuityEmitter();
Expand All @@ -111,17 +111,11 @@ export class DefaultOccupancy
* @param logger An instance of the Logger.
* @param initAfter A promise that is awaited before creating any channels.
*/
constructor(roomId: string, realtime: Ably.Realtime, chatApi: ChatApi, logger: Logger, initAfter: Promise<void>) {
constructor(roomId: string, realtime: Ably.Realtime, chatApi: ChatApi, logger: Logger) {
super();
this._roomId = roomId;

this._channel = initAfter.then(() => this._makeChannel(roomId, realtime));

// Catch this so it won't send unhandledrejection global event
this._channel.catch((error: unknown) => {
logger.debug('Occupancy: channel initialization canceled', { roomId, error });
});

this._roomId = roomId;
this._channel = this._makeChannel(roomId, realtime);
this._chatApi = chatApi;
this._logger = logger;
}
Expand Down Expand Up @@ -173,7 +167,7 @@ export class DefaultOccupancy
/**
* @inheritdoc Occupancy
*/
get channel(): Promise<Ably.RealtimeChannel> {
get channel(): Ably.RealtimeChannel {
return this._channel;
}

Expand Down
Loading

0 comments on commit 86a6a79

Please sign in to comment.