Merge branch 'release/next-userspace' into lf/groups-refactor

This commit is contained in:
Matilde Park 2020-07-21 13:47:31 -04:00
commit b8c8170eb2
10 changed files with 80 additions and 3 deletions

View File

@ -9,6 +9,9 @@ class Channel {
this.onChannelError = (err) => {
console.error('event source error: ', err);
};
this.onChannelOpen = (e) => {
console.log('open', e);
};
}
init() {
@ -58,6 +61,10 @@ class Channel {
this.onChannelError = onError;
}
setOnChannelOpen(onOpen = (e) => {}) {
this.onChannelOpen = onOpen;
}
deleteOnUnload() {
window.addEventListener("unload", (event) => {
this.delete();
@ -216,6 +223,8 @@ class Channel {
}
}
this.eventSource.onopen = this.onChannelOpen;
this.eventSource.onerror = e => {
this.delete();
this.init();

View File

@ -95,6 +95,8 @@ class App extends React.Component {
associations={associations}
invites={this.state.invites}
api={this.api}
connection={this.state.connection}
subscription={this.subscription}
/>
<Content>
<Switch>

View File

@ -33,6 +33,9 @@ const StatusBar = (props) => {
const invites = (props.invites && props.invites['/contacts'])
? props.invites['/contacts']
: {};
const connection = props.connection || 'connected';
const reconnect = props.subscription.restart.bind(props.subscription);
return (
<div
@ -65,6 +68,15 @@ const StatusBar = (props) => {
</Link>
}
<p className="dib f9 v-mid inter ml2 white-d">{locationName}</p>
{ connection === 'disconnected' &&
(<span
onClick={reconnect}
className="ml4 ph2 dib f9 v-mid red2 inter ba b-red2 br1 pointer"
>Reconnect </span> )
}
{ connection === 'reconnecting' &&
(<span className="ml4 ph2 dib f9 v-mid yellow2 inter ba b-yellow2 br1">Reconnecting</span> )
}
</div>
</div>
);

View File

@ -0,0 +1,14 @@
import _ from 'lodash';
import { StoreState } from '../store/type';
import { Cage } from '../types/cage';
type LocalState = Pick<StoreState, 'connection'>;
export default class ConnectionReducer<S extends LocalState> {
reduce(json: Cage, state: S) {
if('connection' in json && json.connection) {
console.log(`Conn: ${json.connection}`);
state.connection = json.connection;
}
}
}

View File

@ -15,6 +15,7 @@ import PublishUpdateReducer from '../reducers/publish-update';
import PublishResponseReducer from '../reducers/publish-response';
import LaunchReducer from '../reducers/launch-update';
import LinkListenReducer from '../reducers/listen-update';
import ConnectionReducer from '../reducers/connection';
export default class GlobalStore extends BaseStore<StoreState> {
@ -31,12 +32,14 @@ export default class GlobalStore extends BaseStore<StoreState> {
publishUpdateReducer = new PublishUpdateReducer();
publishResponseReducer = new PublishResponseReducer();
launchReducer = new LaunchReducer();
connReducer = new ConnectionReducer();
initialState(): StoreState {
return {
pendingMessages: new Map(),
chatInitialized: false,
connection: 'connected',
sidebarShown: true,
baseHash: null,
invites: {},
@ -90,5 +93,6 @@ export default class GlobalStore extends BaseStore<StoreState> {
this.publishResponseReducer.reduce(data, this.state);
this.launchReducer.reduce(data, this.state);
this.linkListenReducer.reduce(data, this.state);
this.connReducer.reduce(data, this.state);
}
}

View File

@ -11,12 +11,14 @@ import { S3State } from '../types/s3-update';
import { Permissions } from '../types/permission-update';
import { LaunchState, WeatherState } from '../types/launch-update';
import { LinkComments, LinkCollections, LinkSeen } from '../types/link-update';
import { ConnectionStatus } from '../types/connection';
export interface StoreState {
// local state
sidebarShown: boolean;
selectedGroups: SelectedGroup[];
dark: boolean;
connection: ConnectionStatus;
baseHash: string | null;
// invite state
invites: Invites;

View File

@ -3,20 +3,39 @@ import BaseApi from "../api/base";
import { Path } from "../types/noun";
export default class BaseSubscription<S extends object> {
private errorCount = 0;
constructor(public store: BaseStore<S>, public api: BaseApi<S>, public channel: any) {
this.channel.setOnChannelError(this.onChannelError.bind(this));
this.channel.setOnChannelOpen(this.onChannelOpen.bind(this));
}
delete() {
this.channel.delete();
}
// Exists to allow subclasses to hook
restart() {
this.handleEvent({ data: { connection: 'reconnecting' }});
this.start();
}
onChannelOpen(e: any) {
this.errorCount = 0;
this.handleEvent({ data: { connection: 'connected' }});
}
onChannelError(err) {
console.error('event source error: ', err);
this.errorCount++;
if(this.errorCount >= 5) {
console.error("bailing out, too many retries");
this.handleEvent({ data: { connection: 'disconnected' }});
return;
}
this.handleEvent({ data: { connection: 'reconnecting' }});
setTimeout(() => {
this.store.clear();
this.start();
}, 2000);
this.restart();
}, Math.pow(2,this.errorCount - 1) * 750);
}
subscribe(path: Path, app: string) {

View File

@ -1,6 +1,7 @@
import BaseSubscription from './base';
import { StoreState } from '../store/type';
import { Path } from '../types/noun';
import _ from 'lodash';
/**
@ -51,6 +52,16 @@ export default class GlobalSubscription extends BaseSubscription<StoreState> {
this.subscribe('/all', 'weather');
}
restart() {
super.restart();
_.mapValues(this.openSubscriptions, (subs, app: AppName) => {
if(subs.length > 0) {
this.stopApp(app);
this.startApp(app);
}
});
}
startApp(app: AppName) {
if(this.openSubscriptions[app].length > 0) {
console.log(`${app} already started`);

View File

@ -10,6 +10,7 @@ import { GroupUpdate } from "./group-update";
import { PermissionUpdate } from "./permission-update";
import { LaunchUpdate, WeatherState } from "./launch-update";
import { LinkListenUpdate } from './link-listen-update';
import { ConnectionStatus } from "./connection";
interface MarksToTypes {
readonly json: any;
@ -28,6 +29,7 @@ interface MarksToTypes {
readonly 'local': LocalUpdate;
readonly 'weather': WeatherState | {};
readonly 'location': string;
readonly 'connection': ConnectionStatus;
}
export type Cage = Partial<MarksToTypes>;

View File

@ -0,0 +1,2 @@
export type ConnectionStatus = 'reconnecting' | 'disconnected' | 'connected';