Skip to content
This repository has been archived by the owner on May 21, 2019. It is now read-only.

Commit

Permalink
Create SessionsService.
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-shatskyi committed Aug 4, 2017
1 parent d580763 commit 38bbd74
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 85 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@
"prestart": "npm install && npm run compile",
"start": "concurrently --kill-others -s first \"tsc --watch\" \"cross-env NODE_ENV=development npm run electron\"",
"test": "npm run lint && npm run compile && npm run unit-tests && npm run ui-tests && npm run integration-tests",
"unit-tests": "electron-mocha --require ts-node/register $(find test -name '*_spec.ts')",
"ui-tests": "electron-mocha --require ts-node/register $(find test -name '*_spec.tsx')",
"integration-tests": "electron-mocha --require ts-node/register test/e2e.ts",
"unit-tests": "NODE_ENV=test electron-mocha --require ts-node/register $(find test -name '*_spec.ts')",
"ui-tests": "NODE_ENV=test electron-mocha --require ts-node/register $(find test -name '*_spec.tsx')",
"integration-tests": "NODE_ENV=test electron-mocha --require ts-node/register test/e2e.ts",
"update-dependencies": "ncu -u",
"lint": "tslint `find src -name '*.ts*'` `find test -name '*.ts*'`",
"cleanup": "rimraf compiled/src",
Expand Down
8 changes: 4 additions & 4 deletions src/services/FontService.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
function getLetterSize(size: number, fontFamily: string) {
const height = size + 2;

// Not defined in tests.
if (typeof document.createElement !== "undefined") {
if (process.env.NODE_ENV === "test") {

return {width: (size / 2) + 1.5, height: height};
} else {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d")!;
context.font = `${size}px ${fontFamily}`;
const metrics = context.measureText("m");
return {width: metrics.width, height: height};
} else {
return {width: (size / 2) + 1.5, height: height};
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/services/HistoryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import {historyFilePath} from "../utils/Common";
import * as _ from "lodash";

import csvParse = require("csv-parse/lib/sync");
import {SessionID} from "../shell/Session";

interface HistoryRecordWithoutID {
command: string;
expandedCommand: string;
timestamp: number;
directory: string;
sessionID: number;
sessionID: SessionID;
}

export interface HistoryRecord extends HistoryRecordWithoutID {
Expand Down
36 changes: 36 additions & 0 deletions src/services/SessionsService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Session, SessionID} from "../shell/Session";
import {ApplicationComponent} from "../views/ApplicationComponent";

export class SessionsService {
private readonly sessions: Map<SessionID, Session> = new Map;

create(application: ApplicationComponent) {
const session = new Session(application);
this.sessions.set(session.id, session);

return session.id;
}

get(id: SessionID) {
return this.sessions.get(id)!;
}

close(id: SessionID) {
const session = this.get(id);

session.jobs.forEach(job => {
job.removeAllListeners();
job.interrupt();
});

session.removeAllListeners();

this.sessions.delete(id);
}

closeAll() {
for (const id of this.sessions.keys()) {
this.close(id);
}
}
}
7 changes: 6 additions & 1 deletion src/services/UpdatesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ import * as https from "https";

export class UpdatesService {
isAvailable = false;
private currentVersion = "v" + remote.app.getVersion();
private currentVersion: string;
private INTERVAL = 1000 * 60 * 60 * 12;

constructor() {
if (process.env.NODE_ENV === "test") {
return;
}

this.currentVersion = "v" + remote.app.getVersion();
this.checkUpdate();
setInterval(() => this.checkUpdate(), this.INTERVAL);
}
Expand Down
3 changes: 3 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ import {GitService} from "./GitService";
import {appendFileSync} from "fs";
import {historyFilePath} from "../utils/Common";
import * as csvStringify from "csv-stringify";
import {SessionsService} from "./SessionsService";

// To help IDE with "find usages" and "go to definition".
interface Services {
font: FontService;
history: HistoryService;
updates: UpdatesService;
git: GitService;
sessions: SessionsService;
}

export const services: Services = {
font: new FontService(),
history: new HistoryService(),
updates: new UpdatesService,
git: new GitService(),
sessions: new SessionsService(),
};

services.history.onChange(record => csvStringify(
Expand Down
16 changes: 5 additions & 11 deletions src/shell/Session.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {readFileSync} from "fs";
import {outputJSON} from "fs-extra";
import {Job} from "./Job";
import {EmitterWithUniqueID} from "../EmitterWithUniqueID";
import * as events from "events";
import {PluginManager} from "../PluginManager";
import {Status} from "../Enums";
import {ApplicationComponent} from "../views/ApplicationComponent";
Expand All @@ -16,7 +16,10 @@ import {Aliases, aliasesFromConfig} from "./Aliases";
import * as _ from "lodash";
import {Prompt} from "./Prompt";

export class Session extends EmitterWithUniqueID {
export type SessionID = number & {__isSessionID: true};

export class Session extends events.EventEmitter {
readonly id: SessionID = <SessionID>Date.now();
jobs: Array<Job> = [];
readonly environment = new Environment(processEnvironment);
readonly aliases = new Aliases(aliasesFromConfig);
Expand Down Expand Up @@ -77,15 +80,6 @@ export class Session extends EmitterWithUniqueID {
this.emit("jobs-changed");
}

prepareForClosing() {
this.jobs.forEach(job => {
job.removeAllListeners();
job.interrupt();
});

this.removeAllListeners();
}

close(): void {
// FIXME: executing `sleep 5 && exit` and switching to another session will close an incorrect one.
this.application.closeFocusedSession();
Expand Down
58 changes: 25 additions & 33 deletions src/views/ApplicationComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import {KeyboardAction} from "../Enums";
import {UserEvent} from "../Interfaces";
import {isModifierKey} from "./ViewUtils";
import {TabComponent} from "./TabComponent";
import {Session} from "../shell/Session";
import {SessionID} from "../shell/Session";
import {services} from "../services";
import * as _ from "lodash";

type ApplicationState = {
tabs: Array<{
sessions: Session[];
focusedSessionIndex: number;
}>;
tabs: Array<{sessionIDs: SessionID[]; focusedSessionID: SessionID}>;
focusedTabIndex: number;
};

Expand Down Expand Up @@ -57,16 +55,16 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>
.removeAllListeners("devtools-closed")
.removeAllListeners("found-in-page");

this.state.tabs.forEach(tab => tab.sessions.forEach(session => session.prepareForClosing()));
services.sessions.closeAll();
};
}

const session = new Session(this);
const id = services.sessions.create(this);

this.state = {
tabs: [{
sessions: [session],
focusedSessionIndex: 0,
sessionIDs: [id],
focusedSessionID: id,
}],
focusedTabIndex: 0,
};
Expand Down Expand Up @@ -103,9 +101,9 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>
<TabComponent {...tabProps}
isFocused={index === this.state.focusedTabIndex}
key={index}
onSessionFocus={(sessionIndex: number) => {
onSessionFocus={(id: SessionID) => {
const state = this.cloneState();
state.tabs[state.focusedTabIndex].focusedSessionIndex = sessionIndex;
state.tabs[state.focusedTabIndex].focusedSessionID = id;
this.setState(state);
}}
ref={tabComponent => this.tabComponents[index] = tabComponent!}/>)}
Expand All @@ -127,12 +125,12 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>

addTab(): void {
if (this.state.tabs.length < 9) {
const session = new Session(this);
const id = services.sessions.create(this);

const state = this.cloneState();
state.tabs.push({
sessions: [session],
focusedSessionIndex: 0,
sessionIDs: [id],
focusedSessionID: id,
});
state.focusedTabIndex = state.tabs.length - 1;

Expand Down Expand Up @@ -172,7 +170,7 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>

closeTab(index: number, quit = true): void {
const state = this.cloneState();
state.tabs[index].sessions.forEach(session => session.prepareForClosing());
state.tabs[index].sessionIDs.forEach(id => services.sessions.close(id));

state.tabs.splice(index, 1);
state.focusedTabIndex = Math.max(0, index - 1);
Expand All @@ -193,22 +191,21 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>
*/

get focusedSession() {
return this.focusedTab.sessions[this.focusedTab.focusedSessionIndex];
return services.sessions.get(this.focusedTab.focusedSessionID);
}

otherSession(): void {
const state = this.cloneState();
const tabState = state.tabs[state.focusedTabIndex];

if (this.focusedTab.sessions.length < 2) {
const session = new Session(this);
tabState.sessions.push(session);
tabState.focusedSessionIndex = 1;
if (this.focusedTab.sessionIDs.length < 2) {
const id = services.sessions.create(this);
tabState.sessionIDs.push(id);
tabState.focusedSessionID = id;

this.setState(state, () => this.resizeFocusedTabSessions());
} else {
// Change 1 to 0 or 0 to 1.
tabState.focusedSessionIndex = Math.abs(tabState.focusedSessionIndex - 1);
tabState.focusedSessionID = tabState.sessionIDs.find(id => id !== tabState.focusedSessionID)!;
this.setState(state);
}
}
Expand All @@ -217,13 +214,14 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>
const state = this.cloneState();
const tabState = state.tabs[state.focusedTabIndex];

if (tabState.sessions.length === 1) {
if (tabState.sessionIDs.length === 1) {
this.closeFocusedTab();
} else {
tabState.sessions[tabState.focusedSessionIndex].prepareForClosing();
services.sessions.close(tabState.focusedSessionID);

tabState.sessions.splice(tabState.focusedSessionIndex, 1);
tabState.focusedSessionIndex = 0;
const index = tabState.sessionIDs.findIndex(id => id === tabState.focusedSessionID);
tabState.sessionIDs.splice(index, 1);
tabState.focusedSessionID = tabState.sessionIDs[0];

this.setState(state, () => this.resizeFocusedTabSessions());
}
Expand Down Expand Up @@ -405,12 +403,6 @@ export class ApplicationComponent extends React.Component<{}, ApplicationState>
* accidentally mutate it.
*/
private cloneState(): ApplicationState {
return {
tabs: this.state.tabs.map(tab => ({
sessions: tab.sessions.slice(),
focusedSessionIndex: tab.focusedSessionIndex,
})),
focusedTabIndex: this.state.focusedTabIndex,
};
return _.cloneDeep(this.state);
}
}
Loading

0 comments on commit 38bbd74

Please sign in to comment.