diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..c1582722c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +matrix: + include: + - os: osx + osx_image: xcode9.4 + language: node_js + node_js: "10" + env: + - ELECTRON_CACHE=$HOME/.cache/electron + - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder + +cache: + directories: + - node_modules + - $HOME/.cache/electron + - $HOME/.cache/electron-builder + +script: + - yarn workspace @devhub/desktop build --mac --win --linux + +before_cache: + - rm -rf $HOME/.cache/electron-builder/wine + +branches: + only: + - master + +if: tag IS present diff --git a/@types/electron/index.d.ts b/@types/electron/index.d.ts new file mode 100644 index 000000000..c85a5c941 --- /dev/null +++ b/@types/electron/index.d.ts @@ -0,0 +1,16 @@ +/// + +interface Window { + eval: never + ipc: Electron.IpcRenderer + process?: { + type?: string + } + require: NodeRequireFunction +} + +declare namespace NodeJS { + interface ProcessVersions { + electron?: boolean + } +} diff --git a/assets/icon-desktop.psd b/assets/icon-desktop.psd new file mode 100644 index 000000000..1a6f16b21 Binary files /dev/null and b/assets/icon-desktop.psd differ diff --git a/assets/icon-tray.psd b/assets/icon-tray.psd new file mode 100644 index 000000000..c6278fc68 Binary files /dev/null and b/assets/icon-tray.psd differ diff --git a/assets/logo-circle.psd b/assets/logo-circle.psd new file mode 100644 index 000000000..a24e26533 Binary files /dev/null and b/assets/logo-circle.psd differ diff --git a/assets/logo-rounded.psd b/assets/logo-rounded.psd new file mode 100644 index 000000000..ae555e784 Binary files /dev/null and b/assets/logo-rounded.psd differ diff --git a/packages/mobile/assets/logo.psd b/assets/logo-transparent.psd similarity index 54% rename from packages/mobile/assets/logo.psd rename to assets/logo-transparent.psd index 2042f5f07..f8bf14fc8 100644 Binary files a/packages/mobile/assets/logo.psd and b/assets/logo-transparent.psd differ diff --git a/assets/logo.psd b/assets/logo.psd new file mode 100644 index 000000000..ba1fceabd Binary files /dev/null and b/assets/logo.psd differ diff --git a/packages/mobile/assets/play_store_feature.psd b/assets/play-store-feature.psd similarity index 100% rename from packages/mobile/assets/play_store_feature.psd rename to assets/play-store-feature.psd diff --git a/package.json b/package.json index 53c4c78b9..4ceb9af61 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,13 @@ "name": "devhub", "version": "0.40.0", "private": true, + "author": { + "name": "Bruno Lemos", + "email": "bruno@devhubapp.com", + "url": "https://twitter.com/brunolemos" + }, + "description": "TweetDeck for GitHub", + "repository": "https://github.com/devhubapp/devhub", "workspaces": { "packages": [ "packages/*" @@ -11,16 +18,15 @@ ] }, "scripts": { - "build:desktop": "yarn compile && concurrently --kill-others \"yarn compile -w\" \"yarn workspace @devhub/desktop build\"", + "build:desktop": "yarn workspace @devhub/desktop compile && concurrently --kill-others \"yarn workspace @devhub/desktop compile -w\" \"yarn workspace @devhub/desktop build\"", "clean": "yarn workspaces run clean", - "compile": "yarn workspace @devhub/components build", "deploy": "yarn deploy:web", "deploy:web": "yarn workspace @devhub/web build && yarn now && yarn now alias", - "deploy:www": "pushd packages/www; now && now alias; popd;", - "dev": "yarn compile && concurrently --kill-others \"yarn compile -w\" \"yarn workspace @devhub/web start\" \"yarn workspace @devhub/mobile start\" \"yarn workspace @devhub/desktop start\"", - "dev:desktop": "yarn compile && concurrently --kill-others \"yarn compile -w\" \"yarn workspace @devhub/desktop start\"", - "dev:mobile": "yarn compile && concurrently --kill-others \"yarn compile -w\" \"yarn workspace @devhub/mobile start\"", - "dev:web": "yarn compile && concurrently --kill-others \"yarn compile -w\" \"yarn workspace @devhub/web start\"", + "deploy:www": "pushd \"packages/www\"; now && now alias; popd;", + "dev": "yarn workspace @devhub/desktop compile && concurrently --kill-others \"yarn workspace @devhub/desktop compile -w\" \"yarn workspace @devhub/web start\" \"yarn workspace @devhub/mobile start\" \"wait-on http://localhost:3000 && yarn workspace @devhub/desktop start\"", + "dev:desktop": "cross-env BROWSER=none concurrently \"yarn dev:web\" \"yarn workspace @devhub/desktop compile -w\" \"wait-on http://localhost:3000 && yarn workspace @devhub/desktop start\"", + "dev:mobile": "yarn workspace @devhub/mobile compile && concurrently --kill-others \"yarn workspace @devhub/mobile compile -w\" \"yarn workspace @devhub/mobile start\"", + "dev:web": "yarn workspace @devhub/web compile && concurrently --kill-others \"yarn workspace @devhub/web compile -w\" \"yarn workspace @devhub/web start\"", "format": "yarn workspaces run format", "lint": "yarn workspaces run lint", "now": "npx now", @@ -32,16 +38,17 @@ "dependencies": {}, "devDependencies": { "concurrently": "^4.1.0", + "cross-env": "^5.2.0", "husky": "^1.2.0", "lint-staged-offline": "^0.0.1", "patch-package": "^5.1.1", "postinstall-prepare": "^1.0.1", "prettier": "^1.15.3", - "rimraf": "^2.6.2", + "shx": "0.3.2", "tslint": "^5.11.0", "tslint-config-prettier": "^1.17.0", "tslint-react": "^3.6.0", - "typescript": "^3.2.1" + "typescript": "^3.2.2" }, "resolutions": { "scheduler": "0.12.0" diff --git a/packages/components/assets/logo.png b/packages/components/assets/logo.png deleted file mode 100644 index ed445e670..000000000 Binary files a/packages/components/assets/logo.png and /dev/null differ diff --git a/packages/components/assets/logo_circle.png b/packages/components/assets/logo_circle.png new file mode 100644 index 000000000..78a229d79 Binary files /dev/null and b/packages/components/assets/logo_circle.png differ diff --git a/packages/components/assets/logo_circle@2x.png b/packages/components/assets/logo_circle@2x.png new file mode 100644 index 000000000..afe37abdd Binary files /dev/null and b/packages/components/assets/logo_circle@2x.png differ diff --git a/packages/components/assets/logo_circle@3x.png b/packages/components/assets/logo_circle@3x.png new file mode 100644 index 000000000..0defc1057 Binary files /dev/null and b/packages/components/assets/logo_circle@3x.png differ diff --git a/packages/components/assets/logo_rounded.png b/packages/components/assets/logo_rounded.png new file mode 100644 index 000000000..c17fc1e21 Binary files /dev/null and b/packages/components/assets/logo_rounded.png differ diff --git a/packages/components/assets/logo_rounded@2x.png b/packages/components/assets/logo_rounded@2x.png new file mode 100644 index 000000000..838059754 Binary files /dev/null and b/packages/components/assets/logo_rounded@2x.png differ diff --git a/packages/components/assets/logo_rounded@3x.png b/packages/components/assets/logo_rounded@3x.png new file mode 100644 index 000000000..5f0ce786b Binary files /dev/null and b/packages/components/assets/logo_rounded@3x.png differ diff --git a/packages/components/assets/logo_square.png b/packages/components/assets/logo_square.png new file mode 100644 index 000000000..ce6119515 Binary files /dev/null and b/packages/components/assets/logo_square.png differ diff --git a/packages/components/assets/logo_square@2x.png b/packages/components/assets/logo_square@2x.png new file mode 100644 index 000000000..dbf3951c4 Binary files /dev/null and b/packages/components/assets/logo_square@2x.png differ diff --git a/packages/components/assets/logo_square@3x.png b/packages/components/assets/logo_square@3x.png new file mode 100644 index 000000000..d8b89e448 Binary files /dev/null and b/packages/components/assets/logo_square@3x.png differ diff --git a/packages/components/package.json b/packages/components/package.json index 90a7f7ec6..27c659d3a 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -4,8 +4,8 @@ "private": false, "main": "dist", "scripts": { - "build": "tsc -b", - "clean": "rimraf dist/*", + "compile": "tsc -b", + "clean": "shx rm -rf dist/*", "format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'", "lint": "tslint -p .", "prepare": "cd .. && yarn patch-package" @@ -66,7 +66,7 @@ "tslint-config-airbnb": "^5.11.1", "tslint-config-prettier": "^1.17.0", "tslint-react": "^3.6.0", - "typescript": "^3.2.1" + "typescript": "^3.2.2" }, "resolutions": { "scheduler": "0.12.0" diff --git a/packages/components/src/components/common/Link.tsx b/packages/components/src/components/common/Link.tsx index 3607f20ed..96d3cbb59 100644 --- a/packages/components/src/components/common/Link.tsx +++ b/packages/components/src/components/common/Link.tsx @@ -1,8 +1,9 @@ import React, { AnchorHTMLAttributes } from 'react' -import { Animated, Linking, View } from 'react-native' +import { Animated, View } from 'react-native' import { Omit } from '@devhub/core' import { Browser } from '../../libs/browser' +import { Linking } from '../../libs/linking' import { Platform } from '../../libs/platform' import { AnimatedTouchableOpacity } from '../animated/AnimatedTouchableOpacity' import { TouchableOpacity, TouchableOpacityProps } from './TouchableOpacity' @@ -65,7 +66,7 @@ export function Link(props: LinkProps) { {...Platform.select({ default: { onPress: href - ? href.includes('http') + ? href.startsWith('http') ? () => Browser.openURL(href) : () => Linking.openURL(href) : undefined, diff --git a/packages/components/src/components/layout/Sidebar.tsx b/packages/components/src/components/layout/Sidebar.tsx index f7321d26f..504bd5cca 100644 --- a/packages/components/src/components/layout/Sidebar.tsx +++ b/packages/components/src/components/layout/Sidebar.tsx @@ -29,7 +29,7 @@ import { Separator } from '../common/Separator' import { TouchableOpacity } from '../common/TouchableOpacity' import { useAppLayout } from '../context/LayoutContext' -const logo = require('@devhub/components/assets/logo.png') // tslint:disable-line +const logo = require('@devhub/components/assets/logo_circle.png') // tslint:disable-line const styles = StyleSheet.create({ centerContainer: { @@ -218,7 +218,6 @@ export const Sidebar = React.memo((props: SidebarProps) => { style={{ width: sidebarSize / 2, height: sidebarSize / 2, - borderRadius: sidebarSize / (2 * 2), }} /> diff --git a/packages/components/src/components/modals/EnterpriseSetupModal.tsx b/packages/components/src/components/modals/EnterpriseSetupModal.tsx index 52c42a2bc..044c37291 100644 --- a/packages/components/src/components/modals/EnterpriseSetupModal.tsx +++ b/packages/components/src/components/modals/EnterpriseSetupModal.tsx @@ -1,6 +1,6 @@ import _ from 'lodash' import React, { useState } from 'react' -import { Alert, Animated, Clipboard, Linking, View } from 'react-native' +import { Animated, Clipboard, View } from 'react-native' import { useAnimatedTheme } from '../../hooks/use-animated-theme' import { useReduxState } from '../../redux/hooks/use-redux-state' diff --git a/packages/components/src/hooks/use-app-visibility.ts b/packages/components/src/hooks/use-app-visibility.ts index dcc521de1..dfaa0f8e2 100644 --- a/packages/components/src/hooks/use-app-visibility.ts +++ b/packages/components/src/hooks/use-app-visibility.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react' import { AppState, AppStateStatus } from 'react-native' +import { Platform } from '../libs/platform' export function useAppVisibility() { const [isVisible, setIsVisible] = useState( @@ -20,5 +21,30 @@ export function useAppVisibility() { [isVisible], ) + useEffect( + () => { + if (!Platform.isElectron) return + + const focusHandler = () => { + if (isVisible) return + setIsVisible(true) + } + + const blurHandler = () => { + if (!isVisible) return + setIsVisible(false) + } + + window.addEventListener('focus', focusHandler) + window.addEventListener('blur', blurHandler) + + return () => { + window.removeEventListener('focus', focusHandler) + window.removeEventListener('blur', blurHandler) + } + }, + [isVisible], + ) + return isVisible } diff --git a/packages/components/src/libs/browser/index-native.ts b/packages/components/src/libs/browser/index-native.ts new file mode 100644 index 000000000..7ff98975a --- /dev/null +++ b/packages/components/src/libs/browser/index-native.ts @@ -0,0 +1,15 @@ +import { BrowserCrossPlatform } from '.' +import { Linking } from '../linking' + +export const Browser: BrowserCrossPlatform = { + addEventListener: (e: any, handler: any) => { + if (e === 'url') return Linking.addEventListener(e, handler) + console.debug('[BROWSER] Ignoring addEventListener event', e) // tslint:disable-line no-console + }, + removeEventListener: (e: any, handler: any) => { + if (e === 'url') return Linking.removeEventListener(e, handler) + console.debug('[BROWSER] Ignoring removeEventListener event', e) // tslint:disable-line no-console + }, + dismiss: () => undefined, + openURL: Linking.openURL, +} diff --git a/packages/components/src/libs/browser/index.d.ts b/packages/components/src/libs/browser/index.d.ts new file mode 100644 index 000000000..6e37177ed --- /dev/null +++ b/packages/components/src/libs/browser/index.d.ts @@ -0,0 +1,14 @@ +export interface BrowserCrossPlatform { + addEventListener: { + (event: 'url', handler: ((payload: { url: string }) => void)): void + (event: 'onDismiss', handler: (() => void)): void + } + removeEventListener: { + (event: 'url', handler: ((payload: { url: string }) => void)): void + (event: 'onDismiss', handler: (() => void)): void + } + dismiss(): void + openURL(url: string): void +} + +export const Browser: BrowserCrossPlatform diff --git a/packages/components/src/libs/browser/index.ios.ts b/packages/components/src/libs/browser/index.ios.ts index 131f9e08f..65d6e2f1a 100644 --- a/packages/components/src/libs/browser/index.ios.ts +++ b/packages/components/src/libs/browser/index.ios.ts @@ -1,10 +1,25 @@ -import { Linking, StatusBar } from 'react-native' +import { StatusBar } from 'react-native' import SafariView, { SafaryOptions } from 'react-native-safari-view' + +import { BrowserCrossPlatform } from '.' import { bugsnag } from '../bugsnag' +import { Linking } from '../linking' -export const Browser = { - ...Linking, +export const Browser: BrowserCrossPlatform = { ...SafariView, + addEventListener: (e: any, handler: any) => { + if (e === 'url') return Linking.addEventListener(e, handler) + if (e === 'onShow') return SafariView.addEventListener(e, handler) + if (e === 'onDismiss') return SafariView.addEventListener(e, handler) + console.debug('[BROWSER] Unknown addEventListener event', e) // tslint:disable-line no-console + }, + removeEventListener: (e: any, handler: any) => { + if (e === 'url') return Linking.removeEventListener(e, handler) + if (e === 'onShow') return SafariView.removeEventListener(e, handler) + if (e === 'onDismiss') return SafariView.removeEventListener(e, handler) + console.debug('[BROWSER] Unknown removeEventListener event', e) // tslint:disable-line no-console + }, + dismiss: SafariView.dismiss, openURL: (url: string, options?: SafaryOptions) => { SafariView.isAvailable() .then(isAvailable => { diff --git a/packages/components/src/libs/browser/index.native.ts b/packages/components/src/libs/browser/index.native.ts new file mode 100644 index 000000000..6ed08b509 --- /dev/null +++ b/packages/components/src/libs/browser/index.native.ts @@ -0,0 +1,4 @@ +import { BrowserCrossPlatform } from '.' + +// tslint:disable-next-line no-var-requires +export const Browser: BrowserCrossPlatform = require('./index-native').Browser diff --git a/packages/components/src/libs/browser/index.ts b/packages/components/src/libs/browser/index.ts deleted file mode 100644 index f4f8ebea3..000000000 --- a/packages/components/src/libs/browser/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Linking } from 'react-native' - -export const Browser = Object.assign(Linking, { - dismiss: () => undefined, -}) diff --git a/packages/components/src/libs/browser/index.web.ts b/packages/components/src/libs/browser/index.web.ts new file mode 100644 index 000000000..6ed08b509 --- /dev/null +++ b/packages/components/src/libs/browser/index.web.ts @@ -0,0 +1,4 @@ +import { BrowserCrossPlatform } from '.' + +// tslint:disable-next-line no-var-requires +export const Browser: BrowserCrossPlatform = require('./index-native').Browser diff --git a/packages/components/src/libs/linking/index-electron.ts b/packages/components/src/libs/linking/index-electron.ts new file mode 100644 index 000000000..579dbf3a4 --- /dev/null +++ b/packages/components/src/libs/linking/index-electron.ts @@ -0,0 +1,40 @@ +// Source: https://github.com/PaulLeCam/react-native-electron/blob/master/src/apis/Linking.js + +import _ from 'lodash' + +import { LinkingCrossPlatform } from './index' +import { Linking as LinkingOriginal } from './index-native' + +const eventHandlers = new Map() + +export const Linking: LinkingCrossPlatform = { + addEventListener: (type: string, handler: any) => { + if (!(type === 'url' && typeof handler === 'function')) return + + const wrapHandler = (_e: any, url: string) => { + handler({ type, url }) + } + + eventHandlers.set(handler, wrapHandler) + window.ipc.addListener('open-url', wrapHandler) + }, + async canOpenURL(url: string) { + return LinkingOriginal.canOpenURL(url) + }, + async getInitialURL() { + return '' + }, + openURL: (url: string): Promise => { + return LinkingOriginal.openURL(url) + }, + removeEventListener: (type: string, handler: any) => { + if (!(type === 'url' && typeof handler === 'function')) return + + const wrapHandler = eventHandlers.get(handler) + if (wrapHandler) { + window.ipc.removeListener('open-url', wrapHandler) + } + + eventHandlers.delete(handler) + }, +} diff --git a/packages/components/src/libs/linking/index-native.ts b/packages/components/src/libs/linking/index-native.ts new file mode 100644 index 000000000..950afcaa8 --- /dev/null +++ b/packages/components/src/libs/linking/index-native.ts @@ -0,0 +1,3 @@ +import _ from 'lodash' + +export { Linking } from 'react-native' diff --git a/packages/components/src/libs/linking/index.d.ts b/packages/components/src/libs/linking/index.d.ts new file mode 100644 index 000000000..4fb5e3347 --- /dev/null +++ b/packages/components/src/libs/linking/index.d.ts @@ -0,0 +1,15 @@ +export interface LinkingCrossPlatform { + addEventListener: ( + event: 'url', + handler: (payload: { url: string }) => void, + ) => void + removeEventListener: ( + event: 'url', + handler: (payload: { url: string }) => void, + ) => void + canOpenURL(url: string): Promise + getInitialURL(): Promise + openURL(url: string): void +} + +export const Linking: LinkingCrossPlatform diff --git a/packages/components/src/libs/linking/index.native.ts b/packages/components/src/libs/linking/index.native.ts new file mode 100644 index 000000000..cce31f04e --- /dev/null +++ b/packages/components/src/libs/linking/index.native.ts @@ -0,0 +1,5 @@ +import _ from 'lodash' + +import { LinkingCrossPlatform } from './index' + +export const Linking: LinkingCrossPlatform = require('./index-native').Linking // tslint:disable-line no-var-requires diff --git a/packages/components/src/libs/linking/index.web.ts b/packages/components/src/libs/linking/index.web.ts new file mode 100644 index 000000000..f8971cbeb --- /dev/null +++ b/packages/components/src/libs/linking/index.web.ts @@ -0,0 +1,8 @@ +import _ from 'lodash' + +import { Platform } from '../platform' +import { LinkingCrossPlatform } from './index' + +export const Linking: LinkingCrossPlatform = Platform.isElectron + ? require('./index-electron').Linking // tslint:disable-line no-var-requires + : require('./index-native').Linking // tslint:disable-line no-var-requires diff --git a/packages/components/src/libs/oauth/helpers.shared.ts b/packages/components/src/libs/oauth/helpers.shared.ts deleted file mode 100644 index bbc5a5099..000000000 --- a/packages/components/src/libs/oauth/helpers.shared.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Omit } from '@devhub/core' -import qs from 'qs' - -export interface OAuthResponseData { - app_token?: string - code: string - github_scope: string[] - github_token?: string - github_token_created_at?: string - github_token_type?: string - oauth: boolean -} - -export const getUrlParamsIfMatches = ( - url: string, - prefix: string, -): OAuthResponseData | null => { - if (!url || typeof url !== 'string') return null - - if (url.startsWith(prefix)) { - const query = url.replace(new RegExp(`^${prefix}[?]?`), '') - const params = (qs.parse(query) || {}) as Omit< - OAuthResponseData, - 'github_scope' | 'oauth' - > & { - github_scope?: string - oauth?: string | boolean - } - - return { - ...params, - github_scope: (params.github_scope || '') - .replace(/,/g, ' ') - .split(' ') - .filter(Boolean) - .map((scope: string) => `${scope || ''}`.trim()) - .filter(Boolean), - oauth: params.oauth === true || params.oauth === 'true', - } - } - - return null -} diff --git a/packages/components/src/libs/oauth/helpers.ts b/packages/components/src/libs/oauth/helpers.ts index 325dc24ad..6fd3e6963 100644 --- a/packages/components/src/libs/oauth/helpers.ts +++ b/packages/components/src/libs/oauth/helpers.ts @@ -1,9 +1,50 @@ -import { Linking } from 'react-native' +import qs from 'qs' +import { Omit } from '@devhub/core' import { Browser } from '../browser' +import { Linking } from '../linking' import { Platform } from '../platform' -export * from './helpers.shared' +export interface OAuthResponseData { + app_token?: string + code: string + github_scope: string[] + github_token?: string + github_token_created_at?: string + github_token_type?: string + oauth: boolean +} + +export const getUrlParamsIfMatches = ( + url: string, + prefix: string, +): OAuthResponseData | null => { + if (!url || typeof url !== 'string') return null + + if (url.startsWith(prefix)) { + const query = url.replace(new RegExp(`^${prefix}[?]?`), '') + const params = (qs.parse(query) || {}) as Omit< + OAuthResponseData, + 'github_scope' | 'oauth' + > & { + github_scope?: string + oauth?: string | boolean + } + + return { + ...params, + github_scope: (params.github_scope || '') + .replace(/,/g, ' ') + .split(' ') + .filter(Boolean) + .map((scope: string) => `${scope || ''}`.trim()) + .filter(Boolean), + oauth: params.oauth === true || params.oauth === 'true', + } + } + + return null +} export const listenForNextUrl = () => { let finished = false @@ -48,6 +89,70 @@ export const listenForNextUrl = () => { }) } -export const listenForNextMessageData = () => { - throw new Error('Not implemented on this platform.') +export const listenForNextMessageData = ( + popup?: ReturnType, +) => { + let finished = false + + return new Promise((resolve, reject) => { + const handleMessage = (e?: { + data?: OAuthResponseData & { error?: boolean | string; oauth: boolean } + }) => { + // console.log('[OAUTH] Message received', e) + // Can be messages from other places, e.g. from the redux devtools + if ( + !( + e && + e.data && + (e.data.oauth || + (e.data.app_token || e.data.github_token || e.data.error)) + ) + ) { + return + } + + const { app_token: appToken, github_token: githubToken, error } = e.data + + window.removeEventListener('message', handleMessage) + + if (appToken && githubToken && !error) resolve(e.data) + else + reject( + new Error(typeof error === 'string' ? error : 'No token received'), + ) + + finished = true + } + + window.addEventListener('message', handleMessage, false) + setTimeout(() => { + if (finished) return + + finished = true + window.removeEventListener('message', handleMessage) + reject(new Error('Timeout')) + }, 120 * 1000) + + if (popup) { + const onClosePopup = async () => { + // the close may be detected before the postMessage + if (!(popup.closed && !finished)) return + + // console.log('[OAUTH] Popup closed.') + finished = true + window.removeEventListener('message', handleMessage) + reject(new Error('Canceled')) + } + + // reliable cross-browser way to check if popup was closed + const timer = setInterval(() => { + if (popup.closed) { + onClosePopup() + clearInterval(timer) + } else if (finished) { + clearInterval(timer) + } + }, 500) + } + }) } diff --git a/packages/components/src/libs/oauth/helpers.web.ts b/packages/components/src/libs/oauth/helpers.web.ts deleted file mode 100644 index 19d8c4892..000000000 --- a/packages/components/src/libs/oauth/helpers.web.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { OAuthResponseData } from './helpers' - -export * from './helpers.shared' - -export const listenForNextUrl = () => { - throw new Error('Not implemented on this platform.') -} - -export const listenForNextMessageData = ( - popup?: ReturnType, -) => { - let finished = false - - return new Promise((resolve, reject) => { - const handleMessage = (e?: { - data?: OAuthResponseData & { error?: boolean | string; oauth: boolean } - }) => { - // console.log('[OAUTH] Message received', e) - // Can be messages from other places, e.g. from the redux devtools - if ( - !( - e && - e.data && - (e.data.oauth || - (e.data.app_token || e.data.github_token || e.data.error)) - ) - ) { - return - } - - const { app_token: appToken, github_token: githubToken, error } = e.data - - window.removeEventListener('message', handleMessage) - - if (appToken && githubToken && !error) resolve(e.data) - else - reject( - new Error(typeof error === 'string' ? error : 'No token received'), - ) - - finished = true - } - - window.addEventListener('message', handleMessage, false) - setTimeout(() => { - if (finished) return - - finished = true - window.removeEventListener('message', handleMessage) - reject(new Error('Timeout')) - }, 120 * 1000) - - if (popup) { - const onClosePopup = async () => { - // the close may be detected before the postMessage - if (!(popup.closed && !finished)) return - - // console.log('[OAUTH] Popup closed.') - finished = true - window.removeEventListener('message', handleMessage) - reject(new Error('Canceled')) - } - - // reliable cross-browser way to check if popup was closed - const timer = setInterval(() => { - if (popup.closed) { - onClosePopup() - clearInterval(timer) - } else if (finished) { - clearInterval(timer) - } - }, 500) - } - }) -} diff --git a/packages/components/src/libs/oauth/index.web.ts b/packages/components/src/libs/oauth/index.web.ts index 741caff28..648b957f3 100644 --- a/packages/components/src/libs/oauth/index.web.ts +++ b/packages/components/src/libs/oauth/index.web.ts @@ -2,7 +2,14 @@ import qs from 'qs' import { constants } from '@devhub/core' import { Platform } from '../platform/index.web' -import { listenForNextMessageData } from './helpers.web' +import { + getUrlParamsIfMatches, + listenForNextMessageData, + listenForNextUrl, + OAuthResponseData, +} from './helpers' + +const redirectUri = 'devhub://oauth/github' const popupTarget = Platform.realOS !== 'web' || @@ -26,7 +33,7 @@ export async function executeOAuth(scope: string[]) { const scopeStr = (scope || []).join(' ') const querystring = qs.stringify({ scope: scopeStr, - redirect_uri: '', + redirect_uri: Platform.isElectron ? redirectUri : '', }) // console.log('[OAUTH] Opening popup...') @@ -35,14 +42,24 @@ export async function executeOAuth(scope: string[]) { ) try { - const data = await listenForNextMessageData(popup) - // console.log('[OAUTH] Received data:', data) + let params: OAuthResponseData | null + + if (Platform.isElectron) { + const url = await listenForNextUrl() + // console.log('[OAUTH] Received URL:', url) + + params = getUrlParamsIfMatches(url, redirectUri) + // console.log('[OAUTH] URL params:', params) + } else { + params = await listenForNextMessageData(popup) + // console.log('[OAUTH] Received data:', params) + } - if (!(data && data.app_token && data.github_token)) { + if (!(params && params.app_token && params.github_token)) { throw new Error('Login failed: No access token received.') } - return data + return params } catch (e) { if (popup && popup !== window) popup.close() diff --git a/packages/components/src/libs/platform/index.ts b/packages/components/src/libs/platform/index.ts index f550c4466..1367163dc 100644 --- a/packages/components/src/libs/platform/index.ts +++ b/packages/components/src/libs/platform/index.ts @@ -4,6 +4,7 @@ import { PlataformSelectSpecifics, PlatformSelectOptions } from './index.shared' export const Platform = { ..._Platform, + isElectron: false, isStandalone: true, realOS: _Platform.OS, selectUsingRealOS( diff --git a/packages/components/src/libs/platform/index.web.ts b/packages/components/src/libs/platform/index.web.ts index 9a3b5f4c6..3cc2ce571 100644 --- a/packages/components/src/libs/platform/index.web.ts +++ b/packages/components/src/libs/platform/index.web.ts @@ -6,13 +6,44 @@ import { PlatformSelectOptions, } from './index.shared' +// From: https://github.com/cheton/is-electron +function isElectron() { + if ( + typeof window !== 'undefined' && + typeof window.process === 'object' && + window.process.type === 'renderer' + ) { + return true + } + + if ( + typeof process !== 'undefined' && + typeof process.versions === 'object' && + !!process.versions.electron + ) { + return true + } + + if ( + typeof navigator === 'object' && + typeof navigator.userAgent === 'string' && + navigator.userAgent.indexOf('Electron') >= 0 + ) { + return true + } + + return false +} + function getOSName(): PlatformOSType { const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera if (/android/i.test(userAgent)) return 'android' + if (/iPad|iPhone|iPod/.test(userAgent) && !(window as any).MSStream) return 'ios' + return 'web' } @@ -21,6 +52,7 @@ const realOS = getOSName() export const Platform = { realOS, ..._Platform, + isElectron: isElectron(), isStandalone: (window.navigator as any).standalone, selectUsingRealOS( specifics: PlataformSelectSpecifics, diff --git a/packages/components/src/screens/LoginScreen.tsx b/packages/components/src/screens/LoginScreen.tsx index 55f83f8f5..904daf048 100644 --- a/packages/components/src/screens/LoginScreen.tsx +++ b/packages/components/src/screens/LoginScreen.tsx @@ -19,7 +19,7 @@ import { useReduxState } from '../redux/hooks/use-redux-state' import * as selectors from '../redux/selectors' import { contentPadding } from '../styles/variables' -const logo = require('@devhub/components/assets/logo.png') // tslint:disable-line +const logo = require('@devhub/components/assets/logo_rounded.png') // tslint:disable-line const styles = StyleSheet.create({ container: { @@ -51,7 +51,6 @@ const styles = StyleSheet.create({ logo: { alignSelf: 'center', - borderRadius: 100 / 8, height: 100, marginBottom: contentPadding / 2, width: 100, @@ -161,6 +160,7 @@ export const LoginScreen = React.memo(() => { if ( Platform.OS === 'web' && + !Platform.isElectron && window.history && window.history.replaceState ) { diff --git a/packages/core/package.json b/packages/core/package.json index 349b205e9..6feb67de8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -4,8 +4,8 @@ "private": false, "main": "dist/index.js", "scripts": { - "build": "tsc -b", - "clean": "rimraf dist/*", + "compile": "tsc -b", + "clean": "shx rm -rf dist/*", "format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'", "lint": "tslint -p .", "prepare": "cd .. && yarn patch-package" @@ -25,6 +25,6 @@ "tslint-config-airbnb": "^5.11.1", "tslint-config-prettier": "^1.17.0", "tslint-react": "^3.6.0", - "typescript": "^3.2.1" + "typescript": "^3.2.2" } } diff --git a/packages/desktop/assets/icons/background.png b/packages/desktop/assets/icons/background.png new file mode 100644 index 000000000..1b0c2a4a8 Binary files /dev/null and b/packages/desktop/assets/icons/background.png differ diff --git a/packages/desktop/assets/icons/background@2x.png b/packages/desktop/assets/icons/background@2x.png new file mode 100644 index 000000000..1fdfe501b Binary files /dev/null and b/packages/desktop/assets/icons/background@2x.png differ diff --git a/packages/desktop/assets/icons/icon.ico b/packages/desktop/assets/icons/icon.ico new file mode 100644 index 000000000..19b05c03e Binary files /dev/null and b/packages/desktop/assets/icons/icon.ico differ diff --git a/packages/desktop/assets/icons/icon.png b/packages/desktop/assets/icons/icon.png new file mode 100644 index 000000000..a5eddacac Binary files /dev/null and b/packages/desktop/assets/icons/icon.png differ diff --git a/packages/desktop/assets/icons/icon@2x.png b/packages/desktop/assets/icons/icon@2x.png new file mode 100644 index 000000000..4f4068843 Binary files /dev/null and b/packages/desktop/assets/icons/icon@2x.png differ diff --git a/packages/desktop/assets/icons/trayIconTemplate.png b/packages/desktop/assets/icons/trayIconTemplate.png new file mode 100644 index 000000000..68965c74f Binary files /dev/null and b/packages/desktop/assets/icons/trayIconTemplate.png differ diff --git a/packages/desktop/assets/icons/trayIconTemplate@2x.png b/packages/desktop/assets/icons/trayIconTemplate@2x.png new file mode 100644 index 000000000..11cfb865b Binary files /dev/null and b/packages/desktop/assets/icons/trayIconTemplate@2x.png differ diff --git a/packages/desktop/assets/icons/trayIconTemplate@3x.png b/packages/desktop/assets/icons/trayIconTemplate@3x.png new file mode 100644 index 000000000..6fc8171ba Binary files /dev/null and b/packages/desktop/assets/icons/trayIconTemplate@3x.png differ diff --git a/packages/desktop/assets/icons/trayIconWhite.png b/packages/desktop/assets/icons/trayIconWhite.png new file mode 100644 index 000000000..553025a1f Binary files /dev/null and b/packages/desktop/assets/icons/trayIconWhite.png differ diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 90fc65f26..c26bd2d5c 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -2,80 +2,79 @@ "name": "@devhub/desktop", "version": "0.40.0", "private": false, - "main": "dist/electron.js", + "main": "dist/index.js", + "author": { + "name": "Bruno Lemos", + "email": "bruno@devhubapp.com", + "url": "https://twitter.com/brunolemos" + }, + "description": "TweetDeck for GitHub", + "repository": "https://github.com/devhubapp/devhub", "scripts": { - "build": "tsc -b && electron-builder -mw", - "clean": "rimraf dist/*", + "build": "yarn clean && yarn compile && pushd \"../web\" && yarn build && popd && shx cp -Rf ../web/dist dist/web && yarn build:electron", + "build:electron": "electron-builder", + "compile": "tsc -b", + "clean": "shx rm -rf build && shx rm -rf dist", "format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'", "lint": "tslint -p .", "predeploy": "yarn run build", - "start": "tsc && electron .", + "start": "nodemon --watch dist --watch assets --exec \"electron .\"", "test": "echo \"Error: no test specified\" && exit 1" }, "build": { - "productName": "devhub", - "appId": "com.devhubapp.devhub", - "dmg": { - "contents": [ - { - "x": 410, - "y": 150, - "type": "link", - "path": "/Applications" - }, - { - "x": 130, - "y": 150, - "type": "file" - } - ] - }, + "appId": "com.devhubapp", + "productName": "DevHub", + "extends": null, "directories": { + "buildResources": "assets", "output": "build" }, - "files": [ - "dist/**/*", - "node_modules/**/*", - "package.json" - ], + "extraMetadata": { + "name": "devhub" + }, + "linux": { + "icon": "icons/icon.png" + }, "mac": { - "icon": "public/static/icns/logo.icns" + "category": "public.app-category.developer-tools", + "icon": "icons/icon.png" }, "win": { - "target": "nsis", - "icon": "public/static/icns/logo.ico" + "icon": "icons/icon.ico" }, - "nsis": { - "oneClick": true + "files": [ + "assets", + "dist" + ], + "protocols": [ + { + "name": "DevHub", + "schemes": [ + "devhub" + ] + } + ], + "publish": { + "provider": "github" } }, "dependencies": { - "@babel/polyfill": "^7.0.0", - "@devhub/components": "0.40.0", - "react": "16.7.0-alpha.2", - "react-app-polyfill": "^0.1.3", - "react-art": "16.7.0-alpha.2", - "react-dom": "16.7.0-alpha.2", - "react-native-web": "0.9.9", - "react-scripts": "2.1.1", - "resize-observer-polyfill": "^1.5.0", - "smoothscroll-polyfill": "0.4.3" + "electron-store": "^2.0.0", + "electron-updater": "^4.0.6", + "jsonfile": "^5.0.0", + "mkdirp": "^0.5.1" }, "devDependencies": { + "@types/electron-devtools-installer": "^2.2.0", + "@types/electron-store": "^1.3.0", + "@types/jsonfile": "^5.0.0", + "@types/mkdirp": "^0.5.2", "electron": "^4.0.0", "electron-builder": "^20.38.3", + "electron-devtools-installer": "^2.2.4", + "nodemon": "^1.18.9", "tslint": "^5.11.0", - "tslint-config-airbnb": "^5.11.1", - "tslint-config-prettier": "^1.17.0", - "typescript": "^3.2.1" - }, - "resolutions": { - "scheduler": "0.12.0" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 9", - "not op_mini all" - ] + "typescript": "^3.2.2", + "wait-on": "^3.2.0" + } } diff --git a/packages/desktop/public/favicon.ico b/packages/desktop/public/favicon.ico deleted file mode 100644 index da4c18b30..000000000 Binary files a/packages/desktop/public/favicon.ico and /dev/null differ diff --git a/packages/desktop/public/index.html b/packages/desktop/public/index.html deleted file mode 100644 index e1c50ddaa..000000000 --- a/packages/desktop/public/index.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - DevHub - TweetDeck for GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - \ No newline at end of file diff --git a/packages/desktop/public/manifest.json b/packages/desktop/public/manifest.json deleted file mode 100644 index 68610dc2c..000000000 --- a/packages/desktop/public/manifest.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "DevHub", - "short_name": "DevHub", - "description": "TweetDeck for GitHub", - "lang": "en-US", - "start_url": "./?utm_source=web_app_manifest", - "display": "standalone", - "orientation": "any", - "theme_color": "#292c33", - "icons": [ - { - "src": "static/media/logo.png", - "sizes": "100x100", - "type": "image/png" - }, - { - "src": "static/media/logo.png", - "sizes": "200x200", - "type": "image/png" - }, - { - "src": "static/media/logo.png", - "sizes": "300x300", - "type": "image/png" - }, - { - "src": "static/media/logo@2x.png", - "sizes": "600x600", - "type": "image/png" - }, - { - "src": "static/media/logo@3x.png", - "sizes": "900x900", - "type": "image/png" - } - ], - "background_color": "#1c1c1c", - "prefer_related_applications": true -} \ No newline at end of file diff --git a/packages/desktop/public/static/css/arrow.css b/packages/desktop/public/static/css/arrow.css deleted file mode 100644 index 7f772a1fd..000000000 --- a/packages/desktop/public/static/css/arrow.css +++ /dev/null @@ -1,14 +0,0 @@ -body { - background: transparent !important; - -webkit-app-region: drag !important; -} - -.header-arrow { - width: 0; - height: 0; - border-right: 10px solid transparent; - border-bottom: 10px solid #fff; - border-left: 10px solid transparent; - margin: auto; - transition: border-bottom-width .2s ease,border-bottom-color .3s ease; -} \ No newline at end of file diff --git a/packages/desktop/public/static/css/error.css b/packages/desktop/public/static/css/error.css deleted file mode 100644 index bced294f2..000000000 --- a/packages/desktop/public/static/css/error.css +++ /dev/null @@ -1,25 +0,0 @@ -* { - box-sizing: border-box; -} - -body, -html { - width: 100%; - height: 100%; - margin: 0; - padding: 0; -} - -body { - display: flex; - align-content: center; - align-items: center; - background-color: #1c1c1c; - text-align: center; - color: #fff; - font-family: sans-serif; -} - -#error-container { - margin: auto; -} \ No newline at end of file diff --git a/packages/desktop/public/static/icns/logo.icns b/packages/desktop/public/static/icns/logo.icns deleted file mode 100644 index 92491eedf..000000000 Binary files a/packages/desktop/public/static/icns/logo.icns and /dev/null differ diff --git a/packages/desktop/public/static/icns/logo.ico b/packages/desktop/public/static/icns/logo.ico deleted file mode 100644 index cd76e6c02..000000000 Binary files a/packages/desktop/public/static/icns/logo.ico and /dev/null differ diff --git a/packages/desktop/public/static/media/logo-transparent@2x.png b/packages/desktop/public/static/media/logo-transparent@2x.png deleted file mode 100644 index 9aaa6d9d2..000000000 Binary files a/packages/desktop/public/static/media/logo-transparent@2x.png and /dev/null differ diff --git a/packages/desktop/public/static/media/logo.png b/packages/desktop/public/static/media/logo.png deleted file mode 100644 index 71330d5fd..000000000 Binary files a/packages/desktop/public/static/media/logo.png and /dev/null differ diff --git a/packages/desktop/public/static/media/logo@2x.png b/packages/desktop/public/static/media/logo@2x.png deleted file mode 100644 index 17400f328..000000000 Binary files a/packages/desktop/public/static/media/logo@2x.png and /dev/null differ diff --git a/packages/desktop/public/static/media/logo@3x.png b/packages/desktop/public/static/media/logo@3x.png deleted file mode 100644 index bfcd83a45..000000000 Binary files a/packages/desktop/public/static/media/logo@3x.png and /dev/null differ diff --git a/packages/desktop/src/electron.ts b/packages/desktop/src/electron.ts deleted file mode 100644 index 373234d51..000000000 --- a/packages/desktop/src/electron.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { constants } from '@devhub/core' -import { - app, - BrowserWindow, - Menu, - nativeImage, - shell, - TouchBar, - Tray, -} from 'electron' -import fs from 'fs' -import path from 'path' - -const URL = 'https://devhubapp.com' -const WIDTH = 350 -const HEIGHT = 450 - -let mainWindow: Electron.BrowserWindow -let menubarWindow: Electron.BrowserWindow -let tray: Electron.Tray - -const template: Electron.MenuItemConstructorOptions[] = [ - { - label: 'Edit', - submenu: [ - { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', - role: 'undo', - }, - { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', - role: 'redo', - }, - { - type: 'separator', - }, - { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', - role: 'cut', - }, - { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', - role: 'copy', - }, - { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', - role: 'paste', - }, - { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', - role: 'selectall', - }, - ], - }, - { - label: 'View', - submenu: [ - { - label: 'Reload', - accelerator: 'CmdOrCtrl+R', - }, - ], - }, - { - label: 'Window', - role: 'window', - submenu: [ - { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', - role: 'minimize', - }, - { - label: 'Close', - accelerator: 'CmdOrCtrl+W', - role: 'close', - }, - ], - }, - { - type: 'separator', - }, - { - label: 'Bring All to Front', - role: 'front', - }, - { - label: 'Help', - role: 'help', - submenu: [ - { - label: 'Learn More', - click: () => { - shell.openExternal('https://devhubapp.com') - }, - }, - ], - }, -] - -if (process.platform === 'darwin') { - const name = app.getName() - template.unshift({ - label: name, - submenu: [ - { - label: `About ${name}`, - role: 'about', - }, - { - type: 'separator', - }, - { - label: `Hide ${name}`, - accelerator: 'Command+H', - role: 'hide', - }, - { - label: 'Hide Others', - accelerator: 'Command+Alt+H', - role: 'hideothers', - }, - { - type: 'separator', - }, - { - label: 'Quit', - accelerator: 'Command+Q', - click: () => { - app.quit() - }, - }, - ], - }) -} - -function createWindow() { - // Create a new window - mainWindow = new BrowserWindow({ - minWidth: constants.MIN_COLUMN_WIDTH, - width: WIDTH, - height: HEIGHT, - // Don't show the window until it ready, this prevents any white flickering - show: false, - }) - - mainWindow.loadURL(URL) - - // Emitted when the window is closed. - mainWindow.on('closed', () => { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - mainWindow.destroy() - }) - - // Show window when page is ready - mainWindow.once('ready-to-show', () => { - mainWindow.show() - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) - if (process.platform === 'darwin') { - const spin = new TouchBar.TouchBarButton({ - label: 'devhub', - }) - - const touchBar = new TouchBar({ - items: [spin], - }) - mainWindow.setTouchBar(touchBar) - } - }) - - mainWindow.on('move', () => { - const [x, y] = mainWindow.getPosition() - if (y <= 100) { - mainWindow.hide() - showMenubarWindow() - } - }) -} - -function createMenubarWindow() { - menubarWindow = new BrowserWindow({ - minWidth: constants.MIN_COLUMN_WIDTH, - width: WIDTH, - height: HEIGHT, - show: false, - frame: false, - fullscreenable: false, - movable: true, - hasShadow: false, - transparent: true, - webPreferences: { - backgroundThrottling: false, - }, - }) - menubarWindow.loadURL(URL) - const webContents = menubarWindow.webContents - - webContents.on('dom-ready', () => { - const position = getWindowPosition() - setWindowPosition(menubarWindow, position.x, position.y) - webContents.insertCSS( - fs.readFileSync( - path.join(__dirname, '../public/static/css/arrow.css'), - 'utf8', - ), - ) - menubarWindow.show() - menubarWindow.focus() - }) - - // Hide the window when it loses focus - menubarWindow.on('blur', () => { - menubarWindow.hide() - }) - - menubarWindow.on('move', () => { - const [x, y] = menubarWindow.getPosition() - if (y <= 100) { - const position = getWindowPosition() - menubarWindow.setPosition(position.x, position.y, false) - } else { - menubarWindow.hide() - showMainWindow(x, y) - } - }) -} - -function createTray() { - const trayIcon = nativeImage.createFromPath( - `${path.join(__dirname, '../public/static/media/logo-transparent@2x.png')}`, - ) - tray = new Tray( - trayIcon.resize({ - width: 22, - height: 22, - }), - ) - - const trayMenuTemplate = [ - { - label: 'Devhub', - click() { - menubarWindow.hide() - toggleMainWindow() - }, - }, - { - label: 'Quit', - click() { - app.quit() - }, - }, - ] - tray.setToolTip(app.getName()) - - tray.on('click', () => { - toggleMenubarWindow() - }) - - tray.on('right-click', () => { - const contextMenu = Menu.buildFromTemplate(trayMenuTemplate) - tray.popUpContextMenu(contextMenu) - }) -} - -// Wait until the app is ready -app.on('ready', () => { - app.setAsDefaultProtocolClient('x-devhub-client') - createTray() - createMenubarWindow() - if (process.platform === 'darwin') { - app.setAboutPanelOptions({ - applicationName: 'devhub', - applicationVersion: app.getVersion(), - copyright: 'Copyright 2018', - credits: 'devhub', - }) - } -}) - -// window-all-closed -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } -}) - -// activate -app.on('activate', () => { - if (menubarWindow === null) { - createTray() - } -}) - -// web-contents-created -app.on('web-contents-created', (event, webContents) => { - webContents.on('new-window', (e, url) => { - if (!url.includes('https://api.devhubapp.com/oauth')) { - e.preventDefault() - shell.openExternal(url) - } - }) -}) - -function getWindowPosition() { - const windowBounds = menubarWindow.getBounds() - const trayBounds = tray.getBounds() - - // Center window horizontally below the tray icon - const x = Math.round( - trayBounds.x + trayBounds.width / 2 - windowBounds.width / 2, - ) - - // Position window 4 pixels vertically below the tray icon - const y = Math.round(trayBounds.y + trayBounds.height + 4) - - return { x, y } -} - -// toggle menubar window -function toggleMenubarWindow() { - if (menubarWindow.isVisible()) { - menubarWindow.hide() - } else { - showMenubarWindow() - } -} - -// toggle main window -function toggleMainWindow() { - if (mainWindow === undefined) { - createWindow() - } else if (mainWindow.isVisible()) { - mainWindow.hide() - } else { - mainWindow.show() - } -} - -function showMainWindow(x: number, y: number) { - if (mainWindow === undefined) { - createWindow() - } else { - mainWindow.show() - mainWindow.focus() - } - setWindowPosition(mainWindow, x, y) -} - -function showMenubarWindow() { - const position = getWindowPosition() - menubarWindow.setPosition(position.x, position.y, false) - setWindowPosition(menubarWindow, position.x, position.y) - menubarWindow.show() - menubarWindow.focus() -} - -function setWindowPosition( - window: Electron.BrowserWindow, - x: number, - y: number, -): void { - window.setPosition(x, y) -} diff --git a/packages/desktop/src/index.ts b/packages/desktop/src/index.ts new file mode 100644 index 000000000..bf1360619 --- /dev/null +++ b/packages/desktop/src/index.ts @@ -0,0 +1,815 @@ +import { + app, + BrowserWindow, + Menu, + nativeImage, + screen, + shell, + TouchBar, + Tray, +} from 'electron' +import Store from 'electron-store' +import { autoUpdater } from 'electron-updater' +import path from 'path' + +import { __DEV__ } from './libs/electron-is-dev' +import { WindowState, windowStateKeeper } from './libs/electron-window-state' + +const config = new Store({ + defaults: { + isMenuBarMode: false, + launchCount: 0, + lockOnCenter: false, + }, +}) + +const frameIsDifferentBetweenModes = process.platform !== 'darwin' + +const dock: Electron.Dock | null = app.dock || null +let mainWindow: Electron.BrowserWindow +let tray: Electron.Tray | null = null + +let mainWindowState: WindowState +let menubarWindowState: WindowState + +const startURL = __DEV__ + ? 'http://localhost:3000' + : `file://${path.join(__dirname, 'web/index.html')}` + +function setupBrowserExtensions() { + const { + default: installExtension, + REACT_DEVELOPER_TOOLS, + REDUX_DEVTOOLS, + } = require('electron-devtools-installer') // tslint:disable-line no-var-requires + + installExtension(REACT_DEVELOPER_TOOLS).catch(console.error) + installExtension(REDUX_DEVTOOLS).catch(console.error) +} + +function getBrowserWindowOptions() { + if (!mainWindowState) { + mainWindowState = windowStateKeeper({ + defaultWidth: screen.getPrimaryDisplay().workAreaSize.width, + defaultHeight: screen.getPrimaryDisplay().workAreaSize.height, + file: 'main-window.json', + }) + } + + if (!menubarWindowState) { + menubarWindowState = windowStateKeeper({ + defaultWidth: 340, + defaultHeight: 550, + file: 'menubar-window.json', + }) + } + + const options: Electron.BrowserWindowConstructorOptions = { + minWidth: 320, + minHeight: 450, + backgroundColor: '#292c33', + darkTheme: true, + fullscreenable: true, + resizable: true, + show: true, + title: 'DevHub', + webPreferences: { + affinity: 'main-window', + backgroundThrottling: false, + contextIsolation: false, + nativeWindowOpen: true, + nodeIntegration: false, + preload: path.join(__dirname, 'preload.js'), + }, + ...(config.get('isMenuBarMode') + ? { + x: menubarWindowState.x, + y: menubarWindowState.y, + width: menubarWindowState.width, + height: menubarWindowState.height, + alwaysOnTop: true, + center: false, + frame: false, + maxWidth: screen.getPrimaryDisplay().workAreaSize.width * 0.8, + maxHeight: screen.getPrimaryDisplay().workAreaSize.height * 0.8, + movable: false, + skipTaskbar: true, + } + : { + x: mainWindowState.x, + y: mainWindowState.y, + width: mainWindowState.width, + height: mainWindowState.height, + alwaysOnTop: false, + center: true, + frame: frameIsDifferentBetweenModes, + maxWidth: undefined, + maxHeight: undefined, + movable: !config.get('lockOnCenter'), + skipTaskbar: false, + }), + } + + return options +} + +function showWindow() { + if (mainWindow.isMinimized()) mainWindow.restore() + if (mainWindow.isVisible()) mainWindow.focus() + else mainWindow.show() +} + +function createWindow() { + const win = new BrowserWindow(getBrowserWindowOptions()) + + win.loadURL(startURL) + + win.once('ready-to-show', () => { + win.show() + }) + + win.on('show', () => { + updateTrayHightlightMode() + updateBrowserWindowOptions() + }) + + win.on('hide', () => { + if (tray) tray.setHighlightMode('selection') + }) + + win.on('closed', () => { + win.destroy() + }) + + win.on('resize', () => { + if (config.get('isMenuBarMode')) { + alignWindowWithTray() + } else if (config.get('lockOnCenter')) { + win.center() + } + }) + + win.on('blur', () => { + setTimeout(() => { + if (config.get('isMenuBarMode')) win.hide() + }, 200) + }) + + win.on('enter-full-screen', () => { + if (dock) dock.show() + }) + + win.on('leave-full-screen', () => { + update() + }) + + return win +} + +function showTrayContextPopup() { + tray!.popUpContextMenu(getTrayContextMenu()) +} + +function createTray() { + const trayIcon = nativeImage.createFromPath( + path.join( + __dirname, + `../assets/icons/${ + process.platform === 'win32' ? 'trayIconWhite' : 'trayIconTemplate' + }.png`, + ), + ) + + if (tray && !tray.isDestroyed()) tray.destroy() + + tray = new Tray(trayIcon) + + tray.on('click', () => { + if (mainWindow.isFullScreen()) { + showTrayContextPopup() + return + } + + if (mainWindow.isVisible() && !mainWindow.isMinimized()) { + if (mainWindow.isFocused() || process.platform !== 'darwin') { + if (config.get('isMenuBarMode')) { + mainWindow.hide() + } else { + showTrayContextPopup() + } + + return + } + } + + showWindow() + }) + + tray.on('right-click', () => { + showTrayContextPopup() + }) +} + +function init() { + app.setName('DevHub') + + const gotTheLock = app.requestSingleInstanceLock() + if (!gotTheLock) { + app.quit() + return + } + + app.on('second-instance', (event, argv, _workingDirectory) => { + if (!mainWindow) return + + showWindow() + + app.emit('open-url', event, __DEV__ ? argv[2] : argv[1]) + }) + + app.on('ready', () => { + config.set('launchCount', config.get('launchCount', 0) + 1) + + app.removeAsDefaultProtocolClient('devhub') + + if (__DEV__ && process.platform === 'win32') { + app.setAsDefaultProtocolClient('devhub', process.execPath, [ + path.resolve(process.argv[1]), + ]) + } else { + app.setAsDefaultProtocolClient('devhub') + } + + createTray() + if (!mainWindow) mainWindow = createWindow() + update() + + if (process.platform === 'darwin') { + app.setAboutPanelOptions({ + applicationName: 'DevHub', + applicationVersion: app.getVersion(), + copyright: 'Copyright 2018', + credits: 'devhub', + }) + } + + if (__DEV__) setupBrowserExtensions() + + autoUpdater.checkForUpdatesAndNotify() + }) + + app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } + }) + + app.on('activate', () => { + if (!mainWindow) mainWindow = createWindow() + + mainWindow.show() + }) + + app.on('web-contents-created', (_event, webContents) => { + webContents.on( + 'new-window', + (event, uri, _frameName, _disposition, _options) => { + event.preventDefault() + shell.openExternal(uri) + }, + ) + }) + + app.on('open-url', (_event, url) => { + if (!mainWindow) return + + mainWindow.webContents.send('open-url', url) + showWindow() + }) +} + +function getCenterPosition(obj: Electron.BrowserWindow | Electron.Tray) { + const bounds = obj.getBounds() + + const x = Math.round(bounds.x + bounds.width / 2) + const y = Math.round(bounds.y + bounds.height / 2) + + return { x, y } +} + +function alignWindowWithTray() { + if (!(tray && !tray.isDestroyed())) return + + const screenSize = screen.getPrimaryDisplay().size + const workArea = screen.getPrimaryDisplay().workArea + const windowBounds = mainWindow.getBounds() + const trayBounds = tray.getBounds() + const trayCenter = getCenterPosition(tray) + + const top = trayBounds.y < screenSize.height / 3 + const bottom = screenSize.height - trayBounds.y < screenSize.height / 3 + const left = trayBounds.x < screenSize.width / 3 + const right = screenSize.width - trayBounds.x < screenSize.width / 3 + + let x: number + let y: number + const spacing = 8 + + if (top) { + y = Math.round(trayCenter.y) + } else if (bottom) { + y = Math.round(trayCenter.y - windowBounds.height / 2) + } else { + y = Math.round(trayCenter.y - windowBounds.height / 2) + } + + if (left) { + x = Math.round(trayCenter.x) + } else if (right) { + x = Math.round(trayCenter.x - windowBounds.width / 2) + } else { + x = Math.round(trayCenter.x - windowBounds.width / 2) + } + + const fixedX = Math.max( + workArea.x + spacing, + Math.min(x, workArea.x + workArea.width - windowBounds.width - spacing), + ) + const fixedY = Math.max( + workArea.y + spacing, + Math.min(y, workArea.y + workArea.height - windowBounds.height - spacing), + ) + + mainWindow.setPosition(fixedX, fixedY) +} + +function getAboutMenuItems() { + const menuItems: Electron.MenuItemConstructorOptions[] = [ + ...(process.platform === 'darwin' + ? [ + { + label: `About ${app.getName()}`, + role: 'about', + }, + ] + : []), + { + label: 'View on GitHub', + click: () => { + shell.openExternal('https://github.com/devhubapp/devhub') + }, + }, + { + type: 'separator', + }, + { + label: 'Check for Updates...', + enabled: !__DEV__, + click: () => { + autoUpdater.checkForUpdatesAndNotify() + }, + }, + ] + + return menuItems +} + +function getModeMenuItems() { + const isCurrentWindow = mainWindow.isVisible() && !mainWindow.isMinimized() + const enabled = isCurrentWindow || config.get('isMenuBarMode') + + const menuItems: Electron.MenuItemConstructorOptions[] = [ + { + type: 'radio', + label: 'Desktop mode', + checked: !config.get('isMenuBarMode'), + enabled, + click() { + enableDesktopMode() + }, + }, + { + type: 'radio', + label: 'Menubar mode', + checked: !!config.get('isMenuBarMode'), + enabled, + click() { + enableMenuBarMode() + }, + }, + ] + + return menuItems +} + +function getOptionsMenuItems() { + const isCurrentWindow = mainWindow.isVisible() && !mainWindow.isMinimized() + const enabled = isCurrentWindow || config.get('isMenuBarMode') + + const menuItems: Electron.MenuItemConstructorOptions[] = [ + { + type: 'checkbox', + label: 'Lock on center', + checked: config.get('lockOnCenter'), + enabled, + visible: !config.get('isMenuBarMode'), + click(item) { + config.set('lockOnCenter', item.checked) + + if (item.checked) { + if (!config.get('isMenuBarMode')) { + mainWindow.setMovable(false) + } + + mainWindow.center() + } else { + if (!config.get('isMenuBarMode')) { + mainWindow.setMovable(getBrowserWindowOptions().movable !== false) + } + } + }, + }, + ] + + return menuItems +} + +function getMainMenuItems() { + const isCurrentWindow = mainWindow.isVisible() && !mainWindow.isMinimized() + const enabled = isCurrentWindow || config.get('isMenuBarMode') + + const menuItems: Electron.MenuItemConstructorOptions[] = [ + ...(process.platform === 'darwin' + ? ([ + { + label: app.getName(), + submenu: [ + ...getAboutMenuItems(), + { + type: 'separator', + }, + { + label: `Hide ${app.getName()}`, + accelerator: 'Command+H', + role: 'hide', + }, + { + label: 'Hide Others', + accelerator: 'Command+Alt+H', + role: 'hideothers', + }, + { + type: 'separator', + }, + { + label: 'Quit', + role: 'quit', + click: () => { + app.quit() + }, + }, + ], + }, + ] as Electron.MenuItemConstructorOptions[]) + : []), + { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + role: 'undo', + }, + { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + role: 'redo', + }, + { + type: 'separator', + }, + { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + role: 'cut', + }, + { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + role: 'copy', + }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + role: 'paste', + }, + { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + role: 'selectall', + }, + ], + }, + { + label: 'View', + submenu: [ + { + label: 'Reload', + accelerator: 'CmdOrCtrl+R', + click(_, focusedWindow) { + if (focusedWindow) focusedWindow.reload() + }, + }, + { + label: 'Toggle Developer Tools', + accelerator: __DEV__ + ? process.platform === 'darwin' + ? 'Alt+Command+I' + : 'Ctrl+Shift+I' + : undefined, + visible: __DEV__, + click(_, focusedWindow) { + if (focusedWindow) focusedWindow.webContents.toggleDevTools() + }, + }, + { + type: 'separator', + enabled, + }, + ...getModeMenuItems(), + { + type: 'separator', + enabled, + }, + ...getOptionsMenuItems(), + ], + }, + { + label: 'Window', + role: 'window', + submenu: getWindowMenuItems(), + }, + { + label: 'Help', + role: 'help', + submenu: [ + ...(process.platform === 'darwin' + ? [] + : ([ + ...getAboutMenuItems(), + { + type: 'separator', + }, + ] as Electron.MenuItemConstructorOptions[])), + { + label: 'Report bug', + click: () => { + shell.openExternal('https://github.com/devhubapp/devhub/issues/new') + }, + }, + { + label: 'Send feedback', + click: () => { + shell.openExternal('https://github.com/devhubapp/devhub/issues/new') + }, + }, + ], + }, + ] + + return menuItems +} + +function getDockMenuItems() { + return getModeMenuItems() +} + +function getWindowMenuItems() { + const isCurrentWindow = mainWindow.isVisible() && !mainWindow.isMinimized() + const enabled = isCurrentWindow || config.get('isMenuBarMode') + + const menuItems: Electron.MenuItemConstructorOptions[] = [ + { + label: 'Close', + accelerator: 'CmdOrCtrl+W', + click() { + mainWindow.hide() + }, + }, + { + label: 'Minimize', + accelerator: config.get('isMenuBarMode') ? undefined : 'CmdOrCtrl+M', + role: config.get('isMenuBarMode') ? undefined : 'minimize', + enabled: enabled && !mainWindow.isMinimized(), + visible: !config.get('isMenuBarMode'), // && mainWindow.isMinimizable(), + }, + { + label: 'Maximize', + visible: !config.get('isMenuBarMode'), // && mainWindow.isMaximizable(), + enabled, // && !mainWindow.isMaximized(), + click() { + showWindow() + mainWindow.maximize() + }, + }, + { + type: 'checkbox', + label: 'Full Screen', + accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', + checked: mainWindow.isFullScreen(), + enabled: enabled || mainWindow.isFullScreen(), + click(item) { + mainWindow.setFullScreen(item.checked) + }, + }, + ] + + return menuItems +} + +function getTrayMenuItems() { + const isCurrentWindow = mainWindow.isVisible() && !mainWindow.isMinimized() + const enabled = isCurrentWindow || config.get('isMenuBarMode') + + const menuItems: Electron.MenuItemConstructorOptions[] = [ + { + label: 'Open', + visible: !isCurrentWindow || config.get('isMenuBarMode'), + click() { + showWindow() + }, + }, + { + type: 'separator', + visible: !isCurrentWindow || config.get('isMenuBarMode'), + }, + ...getModeMenuItems(), + { + type: 'separator', + enabled, + }, + ...getOptionsMenuItems(), + { + type: 'separator', + enabled, + visible: !config.get('isMenuBarMode'), + }, + ...getWindowMenuItems().filter(item => item.label !== 'Close'), + { + type: 'separator', + enabled, + }, + { + label: 'Quit', + accelerator: 'CmdOrCtrl+Q', + role: 'quit', + }, + ] + + return menuItems +} + +function getTrayContextMenu() { + return Menu.buildFromTemplate(getTrayMenuItems()) +} + +function updateTrayHightlightMode() { + if (!(tray && !tray.isDestroyed())) return + + tray.setHighlightMode( + config.get('isMenuBarMode') && + mainWindow.isVisible() && + mainWindow.isFocused() && + !mainWindow.isFullScreen() + ? 'always' + : 'selection', + ) +} + +function updateBrowserWindowOptions() { + const options = getBrowserWindowOptions() + + const maximize = + !config.get('isMenuBarMode') && + (mainWindow.isMaximized() || + mainWindowState.isMaximized || + config.get('launchCount') === 1) + + mainWindow.setAlwaysOnTop(options.alwaysOnTop === true) + + mainWindow.setMinimumSize( + Math.floor(options.minWidth || 0), + Math.floor(options.minHeight || 0), + ) + + mainWindow.setMaximumSize( + Math.ceil( + options.maxWidth || + (process.platform === 'darwin' + ? screen.getPrimaryDisplay().workAreaSize.width + : 0), + ), + Math.ceil( + options.maxHeight || + (process.platform === 'darwin' + ? screen.getPrimaryDisplay().workAreaSize.height + : 0), + ), + ) + + mainWindow.setMovable(options.movable !== false) + + mainWindow.setPosition( + maximize ? screen.getPrimaryDisplay().workArea.x || 0 : options.x || 0, + maximize ? screen.getPrimaryDisplay().workArea.y || 0 : options.y || 0, + false, + ) + + mainWindow.setSkipTaskbar(options.skipTaskbar === true) + + mainWindow.setSize( + maximize + ? screen.getPrimaryDisplay().workAreaSize.width + : options.width || 500, + maximize + ? screen.getPrimaryDisplay().workAreaSize.height + : options.height || 500, + false, + ) + + if (dock) { + if (options.skipTaskbar === true) { + dock.hide() + } else { + dock.show() + } + } + + mainWindowState.unmanage() + menubarWindowState.unmanage() + if (config.get('isMenuBarMode')) { + menubarWindowState.manage(mainWindow) + } else { + mainWindowState.manage(mainWindow) + } + + if (config.get('isMenuBarMode')) { + alignWindowWithTray() + } else { + if (config.get('lockOnCenter')) { + mainWindow.center() + } + + if (maximize) { + mainWindow.maximize() + } + } +} + +function updateMenu() { + Menu.setApplicationMenu(Menu.buildFromTemplate(getMainMenuItems())) + + if (process.platform === 'darwin') { + const touchBar = new TouchBar({ + items: [], + }) + + mainWindow.setTouchBar(touchBar) + } + + if (dock) dock.setMenu(Menu.buildFromTemplate(getDockMenuItems())) +} + +function update() { + if (mainWindow.isFullScreen()) { + mainWindow.setFullScreen(false) + return + } + + showWindow() + updateMenu() + updateTrayHightlightMode() + updateBrowserWindowOptions() +} + +function updateOrRecreateWindow() { + if (frameIsDifferentBetweenModes) { + const oldWindow = mainWindow + mainWindow = createWindow() + oldWindow.close() + } + + update() +} + +function enableDesktopMode() { + config.set('isMenuBarMode', false) + updateOrRecreateWindow() +} + +function enableMenuBarMode() { + config.set('isMenuBarMode', true) + updateOrRecreateWindow() +} + +init() diff --git a/packages/desktop/src/libs/electron-is-dev/index.ts b/packages/desktop/src/libs/electron-is-dev/index.ts new file mode 100644 index 000000000..9267d0e3a --- /dev/null +++ b/packages/desktop/src/libs/electron-is-dev/index.ts @@ -0,0 +1,12 @@ +// Source: https://github.com/sindresorhus/electron-is-dev + +import electron from 'electron' + +const app = electron.app || electron.remote.app + +const isEnvSet = 'ELECTRON_IS_DEV' in process.env +const getFromEnv = + !!process.env.ELECTRON_IS_DEV && + parseInt(process.env.ELECTRON_IS_DEV, 10) === 1 + +export const __DEV__ = isEnvSet ? getFromEnv : !app.isPackaged diff --git a/packages/desktop/src/libs/electron-window-state/index.ts b/packages/desktop/src/libs/electron-window-state/index.ts new file mode 100644 index 000000000..356bf12c6 --- /dev/null +++ b/packages/desktop/src/libs/electron-window-state/index.ts @@ -0,0 +1,271 @@ +// Modified version of: https://github.com/mawie81/electron-window-state + +import electron from 'electron' +import jsonfile from 'jsonfile' +import mkdirp from 'mkdirp' +import path from 'path' + +export interface WindowStateOptions { + /** The height that should be returned if no file exists yet. Defaults to `600`. */ + defaultHeight?: number + /** The width that should be returned if no file exists yet. Defaults to `800`. */ + defaultWidth?: number + /** Should we automatically restore the window to full screen, if it was last closed full screen. Defaults to `true`. */ + fullScreen?: boolean + /** The path where the state file should be written to. Defaults to `app.getPath('userData')`. */ + path?: string + /** The name of file. Defaults to `window-state.json`. */ + file?: string + /** Should we automatically maximize the window, if it was last closed maximized. Defaults to `true`. */ + maximize?: boolean +} + +export interface WindowInternalState { + displayBounds: { + height: number + width: number + } + /** The saved height of loaded state. `defaultHeight` if the state has not been saved yet. */ + height: number + /** true if the window state was saved while the window was in full screen mode. `undefined` if the state has not been saved yet. */ + isFullScreen: boolean + /** `true` if the window state was saved while the window was maximized. `undefined` if the state has not been saved yet. */ + isMaximized: boolean + /** The saved width of loaded state. `defaultWidth` if the state has not been saved yet. */ + width: number + /** The saved x coordinate of the loaded state. `undefined` if the state has not been saved yet. */ + x: number + /** The saved y coordinate of the loaded state. `undefined` if the state has not been saved yet. */ + y: number +} + +export interface WindowState extends WindowInternalState { + /** Register listeners on the given `BrowserWindow` for events that are related to size or position changes (resize, move). It will also restore the window's maximized or full screen state. When the window is closed we automatically remove the listeners and save the state. */ + manage: (window: Electron.BrowserWindow) => void + /** Removes all listeners of the managed `BrowserWindow` in case it does not need to be managed anymore. */ + unmanage: () => void +} + +export function windowStateKeeper(options: WindowStateOptions): WindowState { + const app = electron.app || electron.remote.app + const screen = electron.screen || electron.remote.screen + + let state: WindowInternalState + let winRef: Electron.BrowserWindow | null = null + let updateStateTimer: number + let saveStateTimer: number + + const updateStateDelay = 100 + const saveStateDelay = 500 + + const config = Object.assign( + { + file: 'window-state.json', + path: app.getPath('userData'), + maximize: true, + fullScreen: true, + }, + options, + ) + + const fullStoreFileName = path.join(config.path, config.file) + + function isNormal(win: Electron.BrowserWindow) { + return !win.isMaximized() && !win.isMinimized() && !win.isFullScreen() + } + + function hasBounds() { + return ( + state && + Number.isInteger(state.x) && + Number.isInteger(state.y) && + Number.isInteger(state.width) && + state.width > 0 && + Number.isInteger(state.height) && + state.height > 0 + ) + } + + function resetStateToDefault() { + const displayBounds = screen.getPrimaryDisplay().bounds + + // Reset state to default values on the primary display + state = { + displayBounds, + x: 0, + y: 0, + width: config.defaultWidth || 800, + height: config.defaultHeight || 600, + isFullScreen: false, + isMaximized: false, + } + } + + function windowWithinBounds(bounds: Electron.Rectangle) { + return ( + state.x >= bounds.x && + state.y >= bounds.y && + state.x + state.width <= bounds.x + bounds.width && + state.y + state.height <= bounds.y + bounds.height + ) + } + + function ensureWindowVisibleOnSomeDisplay() { + const visible = screen.getAllDisplays().some(display => { + return windowWithinBounds(display.bounds) + }) + + if (!visible) { + // Window is partially or fully not visible now. + // Reset it to safe defaults. + return resetStateToDefault() + } + } + + function validateState() { + const isValid = + state && (hasBounds() || state.isMaximized || state.isFullScreen) + + if (!isValid) { + resetStateToDefault() + return + } + + if (hasBounds() && state.displayBounds) { + ensureWindowVisibleOnSomeDisplay() + } + } + + function updateState(_win?: Electron.BrowserWindow) { + const win = _win || winRef + + if (!win) { + return + } + + // Don't throw an error when window was closed + try { + const winBounds = win.getBounds() + + if (isNormal(win)) { + state.x = winBounds.x + state.y = winBounds.y + state.width = winBounds.width + state.height = winBounds.height + } + + state.isMaximized = win.isMaximized() + state.isFullScreen = win.isFullScreen() + state.displayBounds = screen.getDisplayMatching(winBounds).bounds + } catch (err) { + // + } + } + + function saveState(win?: Electron.BrowserWindow) { + // Update window state only if it was provided + if (win) { + updateState(win) + } + + // Save state + try { + mkdirp.sync(path.dirname(fullStoreFileName)) + jsonfile.writeFileSync(fullStoreFileName, state) + } catch (err) { + // + } + } + + function stateChangeHandler() { + // Handles both 'resize' and 'move' + clearTimeout(updateStateTimer) + clearTimeout(saveStateTimer) + + updateStateTimer = setTimeout(updateState, updateStateDelay) + saveStateTimer = setTimeout(saveState, saveStateDelay) + } + + function closeHandler() { + updateState() + } + + function closedHandler() { + // Unregister listeners and save state + unmanage() + saveState() + } + + function manage(win: Electron.BrowserWindow) { + winRef = win + + if (config.maximize && state.isMaximized) { + win.maximize() + } + + if (config.fullScreen && state.isFullScreen) { + win.setFullScreen(true) + } + + win.on('resize', stateChangeHandler) + win.on('move', stateChangeHandler) + win.on('close', closeHandler) + win.on('closed', closedHandler) + } + + function unmanage() { + if (winRef) { + clearTimeout(updateStateTimer) + winRef.removeListener('resize', stateChangeHandler) + winRef.removeListener('move', stateChangeHandler) + winRef.removeListener('close', closeHandler) + winRef.removeListener('closed', closedHandler) + winRef = null + } + } + + // Load previous state + try { + state = jsonfile.readFileSync(fullStoreFileName) + } catch (err) { + // + } + + // Check state validity + validateState() + + // Set state fallback values + state = Object.assign( + { + width: config.defaultWidth || 800, + height: config.defaultHeight || 600, + }, + state!, + ) + + return { + get x() { + return state.x + }, + get y() { + return state.y + }, + get width() { + return state.width + }, + get height() { + return state.height + }, + get displayBounds() { + return state.displayBounds + }, + get isMaximized() { + return state.isMaximized + }, + get isFullScreen() { + return state.isFullScreen + }, + manage, + unmanage, + } +} diff --git a/packages/desktop/src/preload.ts b/packages/desktop/src/preload.ts new file mode 100644 index 000000000..e2ff13faa --- /dev/null +++ b/packages/desktop/src/preload.ts @@ -0,0 +1,5 @@ +import electron from 'electron' + +// Communication between webapp and electron main process +// Used on oauth flow +window.ipc = electron.ipcRenderer diff --git a/packages/desktop/tsconfig.json b/packages/desktop/tsconfig.json index efbde400d..c71475f3b 100644 --- a/packages/desktop/tsconfig.json +++ b/packages/desktop/tsconfig.json @@ -1,17 +1,27 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "target": "es2015", - "module": "commonjs", "allowJs": false, - "composite": true, - "declaration": true, + "isolatedModules": true, + "module": "commonjs", "noEmit": false, - "outDir": "dist/", + "outDir": "dist", "rootDir": "src", + "target": "es2015", "typeRoots": [ "../../@types", "../../node_modules/@types" ] - } + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../core" + }, + { + "path": "../components" + } + ] } diff --git a/packages/mobile/android/app/src/main/ic_launcher-web.png b/packages/mobile/android/app/src/main/ic_launcher-web.png index 619b3d98f..45f50f257 100644 Binary files a/packages/mobile/android/app/src/main/ic_launcher-web.png and b/packages/mobile/android/app/src/main/ic_launcher-web.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09bc5..4ae7d1237 100644 --- a/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc5..4ae7d1237 100644 --- a/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/packages/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 8e97e08c6..f64d9d5c9 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..94819dca0 Binary files /dev/null and b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index b771d1fd3..7998e2e47 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index c56dad175..3dddd6b75 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/packages/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 4d995f6cf..bdef8d1d1 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..490517266 Binary files /dev/null and b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index b14f09801..3322bfd43 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index a99b1d039..2a3c59185 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/packages/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 7d8dec624..c7de9c618 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..9d9fed24c Binary files /dev/null and b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index 9a6371483..2049ec5dd 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index fa986d480..e525486b8 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/packages/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 402ee0147..e4dc919b1 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..10642d1ae Binary files /dev/null and b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 87754e15e..90e5e8519 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index cc84d203a..526ec4ea9 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/packages/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 0fa4a32b6..58cb4193a 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..cb7844c24 Binary files /dev/null and b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 4be1cb9a9..e00392193 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 19ef9c7dc..a3a4414f8 100644 Binary files a/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/packages/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/packages/mobile/assets/logo-background.png b/packages/mobile/assets/logo-background.png new file mode 100644 index 000000000..b8b71991b Binary files /dev/null and b/packages/mobile/assets/logo-background.png differ diff --git a/packages/mobile/assets/logo-transparent.png b/packages/mobile/assets/logo-transparent.png new file mode 100644 index 000000000..4b6e31967 Binary files /dev/null and b/packages/mobile/assets/logo-transparent.png differ diff --git a/packages/mobile/assets/logo_circle.png b/packages/mobile/assets/logo_circle.png deleted file mode 100644 index cf34f5429..000000000 Binary files a/packages/mobile/assets/logo_circle.png and /dev/null differ diff --git a/packages/mobile/assets/logo_circle@2x.png b/packages/mobile/assets/logo_circle@2x.png deleted file mode 100644 index da583022c..000000000 Binary files a/packages/mobile/assets/logo_circle@2x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_circle@3x.png b/packages/mobile/assets/logo_circle@3x.png deleted file mode 100644 index d9a764867..000000000 Binary files a/packages/mobile/assets/logo_circle@3x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_rounded.png b/packages/mobile/assets/logo_rounded.png deleted file mode 100644 index e323b5e0d..000000000 Binary files a/packages/mobile/assets/logo_rounded.png and /dev/null differ diff --git a/packages/mobile/assets/logo_rounded@2x.png b/packages/mobile/assets/logo_rounded@2x.png deleted file mode 100644 index ddcfbe18b..000000000 Binary files a/packages/mobile/assets/logo_rounded@2x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_rounded@3x.png b/packages/mobile/assets/logo_rounded@3x.png deleted file mode 100644 index 55e9ba72e..000000000 Binary files a/packages/mobile/assets/logo_rounded@3x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_square.png b/packages/mobile/assets/logo_square.png deleted file mode 100644 index ed445e670..000000000 Binary files a/packages/mobile/assets/logo_square.png and /dev/null differ diff --git a/packages/mobile/assets/logo_square@2x.png b/packages/mobile/assets/logo_square@2x.png deleted file mode 100644 index d14aa88de..000000000 Binary files a/packages/mobile/assets/logo_square@2x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_square@3x.png b/packages/mobile/assets/logo_square@3x.png deleted file mode 100644 index ceb114ab6..000000000 Binary files a/packages/mobile/assets/logo_square@3x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_transparent.png b/packages/mobile/assets/logo_transparent.png deleted file mode 100644 index f84f0eb1e..000000000 Binary files a/packages/mobile/assets/logo_transparent.png and /dev/null differ diff --git a/packages/mobile/assets/logo_transparent@2x.png b/packages/mobile/assets/logo_transparent@2x.png deleted file mode 100644 index 9aaa6d9d2..000000000 Binary files a/packages/mobile/assets/logo_transparent@2x.png and /dev/null differ diff --git a/packages/mobile/assets/logo_transparent@3x.png b/packages/mobile/assets/logo_transparent@3x.png deleted file mode 100644 index d59e2233f..000000000 Binary files a/packages/mobile/assets/logo_transparent@3x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Contents.json b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Contents.json index 38134224e..8d8f4bb49 100644 --- a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Contents.json @@ -42,18 +42,6 @@ "filename" : "Icon-App-40x40@3x.png", "scale" : "3x" }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@1x.png", - "scale" : "1x" - }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@2x.png", - "scale" : "2x" - }, { "size" : "60x60", "idiom" : "iphone", @@ -102,30 +90,6 @@ "filename" : "Icon-App-40x40@2x.png", "scale" : "2x" }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@1x.png", - "scale" : "1x" - }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@2x.png", - "scale" : "2x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@1x.png", - "scale" : "1x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@2x.png", - "scale" : "2x" - }, { "size" : "76x76", "idiom" : "ipad", @@ -147,7 +111,7 @@ { "size" : "1024x1024", "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024.png", + "filename" : "ItunesArtwork@2x.png", "scale" : "1x" } ], diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png deleted file mode 100644 index 8b8d87b45..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index cbdbaa09f..adbe39a97 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index d61ae575e..f63eb803b 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 5b9486732..5d21d5b25 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 08c44f5ea..d3e1585f4 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index 0244b8efc..cce3b01cf 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 879609e9d..80bf5d216 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index d61ae575e..f63eb803b 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 6b722a08f..afbbdc057 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 253bbb5b9..d87ad5e9d 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index cb5227b31..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png deleted file mode 100644 index b7cde5ccc..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 253bbb5b9..d87ad5e9d 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 85cc11462..73ecc74c4 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index 5aa5a414d..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index 599196a92..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 4c040cd67..f6e061385 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 6486282c9..fa7319440 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 6dbd2bff7..9a852accb 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png deleted file mode 100644 index 13b4f58e0..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png deleted file mode 100644 index c7914c7c0..000000000 Binary files a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png and /dev/null differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 000000000..132f06711 Binary files /dev/null and b/packages/mobile/ios/devhub/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@1x.png b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@1x.png index 7aab7729b..2ae7b8530 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@1x.png and b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@1x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@2x.png b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@2x.png index 8b8d87b45..132f06711 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@3x.png b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@3x.png index 7522dd2a0..c2e3889d0 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/iTunesArtwork@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle.png b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle.png index cf34f5429..78a229d79 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@2x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@2x.png index da583022c..afe37abdd 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@3x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@3x.png index d9a764867..0defc1057 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_circle.imageset/logo_circle@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded.png b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded.png index e323b5e0d..c17fc1e21 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@2x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@2x.png index ddcfbe18b..838059754 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@3x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@3x.png index 55e9ba72e..5f0ce786b 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_rounded.imageset/logo_rounded@3x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square.png b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square.png index ed445e670..ce6119515 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@2x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@2x.png index d14aa88de..dbf3951c4 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@2x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@2x.png differ diff --git a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@3x.png b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@3x.png index ceb114ab6..d8b89e448 100644 Binary files a/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@3x.png and b/packages/mobile/ios/devhub/Images.xcassets/logo_square.imageset/logo_square@3x.png differ diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 79ebc8e46..82437bf97 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -4,6 +4,7 @@ "private": false, "scripts": { "clean": "cd .", + "compile": "tsc -b", "format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'", "lint": "tslint -p .", "prepare": "cd .. && yarn patch-package", diff --git a/packages/mobile/tsconfig.json b/packages/mobile/tsconfig.json index 4082f16a5..0730105c1 100644 --- a/packages/mobile/tsconfig.json +++ b/packages/mobile/tsconfig.json @@ -1,3 +1,17 @@ { - "extends": "../../tsconfig.json" + "extends": "../../tsconfig.json", + "compilerOptions": { + "typeRoots": [ + "../../@types", + "../../node_modules/@types" + ] + }, + "references": [ + { + "path": "../core" + }, + { + "path": "../components" + } + ] } diff --git a/packages/web/package.json b/packages/web/package.json index d9e6e5018..f45c58136 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,9 +2,11 @@ "name": "@devhub/web", "version": "0.40.0", "private": false, + "homepage": "./", "scripts": { - "build": "tsc -b && react-app-rewired build && rimraf dist && mv build dist", - "clean": "rimraf dist/*", + "build": "yarn compile && react-app-rewired build && shx rm -rf dist && mv build dist", + "clean": "shx rm -rf dist/*", + "compile": "tsc -b", "eject": "react-app-rewired eject", "format": "prettier --write '{.,src/**}/*.{js,jsx,ts,tsx}'", "lint": "tslint -p .", @@ -41,7 +43,7 @@ "tslint-config-airbnb": "^5.11.1", "tslint-config-prettier": "^1.17.0", "tslint-react": "^3.6.0", - "typescript": "^3.2.1", + "typescript": "^3.2.2", "webpack-bundle-analyzer": "^3.0.3" }, "resolutions": { diff --git a/packages/web/public/index.html b/packages/web/public/index.html index 2f2ccb525..0ff6d4062 100644 --- a/packages/web/public/index.html +++ b/packages/web/public/index.html @@ -3,10 +3,10 @@ - + - DevHub - TweetDeck for GitHub - + DevHub + @@ -53,14 +53,6 @@ -