Skip to content

Commit

Permalink
Attempt to reconnect when brought to foreground
Browse files Browse the repository at this point in the history
Previous reconnect logic works well when in the foreground, but not when
in the background. We now always attempt to reconnect when foregrounded.
  • Loading branch information
mhoran committed Mar 19, 2024
1 parent b879b4a commit f5a97d4
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 25 deletions.
9 changes: 4 additions & 5 deletions __tests__/lib/weechat/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe(WeechatConnection, () => {
expect(dispatch).toHaveBeenCalledWith(fetchVersionAction('4.1.2'));
});

describe('close', () => {
describe('disconnect', () => {
it('closes the WebSocket', () => {
const dispatch = jest.fn();
const connection = new WeechatConnection(
Expand All @@ -115,12 +115,10 @@ describe(WeechatConnection, () => {
)
} as WebSocketMessageEvent);

connection.close();
connection.disconnect();

expect(mockWebSocket.mock.instances[0].send).toHaveBeenCalledWith(
'quit\n'
);
expect(mockWebSocket.mock.instances[0].close).toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(2, disconnectAction());
expect(mockWebSocket.mock.instances).toHaveLength(1);
});
Expand Down Expand Up @@ -150,6 +148,7 @@ describe(WeechatConnection, () => {
mockWebSocket.mock.instances[0].close();

expect(onError).toHaveBeenCalledWith(true, ConnectionError.Socket);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(2, disconnectAction());
expect(mockWebSocket.mock.instances).toHaveLength(2);
});
Expand Down
44 changes: 30 additions & 14 deletions src/lib/weechat/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export enum ConnectionError {
Authentication
}

enum State {
CONNECTING = 1,
AUTHENTICATING,
CONNECTED,
DISCONNECTED
}

export default class WeechatConnection {
dispatch: AppDispatch;
hostname: string;
Expand All @@ -22,9 +29,8 @@ export default class WeechatConnection {
reconnect: boolean,
connectionError: ConnectionError | null
) => void;
connected: boolean;
reconnect: boolean;
authenticating: boolean;
reconnect = false;
state = State.DISCONNECTED;

constructor(
dispatch: AppDispatch,
Expand All @@ -43,10 +49,11 @@ export default class WeechatConnection {
this.ssl = ssl;
this.onSuccess = onSuccess;
this.onError = onError;
this.reconnect = this.connected = this.authenticating = false;
}

connect(): void {
if (this.state !== State.DISCONNECTED) return;
this.state = State.CONNECTING;
this.openSocket();
}

Expand All @@ -62,7 +69,7 @@ export default class WeechatConnection {
}

onopen(): void {
this.authenticating = true;
this.state = State.AUTHENTICATING;

this.send(
`init password=${this.password},compression=${
Expand All @@ -74,36 +81,41 @@ export default class WeechatConnection {

handleError(event: Event): void {
console.log(event);
this.reconnect = this.connected;
this.reconnect = this.state === State.CONNECTED;
this.onError(this.reconnect, ConnectionError.Socket);
}

handleClose(): void {
if (this.authenticating) {
if (this.state === State.AUTHENTICATING) {
this.state = State.DISCONNECTED;
this.onError(false, ConnectionError.Authentication);
return;
}

this.connected = false;
if (this.state === State.DISCONNECTED) return;

this.state = State.DISCONNECTED;
this.dispatch(disconnectAction());

if (this.reconnect) {
this.reconnect = false;
this.openSocket();
this.connect();
}
}

close(): void {
this.send('quit');
this.websocket?.close();
disconnect(): void {
this.state = State.DISCONNECTED;
if (this.websocket?.readyState === WebSocket.OPEN) {
this.websocket.close();
}
this.dispatch(disconnectAction());
}

onmessage(event: WebSocketMessageEvent): void {
const parsed = protocol.parse(event.data);

if (parsed.id === 'version') {
this.authenticating = false;
this.connected = true;
this.state = State.CONNECTED;
this.onSuccess(this);
}

Expand All @@ -123,4 +135,8 @@ export default class WeechatConnection {
console.log('Sending data:', data);
this.websocket.send(data + '\n');
}

isDisconnected(): boolean {
return this.state === State.DISCONNECTED;
}
}
33 changes: 27 additions & 6 deletions src/usecase/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { StatusBar } from 'react-native';
import { AppState, StatusBar } from 'react-native';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';

Expand All @@ -9,10 +9,10 @@ import { persistor, store } from '../store';
import { addListener } from '@reduxjs/toolkit';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { getPushNotificationStatusAsync } from '../lib/helpers/push-notifications';
import { upgradeAction } from '../store/actions';
import App from './App';
import ConnectionGate from './ConnectionGate';
import Buffer from './buffers/ui/Buffer';
import { upgradeAction } from '../store/actions';

interface State {
connecting: boolean;
Expand All @@ -25,8 +25,16 @@ export default class WeechatNative extends React.Component<null, State> {
connectionError: null
};

connectOnResume = true;

connection?: WeechatConnection;

appStateListener = AppState.addEventListener('change', (nextAppState) => {
if (nextAppState === 'active') {
this.onResume();
}
});

constructor(props: null) {
super(props);
store.dispatch(
Expand All @@ -39,12 +47,17 @@ export default class WeechatNative extends React.Component<null, State> {
);
}

componentWillUnmount(): void {
this.appStateListener.remove();
}

setNotificationToken = async (): Promise<void> => {
const token = await getPushNotificationStatusAsync();
if (token) this.sendMessageToBuffer('core.weechat', '/weechatrn ' + token);
};

onConnectionSuccess = (connection: WeechatConnection): void => {
this.connectOnResume = true;
this.setState({ connecting: false });
connection.send('(hotlist) hdata hotlist:gui_hotlist(*)');
connection.send(
Expand All @@ -63,14 +76,15 @@ export default class WeechatNative extends React.Component<null, State> {
reconnect: boolean,
connectionError: ConnectionError | null
): void => {
this.setState((state) => ({
this.setState({
connecting: reconnect,
connectionError: state.connecting ? connectionError : null
}));
connectionError: reconnect ? null : connectionError
});
};

disconnect = (): void => {
this.connection && this.connection.close();
this.connectOnResume = false;
this.connection && this.connection.disconnect();
};

onConnect = (hostname: string, password: string, ssl: boolean): void => {
Expand All @@ -86,6 +100,13 @@ export default class WeechatNative extends React.Component<null, State> {
this.connection.connect();
};

onResume = () => {
if (this.connectOnResume && this.connection?.isDisconnected()) {
this.setState({ connecting: true, connectionError: null });
this.connection.connect();
}
};

fetchBufferInfo = (
bufferId: string,
numLines = Buffer.DEFAULT_LINE_INCREMENT
Expand Down

0 comments on commit f5a97d4

Please sign in to comment.