Skip to content

Commit

Permalink
feat(external-connection): Handle external port connectivity both ways.
Browse files Browse the repository at this point in the history
- Standardize contributor actions to only accept a `contributorId` as a payload.
- Introduce `connection` actions to handle port connections.
    - Introduce `sender` and `receive` meta and use standard `MessageSender` API
  • Loading branch information
lutangar committed May 25, 2020
1 parent b76a4a1 commit 7cab7d0
Show file tree
Hide file tree
Showing 58 changed files with 976 additions and 199 deletions.
3 changes: 2 additions & 1 deletion manifest/development/chromium.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
default_title: `${base.browser_action.default_title} - development`
},
externally_connectable: {
matches: [...base.externally_connectable.matches, '*://localhost/*']
matches: [...base.externally_connectable.matches, '*://localhost/*'],
accepts_tls_channel_id: false
}
};
12 changes: 4 additions & 8 deletions src/app/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Action } from 'redux';
import { LocationChangeAction } from 'connected-react-router';
import { StandardAction } from 'app/store/types';
import Tab from 'app/lmem/tab';
import { BrowserActionClickedAction } from './browser';
import { InstallationDetailsAction, InstalledAction } from './install';
Expand Down Expand Up @@ -68,7 +68,7 @@ import { LoginAction } from './user';
import * as R from 'ramda';
import { Level } from '../utils/Logger';

type MessageSender = chrome.runtime.MessageSender;
type MessageSender = browser.runtime.MessageSender;

export * from './badge';
export * from './tabsLifecycle';
Expand All @@ -86,12 +86,6 @@ export * from './subscription';
export * from './webext';
export * from './serviceMessage.actions';

export interface StandardAction extends Action {
payload?: unknown;
meta?: unknown;
error?: true;
}

export interface BaseAction extends StandardAction {
meta?: ActionMeta;
}
Expand Down Expand Up @@ -143,8 +137,10 @@ export interface ActionMeta {
sendToTab?: boolean;
action?: unknown;
external?: boolean;
receiver?: MessageSender;
sender?: MessageSender;
from?: From;
fromText?: string;
tab?: Tab;
}

Expand Down
47 changes: 38 additions & 9 deletions src/app/actions/install.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BaseAction } from '.';
import { InstallationDetails } from '../lmem/installation';
import { ContributorId } from '../lmem/contributor';
import { ActionMeta, BaseAction, ErrorAction } from '.';
import { InstallationDetails } from 'app/lmem/installation';
import { ContributorId } from 'app/lmem/contributor';
import { Level } from 'app/utils/Logger';

export const INSTALLED = 'INSTALLED';
export const INSTALLED = 'EXTENSION/INSTALLED';
export interface InstalledAction extends BaseAction {
type: typeof INSTALLED;
payload: {
Expand All @@ -19,7 +20,37 @@ export const installed = (
}
});

export const INSTALLATION_DETAILS = 'INSTALLATION_DETAILS';
export const FETCH_INSTALLATION_DETAILS =
'EXTENSION/FETCH_INSTALLATION_DETAILS';
export interface FetchInstallationDetailsAction extends BaseAction {
type: typeof FETCH_INSTALLATION_DETAILS;
}
export const fetchInstallationDetails = (
meta?: ActionMeta
): FetchInstallationDetailsAction => ({
type: FETCH_INSTALLATION_DETAILS,
meta
});

export const FETCH_INSTALLATION_DETAILS_FAILURE =
'EXTENSION/FETCH_INSTALLATION_DETAILS_FAILURE';
export interface FetchInstallationDetailsFailureAction extends ErrorAction {
type: typeof FETCH_INSTALLATION_DETAILS_FAILURE;
}
export const fetchInstallationDetailsFailure = (
error: Error,
meta?: ActionMeta
): FetchInstallationDetailsFailureAction => ({
type: FETCH_INSTALLATION_DETAILS_FAILURE,
payload: error,
error: true,
meta: {
...meta,
severity: Level.ERROR
}
});

export const INSTALLATION_DETAILS = 'EXTENSION/INSTALLATION_DETAILS';
export interface InstallationDetailsAction extends BaseAction {
type: typeof INSTALLATION_DETAILS;
payload: {
Expand All @@ -29,15 +60,13 @@ export interface InstallationDetailsAction extends BaseAction {

export const updateInstallationDetails = (
installationDetails: InstallationDetails,
sendToTab = true
meta?: ActionMeta
): InstallationDetailsAction => ({
type: INSTALLATION_DETAILS,
payload: {
installationDetails
},
meta: {
sendToTab
}
meta
});

export const SETUP = 'SETUP';
Expand Down
77 changes: 70 additions & 7 deletions src/app/actions/subscription.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,95 @@
import { Contributor, ContributorId } from 'app/lmem/contributor';
import { ActionMeta, BaseAction } from '.';
import { ContributorId } from 'app/lmem/contributor';
import { ActionMeta, BaseAction, ErrorAction } from '.';
import { Level } from 'app/utils/Logger';

export interface ContributorAction extends BaseAction {
payload: { contributor: Contributor | ContributorId };
payload: ContributorId;
}

export const SUBSCRIBE = 'SUBSCRIBE';
export interface SubscribeAction extends ContributorAction {
type: typeof SUBSCRIBE;
}
export const subscribe = (
contributor: Contributor | ContributorId,
contributorId: ContributorId,
meta?: ActionMeta
): SubscribeAction => ({
type: SUBSCRIBE,
payload: { contributor },
payload: contributorId,
meta
});

export const SUBSCRIBED = 'SUBSCRIBED';
export interface SubscribedAction extends ContributorAction {
type: typeof SUBSCRIBED;
payload: ContributorId;
}
export const subscribed = (
contributorId: ContributorId,
meta?: ActionMeta
): SubscribedAction => ({
type: SUBSCRIBED,
payload: contributorId,
meta
});

export const SUBSCRIBE_FAILED = 'SUBSCRIBE_FAILED';
export interface SubscribeFailedAction extends ErrorAction {
type: typeof SUBSCRIBE_FAILED;
}
export const subscribeFailed = (
error: Error,
meta?: ActionMeta
): SubscribeFailedAction => ({
type: SUBSCRIBE_FAILED,
payload: error,
error: true,
meta: {
...meta,
severity: Level.ERROR
}
});

export const UNSUBSCRIBE = 'UNSUBSCRIBE';
export interface UnsubscribeAction extends ContributorAction {
type: typeof UNSUBSCRIBE;
}
export const unsubscribe = (
contributor: Contributor | ContributorId,
contributorId: ContributorId,
meta?: ActionMeta
): UnsubscribeAction => ({
type: UNSUBSCRIBE,
payload: { contributor },
payload: contributorId,
meta
});

export const UNSUBSCRIBED = 'UNSUBSCRIBED';
export interface UnsubscribedAction extends ContributorAction {
type: typeof UNSUBSCRIBED;
payload: ContributorId;
}
export const unsubscribed = (
contributorId: ContributorId,
meta?: ActionMeta
): UnsubscribedAction => ({
type: UNSUBSCRIBED,
payload: contributorId,
meta
});

export const UNSUBSCRIBED_FAILED = 'UNSUBSCRIBED_FAILED';
export interface UnsubscribedFailedAction extends ErrorAction {
type: typeof UNSUBSCRIBED_FAILED;
}
export const unsubscribedFailed = (
error: Error,
meta?: ActionMeta
): UnsubscribedFailedAction => ({
type: UNSUBSCRIBED_FAILED,
payload: error,
error: true,
meta: {
...meta,
severity: Level.ERROR
}
});
50 changes: 50 additions & 0 deletions src/app/actions/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ActionMeta, BaseAction, ErrorAction } from 'app/actions';
import { Subscriptions } from 'app/lmem/subscription';
import { Level } from 'app/utils/Logger';

export const FETCH_SUBSCRIPTIONS = 'FETCH_SUBSCRIPTIONS';
export interface FetchSubscriptionsAction extends BaseAction {
type: typeof FETCH_SUBSCRIPTIONS;
}
export const fetchSubscriptions = (
meta: ActionMeta
): FetchSubscriptionsAction => ({
type: FETCH_SUBSCRIPTIONS,
meta
});

export const FETCH_SUBSCRIPTIONS_SUCCESS = 'FETCH_SUBSCRIPTIONS_SUCCESS';
export interface FetchSubscriptionsSuccessAction extends BaseAction {
type: typeof FETCH_SUBSCRIPTIONS_SUCCESS;
payload: Subscriptions;
}
export const fetchSubscriptionsSuccess = (
subscriptions: Subscriptions,
meta?: ActionMeta
): FetchSubscriptionsSuccessAction => ({
type: FETCH_SUBSCRIPTIONS_SUCCESS,
payload: subscriptions,
meta
});

export const FETCH_SUBSCRIPTIONS_FAILURE = 'FETCH_SUBSCRIPTIONS_FAILURE';
export interface FetchSubscriptionsFailureAction extends ErrorAction {
type: typeof FETCH_SUBSCRIPTIONS_FAILURE;
}
export const fetchSubscriptionsFailure = (
error: Error,
meta?: ActionMeta
): FetchSubscriptionsFailureAction => ({
type: FETCH_SUBSCRIPTIONS_FAILURE,
payload: error,
error: true,
meta: {
...meta,
severity: Level.ERROR
}
});

export type SubscriptionsAction =
| FetchSubscriptionsAction
| FetchSubscriptionsSuccessAction
| FetchSubscriptionsFailureAction;
3 changes: 2 additions & 1 deletion src/app/actions/tabsLifecycle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReceivedAction } from 'webext/createMessageHandler';
import Tab from 'app/lmem/tab';
import { BaseAction, StandardAction, TabAction } from '.';
import { StandardAction } from 'app/store/types';
import { BaseAction, TabAction } from '.';

export const TAB_REMOVED = 'BROWSER/TAB_REMOVED';

Expand Down
9 changes: 2 additions & 7 deletions src/app/actions/webext.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {
ActionMeta,
AppAction,
BaseAction,
ErrorAction,
StandardAction
} from './index';
import { StandardAction } from 'app/store/types';
import { ActionMeta, AppAction, BaseAction, ErrorAction } from './index';
import { From } from 'webext/From';
import { Level } from '../utils/Logger';

Expand Down
6 changes: 6 additions & 0 deletions src/app/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import onStartup from 'webext/onStartup';
import { installed, startup } from 'app/actions';
import { configureSentryScope, initSentry } from 'app/utils/sentry';
import { store } from './store';
import { connect } from 'app/store/actions/connection';

type Port = browser.runtime.Port;

initSentry();
configureSentryScope(scope => {
Expand All @@ -28,3 +31,6 @@ onInstalled.then(installedDetails => {
});

onStartup.then(() => store.dispatch(startup()));
browser.runtime.onConnectExternal.addListener((port: Port) =>
store.dispatch(connect(port))
);
9 changes: 2 additions & 7 deletions src/app/background/reducers/subscriptions.reducer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable no-unused-expressions, @typescript-eslint/ban-ts-ignore */
import { expect } from 'chai';
import { subscribe, SubscribeAction } from 'app/actions';
import { generateContributor } from 'test/fakers/generateContributor';
import subscriptionsReducer, {
SubscriptionsState
} from './subscriptions.reducer';
Expand All @@ -15,9 +14,7 @@ describe('background > reducers > subscriptions', function() {
});
it('saves subscriptions', () => {
const state: SubscriptionsState = [1, 2, 3];
const subscribeTo42: SubscribeAction = subscribe(
generateContributor({ id: 42 })
);
const subscribeTo42: SubscribeAction = subscribe(42);

const expectedSubscriptions = [1, 2, 3, 42];

Expand All @@ -27,9 +24,7 @@ describe('background > reducers > subscriptions', function() {
});
it('saves unsubscriptions', () => {
const state: SubscriptionsState = [1, 2, 3];
const unsubscribeFrom2: SubscribeAction = subscribe(
generateContributor({ id: 2 })
);
const unsubscribeFrom2: SubscribeAction = subscribe(2);

expect(subscriptionsReducer(state, unsubscribeFrom2)).to.include.members([
1,
Expand Down
15 changes: 2 additions & 13 deletions src/app/background/reducers/subscriptions.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,21 @@ import {
SUBSCRIBE,
UNSUBSCRIBE,
AppAction,
ContributorAction,
UPDATE_CONTRIBUTORS
} from 'app/actions';

export type SubscriptionsState = number[];

export const getContributorId = ({
payload: { contributor }
}: ContributorAction) =>
typeof contributor !== 'number' ? contributor.id : contributor;

export const getContributorName = ({
payload: { contributor }
}: ContributorAction) =>
typeof contributor !== 'number' ? contributor.name : '';

export default function subscriptionsReducer(
state: SubscriptionsState = [],
action: AppAction
) {
switch (action.type) {
case SUBSCRIBE: {
return R.append(getContributorId(action), state);
return R.append(action.payload, state);
}
case UNSUBSCRIBE: {
return R.without([getContributorId(action)], state);
return R.without([action.payload], state);
}
case UPDATE_CONTRIBUTORS:
return R.intersection(
Expand Down
Loading

0 comments on commit 7cab7d0

Please sign in to comment.