From cb67f0b7a92a7abe665b70dda64291bae0fb6ae3 Mon Sep 17 00:00:00 2001 From: mathieudutour Date: Tue, 4 Jul 2017 00:44:17 +0200 Subject: [PATCH] check the sketch version --- app/package-lock.json | 5 +- app/package.json | 1 + app/src/lib/app-state.ts | 2 + app/src/lib/dispatcher/app-store.ts | 9 +++- app/src/lib/dispatcher/dispatcher.ts | 15 +++--- app/src/lib/kactus.ts | 16 +----- app/src/lib/sketch.ts | 52 +++++++++++++++++++ app/src/ui/app.tsx | 25 ++++++++- app/src/ui/updates/index.ts | 1 + .../ui/updates/sketch-version-outdated.tsx | 35 +++++++++++++ package-lock.json | 6 +++ package.json | 1 + 12 files changed, 142 insertions(+), 26 deletions(-) create mode 100644 app/src/lib/sketch.ts create mode 100644 app/src/ui/updates/sketch-version-outdated.tsx diff --git a/app/package-lock.json b/app/package-lock.json index 37e3301d0..35bf87b26 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "kactus", - "version": "0.1.0-alpha6", + "version": "0.1.0", "lockfileVersion": 1, "dependencies": { "7zip": { @@ -852,8 +852,7 @@ "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, "setimmediate": { "version": "1.0.5", diff --git a/app/package.json b/app/package.json index ce08a3774..7d0487a9e 100644 --- a/app/package.json +++ b/app/package.json @@ -38,6 +38,7 @@ "react-stripe-checkout": "2.4.0", "react-transition-group": "1.2.0", "react-virtualized": "9.8.0", + "semver": "^5.3.0", "source-map-support": "^0.4.15", "stripe": "4.22.0", "textarea-caret": "^3.0.2", diff --git a/app/src/lib/app-state.ts b/app/src/lib/app-state.ts index 7756c95ec..722ef7dee 100644 --- a/app/src/lib/app-state.ts +++ b/app/src/lib/app-state.ts @@ -148,6 +148,8 @@ export interface IAppState { readonly imageDiffType: ImageDiffType readonly isUnlockingKactusFullAccess: boolean + + readonly sketchVersion: string | null | undefined } export enum PopupType { diff --git a/app/src/lib/dispatcher/app-store.ts b/app/src/lib/dispatcher/app-store.ts index 9b092d764..46c70e164 100644 --- a/app/src/lib/dispatcher/app-store.ts +++ b/app/src/lib/dispatcher/app-store.ts @@ -182,6 +182,8 @@ export class AppStore { private isUnlockingKactusFullAccess: boolean = false + private sketchVersion: string | null | undefined + public constructor( gitHubUserStore: GitHubUserStore, cloningRepositoriesStore: CloningRepositoriesStore, @@ -495,6 +497,7 @@ export class AppStore { showAdvancedDiffs: this.showAdvancedDiffs, imageDiffType: this.imageDiffType, isUnlockingKactusFullAccess: this.isUnlockingKactusFullAccess, + sketchVersion: this.sketchVersion, } } @@ -835,10 +838,14 @@ export class AppStore { public _loadFromSharedProcess( accounts: ReadonlyArray, repositories: ReadonlyArray, - initialLoad: boolean + initialLoad: boolean, + sketchVersion?: string | null ) { this.accounts = accounts this.repositories = repositories + if (typeof sketchVersion !== 'undefined') { + this.sketchVersion = sketchVersion + } // doing this that the current user can be found by any of their email addresses for (const account of accounts) { diff --git a/app/src/lib/dispatcher/dispatcher.ts b/app/src/lib/dispatcher/dispatcher.ts index 8b91ca343..85609bd0d 100644 --- a/app/src/lib/dispatcher/dispatcher.ts +++ b/app/src/lib/dispatcher/dispatcher.ts @@ -42,11 +42,8 @@ import { resolveOAuthRequest, rejectOAuthRequest, } from '../../lib/oauth' -import { - saveKactusConfig, - shouldShowPremiumUpsell, - openSketch, -} from '../kactus' +import { saveKactusConfig, shouldShowPremiumUpsell } from '../kactus' +import { openSketch, getSketchVersion } from '../sketch' import { validatedRepositoryPath } from './validated-repository-path' /** @@ -114,7 +111,13 @@ export class Dispatcher { public async loadInitialState(): Promise { const users = await this.loadUsers() const repositories = await this.loadRepositories() - this.appStore._loadFromSharedProcess(users, repositories, true) + const sketchVersion = await getSketchVersion() + this.appStore._loadFromSharedProcess( + users, + repositories, + true, + sketchVersion + ) } private dispatchToSharedProcess(action: Action): Promise { diff --git a/app/src/lib/kactus.ts b/app/src/lib/kactus.ts index 6e992d338..a66515289 100644 --- a/app/src/lib/kactus.ts +++ b/app/src/lib/kactus.ts @@ -5,10 +5,7 @@ import { IKactusStatusResult, find } from 'kactus-cli' import { Repository } from '../models/repository' import { Account } from '../models/account' import { getDotComAPIEndpoint } from './api' - -const SKETCH_PATH = '/Applications/Sketch.app' -const SKETCHTOOL_PATH = - SKETCH_PATH + '/Contents/Resources/sketchtool/bin/sketchtool' +import { SKETCHTOOL_PATH } from './sketch' /** * Retrieve the status for a given repository @@ -169,14 +166,3 @@ export function shouldShowPremiumUpsell( return false } - -export async function openSketch() { - return await new Promise((resolve, reject) => { - exec('open -a sketch', err => { - if (err) { - return reject(err) - } - resolve() - }) - }) -} diff --git a/app/src/lib/sketch.ts b/app/src/lib/sketch.ts new file mode 100644 index 000000000..5b6c83328 --- /dev/null +++ b/app/src/lib/sketch.ts @@ -0,0 +1,52 @@ +import * as Path from 'path' +import { execFile, exec } from 'child_process' + +const regex = /sketchtool Version ((\d|\.)+) \(\d+\)/ +function extractVersion(s: string) { + const match = regex.exec(s) + return match && match[1] +} + +export const SKETCH_PATH = '/Applications/Sketch.app' +export const SKETCHTOOL_PATH = Path.join( + SKETCH_PATH, + '/Contents/Resources/sketchtool/bin/sketchtool' +) + +export async function openSketch() { + return await new Promise((resolve, reject) => { + exec('open -a sketch', err => { + if (err) { + return reject(err) + } + resolve() + }) + }) +} + +let sketchVersion: string | undefined + +export async function getSketchVersion(): Promise { + if (sketchVersion) { + return sketchVersion + } + return new Promise((resolve, reject) => { + execFile(Path.join(SKETCHTOOL_PATH), ['-v'], (err, stdout) => { + if (err) { + return resolve(null) + } + let version = extractVersion(stdout) + if (!version) { + return resolve(null) + } + const pointNumbers = version.split('.').length + if (pointNumbers === 1) { + version = version + '.0.0' + } else if (pointNumbers === 2) { + version = version + '.0' + } + sketchVersion = version + return resolve(version) + }) + }) +} diff --git a/app/src/ui/app.tsx b/app/src/ui/app.tsx index 5203ae5a9..aad07b9d8 100644 --- a/app/src/ui/app.tsx +++ b/app/src/ui/app.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { ipcRenderer } from 'electron' +import * as semver from 'semver' import { RepositoriesList } from './repositories-list' import { RepositoryView } from './repository' import { TitleBar } from './window/title-bar' @@ -38,7 +39,7 @@ import { ILaunchStats } from '../lib/stats' import { Welcome } from './welcome' import { AppMenuBar } from './app-menu' import { findItemByAccessKey, itemIsSelectable } from '../models/app-menu' -import { UpdateAvailable } from './updates' +import { UpdateAvailable, SketchVersionOutdated } from './updates' import { Preferences } from './preferences' import { Account } from '../models/account' import { TipState } from '../models/tip' @@ -1295,6 +1296,27 @@ export class App extends React.Component { ) } + private renderSketchVersionWarning() { + const { sketchVersion } = this.state + if (typeof sketchVersion === 'undefined') { + return null + } + + if (sketchVersion === null) { + return ( + + ) + } + + if (semver.satisfies(sketchVersion, '>=43.0.0')) { + return null + } + + return ( + + ) + } + private renderToolbar() { return ( @@ -1386,6 +1408,7 @@ export class App extends React.Component { ? this.renderWelcomeFlow() : this.renderApp()} {this.renderZoomInfo()} + {this.renderSketchVersionWarning()} ) } diff --git a/app/src/ui/updates/index.ts b/app/src/ui/updates/index.ts index 8ce6b4d32..799da7f66 100644 --- a/app/src/ui/updates/index.ts +++ b/app/src/ui/updates/index.ts @@ -1 +1,2 @@ export { UpdateAvailable } from './update-available' +export { SketchVersionOutdated } from './sketch-version-outdated' diff --git a/app/src/ui/updates/sketch-version-outdated.tsx b/app/src/ui/updates/sketch-version-outdated.tsx new file mode 100644 index 000000000..67f7adfb6 --- /dev/null +++ b/app/src/ui/updates/sketch-version-outdated.tsx @@ -0,0 +1,35 @@ +import * as React from 'react' +import { LinkButton } from '../lib/link-button' +import { Octicon, OcticonSymbol } from '../octicons' +import { Dispatcher } from '../../lib/dispatcher' + +interface ISketchVersionOutdatedProps { + readonly found?: string + readonly dispatcher: Dispatcher +} + +/** + * A component which tells the user an update is available and gives them the + * option of moving into the future or being a luddite. + */ +export class SketchVersionOutdated extends React.Component< + ISketchVersionOutdatedProps, + {} +> { + public render() { + const copy = this.props.found ? `Kactus is only compatible with Sketch >= 43. We found ${this.props.found}.` : 'Kactus needs Sketch to function properly and we couldn\'t find it.' + return ( +
+ + + + {copy} Download Sketch now + +
+ ) + } + + private downloadNow = () => { + this.props.dispatcher.openInBrowser('https://www.sketchapp.com/updates/') + } +} diff --git a/package-lock.json b/package-lock.json index 47b5e55f0..81170b2a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,6 +84,12 @@ "integrity": "sha512-L4KuB7MFk+Tk7t8OlcbWjPbxPvlFGtBIOtfUmdLr80neF27UpkgWcguRxHH8E3YmwIcD5A3WMWYWPb5JFKC18g==", "dev": true }, + "@types/semver": { + "version": "5.3.32", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.3.32.tgz", + "integrity": "sha512-MdbWERx4JWmN4zP+skJy9Kio+Cddvmyn1k8x0S8UAqDoMgOJeobQo7yhlE4BfiimonHirgixWfva/hKUlXBsrw==", + "dev": true + }, "@types/ua-parser-js": { "version": "0.7.30", "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.30.tgz", diff --git a/package.json b/package.json index 2fe2ea372..82d8c9406 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "@types/react-dom": "15.5.1", "@types/react-transition-group": "1.1.1", "@types/react-virtualized": "9.7.2", + "@types/semver": "^5.3.32", "@types/ua-parser-js": "^0.7.30", "@types/uuid": "^2.0.29", "@types/winston": "^2.2.0",