Skip to content

Commit

Permalink
open source
Browse files Browse the repository at this point in the history
  • Loading branch information
Matej Lauko committed Apr 5, 2021
0 parents commit e0784a2
Show file tree
Hide file tree
Showing 106 changed files with 13,039 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# compiled output
/dist
/tmp
/out-tsc
.next/
/app

# dependencies
node_modules
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
.DS_Store
.env*
NOTES.md

# System Files
.DS_Store
Thumbs.db

40 changes: 40 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Nextron: Main",
"type": "node",
"request": "attach",
"protocol": "inspector",
"port": 9292,
"skipFiles": ["<node_internals>/**"],
"sourceMapPathOverrides": {
"webpack:///./~/*": "${workspaceFolder}/node_modules/*",
"webpack:///./*": "${workspaceFolder}/*",
"webpack:///*": "*"
}
},
{
"name": "Nextron: Renderer",
"type": "chrome",
"request": "attach",
"port": 5858,
"timeout": 10000,
"urlFilter": "http://localhost:*",
"webRoot": "${workspaceFolder}/app",
"sourceMapPathOverrides": {
"webpack:///./src/*": "${webRoot}/*"
}
}
],
"compounds": [
{
"name": "Nextron: All",
"preLaunchTask": "dev",
"configurations": ["Nextron: Main", "Nextron: Renderer"]
}
]
}
21 changes: 21 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "dev",
"isBackground": true,
"problemMatcher": {
"owner": "custom",
"pattern": {
"regexp": ""
},
"background": {
"beginsPattern": "started server",
"endsPattern": "Debugger listening on"
}
},
"label": "dev"
}
]
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Moods for Spotify
===

## The right music for your every mood

Setup you music **moods** -- Pick your carefully crafted playlist. Or discover new tunes recommended to your taste.

Then switch between them in lightning speed with minimal distractions.
95 changes: 95 additions & 0 deletions main/background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { app, BrowserWindow } from 'electron';
import log from 'electron-log';
import { BASE_URL, IS_PROD } from './constants';
import { initDeepLinks } from './links';
import { MAIN_WINDOW_OPTIONS, WINDOW_SIZES } from './setup';
import { initStore } from './store';
import {
getWindowPosition,
saveWindowState,
setupDisplayHandlers,
} from './window';

let mainWindow: BrowserWindow;

function getOrCreateMainWindow(create = true) {
if (create && (!mainWindow || BrowserWindow.getAllWindows().length === 0)) {
mainWindow = new BrowserWindow({
...MAIN_WINDOW_OPTIONS,
...getWindowPosition(),
});

mainWindow.on('close', () => saveWindowState(mainWindow));
}

return mainWindow;
}

function loadWindow(window: BrowserWindow, url: string) {
log.debug('[window] loading url', url);

if (IS_PROD) {
return window.loadURL(`${BASE_URL}${url}.html`);
} else {
return window.loadURL(`${BASE_URL}${url}`);
}
}

initDeepLinks(() => getOrCreateMainWindow(false));

async function loadApp() {
await app.whenReady();

getOrCreateMainWindow();

initStore(mainWindow);

setupDisplayHandlers(mainWindow);

await loadWindow(mainWindow, '/home');

/* Make sure window is created at activate */
app.on('activate', () => {
getOrCreateMainWindow();
});

mainWindow.webContents.setWindowOpenHandler(({ url }) => {
const urlSplit = url.split('/');
const winName = urlSplit[urlSplit.length - 1].replace('.html', '');

return {
action: 'allow',
overrideBrowserWindowOptions: {
minimizable: false,
...WINDOW_SIZES[winName],
},
};
});

if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
}

const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
log.info("[app] don't got the lock -> quiting");
app.quit();
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
log.info('[window] focusing second instance');

if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});

loadApp();
}

app.on('window-all-closed', () => {
app.quit();
});
7 changes: 7 additions & 0 deletions main/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import packageConfig from '../package.json';

export const IS_PROD: boolean = process.env.NODE_ENV === 'production';
export const PORT = process.argv[2];

export const BASE_URL = IS_PROD ? `app://.` : `http://localhost:${PORT}`;
export const APP_PROTOCOL = packageConfig.build.protocols.schemes[0];
33 changes: 33 additions & 0 deletions main/links.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { app, BrowserWindow, shell } from 'electron';
import { Deeplink } from 'electron-deeplink';
import log from 'electron-log';
import { APP_PROTOCOL, BASE_URL, IS_PROD } from './constants';

export function initDeepLinks(getWindow: () => BrowserWindow) {
const deeplink = new Deeplink({
// @ts-ignore
app,
mainWindow: getWindow(),
protocol: APP_PROTOCOL,
isDev: !IS_PROD,
debugLogging: true,
electronPath: '/node_modules/electron/dist/Electron.app',
});

deeplink.on('received', (link: string) => {
log.debug('[deep link] received', { link });
shell.beep();

if (link.includes('//callback')) {
const query = new URL(link).search;
const path = link.replace(`${APP_PROTOCOL}://`, '');
let url = IS_PROD
? `${BASE_URL}/callback.html${query}`
: `${BASE_URL}/${path}`;

log.debug('[deep link] redirecting to', { url });

getWindow().loadURL(url);
}
});
}
53 changes: 53 additions & 0 deletions main/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { app, BrowserWindowConstructorOptions } from 'electron';
import log from 'electron-log';
import serve from 'electron-serve';
import { IS_PROD } from './constants';

log.transports.console.level = 'debug';

if (IS_PROD) {
serve({ directory: 'app' });
} else {
app.setPath('userData', `${app.getPath('userData')} (development)`);
}

export const MAIN_WINDOW_OPTIONS: BrowserWindowConstructorOptions = {
visualEffectState: 'active',
frame: false,
transparent: true,
titleBarStyle: 'hidden',
autoHideMenuBar: true,
fullscreenable: false,
enableLargerThanScreen: false,
maximizable: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
nativeWindowOpen: true,
enablePreferredSizeMode: true,
contextIsolation: false,
},
};

export const WINDOW_SIZES: Record<
string,
Partial<BrowserWindowConstructorOptions>
> = {
home: {
minWidth: 400,
minHeight: 300,
width: 450,
height: 350,
},
about: {
resizable: false,
width: 350,
height: 530,
},
settings: {
minWidth: 400,
minHeight: 400,
width: 600,
height: 800,
},
};
51 changes: 51 additions & 0 deletions main/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BrowserWindow, ipcMain } from 'electron';
import log from 'electron-log';
import Store from 'electron-store';
import { Mood } from '../renderer/components/moods';

interface StoreData {
auth: {
accessToken: string | null;
refreshToken: string | null;
};
moods: Mood[];
}

const store = new Store<StoreData>({ name: 'data' });

export function initStore(mainWindow: BrowserWindow) {
// Listen to 'store-get' events from renderer
ipcMain.on('store-get', (event, key: keyof StoreData) => {
// Retrieve data from store
const data = store.get(key);

// log.debug(`[store] retrieving ${key} =>`, data);
log.debug(`[store] retrieving ${key}`);

// Return the data to the event emitter (or empty obj)
event.returnValue = data;
});

// Listen to 'store-set' events from renderer
ipcMain.on(
'store-set',
(_event, entry: [keyof StoreData, StoreData[keyof StoreData]]) => {
if (!Array.isArray(entry)) {
log.error(
'[store] setting data in wrong format, must be tuple [key, data]'
);
return;
}

const [key, data] = entry;

log.debug(`[store] setting ${key} =>`, data);

// Save data to store under key
store.set(key, data);

// Emit 'store-saved' event to the main window - to update moods in player
mainWindow.webContents.send('store-saved', entry);
}
);
}
Loading

0 comments on commit e0784a2

Please sign in to comment.