Commit d7f073d5 authored by akiraohgaki's avatar akiraohgaki

Merge branch 'develop'

parents 998a9805 d4a06249
Pipeline #307 passed with stage
in 5 minutes and 20 seconds
......@@ -13,17 +13,16 @@ const isDebugMode = process.argv.includes('--debug');
const previewpicDirectory = `${app.getPath('userData')}/previewpic`;
const windowIcon = `${__dirname}/images/app-icons/ocs-store.png`;
const indexFileUrl = `file://${__dirname}/index.html`;
const storeAppConfigStorage = 'application';
const appConfigStoreStorage = 'application';
let topWindow = null;
let mainWindow = null;
let ocsManager = null;
let ocsManagerUrl = '';
async function startOcsManager() {
return new Promise((resolve) => {
const resolveUrl = (data) => {
const matches = data.toString()
.match(/Websocket server started at: "(wss?:\/\/.+)"/);
const resolveOcsManagerUrl = (data) => {
const matches = data.toString().match(/Websocket server started at: "(wss?:\/\/.+)"/);
if (matches) {
ocsManagerUrl = matches[1];
resolve(true);
......@@ -35,14 +34,14 @@ async function startOcsManager() {
ocsManager.stdout.on('data', (data) => {
console.log(`[${ocsManagerConfig.bin}] ${data}`);
if (!ocsManagerUrl) {
resolveUrl(data);
resolveOcsManagerUrl(data);
}
});
ocsManager.stderr.on('data', (data) => {
console.error(`[${ocsManagerConfig.bin}] ${data}`);
if (!ocsManagerUrl) {
resolveUrl(data);
resolveOcsManagerUrl(data);
}
});
......@@ -66,13 +65,13 @@ function stopOcsManager() {
function createWindow() {
const appConfigStore = new ElectronStore({
name: storeAppConfigStorage,
name: appConfigStoreStorage,
defaults: appConfig.defaults
});
const windowBounds = appConfigStore.get('windowBounds');
topWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
title: appPackage.productName,
icon: windowIcon,
x: windowBounds.x,
......@@ -85,22 +84,22 @@ function createWindow() {
});
if (!isDebugMode) {
topWindow.setMenu(null);
mainWindow.setMenu(null);
}
topWindow.loadURL(indexFileUrl);
mainWindow.loadURL(indexFileUrl);
topWindow.on('close', () => {
const appConfigStore = new ElectronStore({name: storeAppConfigStorage});
appConfigStore.set('windowBounds', topWindow.getBounds());
mainWindow.on('close', () => {
const appConfigStore = new ElectronStore({name: appConfigStoreStorage});
appConfigStore.set('windowBounds', mainWindow.getBounds());
});
topWindow.on('closed', () => {
topWindow = null;
mainWindow.on('closed', () => {
mainWindow = null;
});
if (isDebugMode) {
topWindow.webContents.openDevTools();
mainWindow.webContents.openDevTools();
}
}
......@@ -131,9 +130,9 @@ function btoa(string) {
return buffer.toString('base64');
}
/*function atob(string) {
return Buffer.from(string, 'base64').toString('binary');
}*/
//function atob(string) {
// return Buffer.from(string, 'base64').toString('binary');
//}
function previewpicFilename(itemKey) {
// "itemKey" will be URL to product file
......@@ -145,11 +144,9 @@ function downloadPreviewpic(itemKey, url) {
fs.mkdirSync(previewpicDirectory);
}
const path = `${previewpicDirectory}/${previewpicFilename(itemKey)}`;
request.get(url)
.on('error', (error) => {
console.error(error);
})
.pipe(fs.createWriteStream(path));
request.get(url).on('error', (error) => {
console.error(error);
}).pipe(fs.createWriteStream(path));
}
function removePreviewpic(itemKey) {
......@@ -179,7 +176,7 @@ app.on('window-all-closed', () => {
});
app.on('activate', () => {
if (topWindow === null) {
if (mainWindow === null) {
createWindow();
}
});
......@@ -213,25 +210,25 @@ ipcMain.on('ocs-manager', (event, key) => {
});
ipcMain.on('store', (event, key, value) => {
const appConfigStore = new ElectronStore({name: storeAppConfigStorage});
const appConfigStore = new ElectronStore({name: appConfigStoreStorage});
if (key && value) {
appConfigStore.set(key, value);
}
event.returnValue = key ? appConfigStore.get(key) : appConfigStore.store;
});
ipcMain.on('previewpic', (event, action, itemKey, url) => {
if (action === 'directory') {
ipcMain.on('previewpic', (event, kind, itemKey, url) => {
if (kind === 'directory') {
event.returnValue = previewpicDirectory;
}
else if (action === 'path' && itemKey) {
else if (kind === 'path' && itemKey) {
event.returnValue = `${previewpicDirectory}/${previewpicFilename(itemKey)}`;
}
else if (action === 'download' && itemKey && url) {
else if (kind === 'download' && itemKey && url) {
downloadPreviewpic(itemKey, url);
event.returnValue = undefined;
}
else if (action === 'remove' && itemKey) {
else if (kind === 'remove' && itemKey) {
removePreviewpic(itemKey);
event.returnValue = undefined;
}
......
......@@ -31,11 +31,12 @@ export default class AboutdialogComponent extends BaseComponent {
render() {
return this.html`
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
@import url(images/icon.css);
</style>
<style>
div[slot="content"] {
padding: 1em;
text-align: center;
......@@ -54,7 +55,7 @@ export default class AboutdialogComponent extends BaseComponent {
}
</style>
<app-dialog data-width="500px" data-footer-status="inactive">
<app-dialog data-width="500px" data-footer-state="inactive">
<h3 slot="header">About This App</h3>
<div slot="content">
<figure class="icon-ocs-store"></figure>
......
......@@ -22,7 +22,9 @@ export default class CollectiondialogComponent extends BaseComponent {
render() {
return `
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
</style>
<style>
app-page[slot="content"] {
......@@ -37,7 +39,7 @@ export default class CollectiondialogComponent extends BaseComponent {
}
</style>
<app-dialog data-width="80%" data-height="80%" data-footer-status="inactive">
<app-dialog data-width="80%" data-height="80%" data-footer-state="inactive">
<h3 slot="header">My Collection</h3>
<app-page id="collection" slot="content">
<app-collectionsidebar slot="sidebar"></app-collectionsidebar>
......
......@@ -23,11 +23,12 @@ export default class CollectiondownloadComponent extends BaseComponent {
render() {
return `
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
@import url(styles/material-icons.css);
</style>
<style>
:host {
display: flex;
flex-flow: column nowrap;
......@@ -92,7 +93,7 @@ export default class CollectiondownloadComponent extends BaseComponent {
nav[data-action] button:hover {
border-color: rgba(0,0,0,0.3);
}
nav[data-action] button[data-status="inactive"] {
nav[data-action] button[data-state="inactive"] {
display: none;
}
</style>
......@@ -128,8 +129,7 @@ export default class CollectiondownloadComponent extends BaseComponent {
}
}
else {
this.contentRoot.querySelector('ul[data-container]')
.insertAdjacentHTML('afterbegin', this._listItemHtml(state));
this.contentRoot.querySelector('ul[data-container]').insertAdjacentHTML('afterbegin', this._listItemHtml(state));
}
}
......@@ -137,11 +137,10 @@ export default class CollectiondownloadComponent extends BaseComponent {
const listItem = this.contentRoot.querySelector(`li[data-url="${state.url}"]`);
if (listItem) {
listItem.querySelector('progress[data-progress]').value = '' + state.bytesReceived/state.bytesTotal;
const message = 'Downloading... '
listItem.querySelector('p[data-message]').textContent = 'Downloading... '
+ Chilit.Utility.convertByteToHumanReadable(state.bytesReceived)
+ ' / '
+ Chilit.Utility.convertByteToHumanReadable(state.bytesTotal);
listItem.querySelector('p[data-message]').textContent = message;
}
}
......
......@@ -20,7 +20,9 @@ export default class CollectioninstalledComponent extends BaseComponent {
render() {
return `
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
</style>
<style>
:host {
......@@ -79,7 +81,7 @@ export default class CollectioninstalledComponent extends BaseComponent {
nav[data-action] button:hover {
border-color: rgba(0,0,0,0.3);
}
nav[data-action] button[data-status="inactive"] {
nav[data-action] button[data-state="inactive"] {
display: none;
}
</style>
......@@ -88,11 +90,11 @@ export default class CollectioninstalledComponent extends BaseComponent {
`;
}
_listItemsHtml(state) {
const listItems = [];
_listItemSetHtml(state) {
let listItemSet = '';
if (state.count) {
const applyButtonStatus = state.isApplicableType ? 'active' : 'inactive';
const applyButtonState = state.isApplicableType ? 'active' : 'inactive';
const openButtonText = (state.installType === 'bin') ? 'Run' : 'Open';
const destination = state.installTypes[state.installType].destination;
......@@ -101,7 +103,7 @@ export default class CollectioninstalledComponent extends BaseComponent {
for (const file of value.files) {
const filePath = `${destination}/${file}`;
const fileUrl = `file://${filePath}`;
listItems.push(`
listItemSet += `
<li>
<figure data-previewpic style="background-image: url('${previewpicUrl}');"></figure>
<div data-main>
......@@ -111,42 +113,41 @@ export default class CollectioninstalledComponent extends BaseComponent {
<button data-action="ocsManager_openUrl" data-url="${fileUrl}">${openButtonText}</button>
<button data-action="ocsManager_applyTheme"
data-path="${filePath}" data-install-type="${state.installType}"
data-status="${applyButtonStatus}">Apply</button>
data-state="${applyButtonState}">Apply</button>
<button data-action="ocsManager_uninstall" data-item-key="${key}">Delete</button>
</nav>
</li>
`);
`;
}
}
}
return listItems.join('');
return listItemSet;
}
_handleClick(event) {
if (event.target.closest('button[data-action]')) {
const button = event.target.closest('button[data-action]');
switch (button.getAttribute('data-action')) {
const target = event.target.closest('button[data-action]');
switch (target.getAttribute('data-action')) {
case 'ocsManager_openUrl':
this.dispatch('ocsManager_openUrl', {url: button.getAttribute('data-url')});
this.dispatch('ocsManager_openUrl', {url: target.getAttribute('data-url')});
break;
case 'ocsManager_applyTheme':
this.dispatch('ocsManager_applyTheme', {
path: button.getAttribute('data-path'),
installType: button.getAttribute('data-install-type')
path: target.getAttribute('data-path'),
installType: target.getAttribute('data-install-type')
});
break;
case 'ocsManager_uninstall':
this.dispatch('ocsManager_uninstall', {itemKey: button.getAttribute('data-item-key')});
button.closest('li').remove();
this.dispatch('ocsManager_uninstall', {itemKey: target.getAttribute('data-item-key')});
target.closest('li').remove();
break;
}
}
}
_viewHandler_ocsManager_installedItemsByType(state) {
this.contentRoot.querySelector('ul[data-container]')
.innerHTML = this._listItemsHtml(state);
this.contentRoot.querySelector('ul[data-container]').innerHTML = this._listItemSetHtml(state);
}
}
......@@ -26,7 +26,9 @@ export default class CollectionsidebarComponent extends BaseComponent {
render() {
return `
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
</style>
<style>
:host {
......@@ -46,7 +48,7 @@ export default class CollectionsidebarComponent extends BaseComponent {
nav[data-sidebar] h4 {
padding: 0.5em 1em;
}
nav[data-sidebar] ul[data-menu="activity"] {
nav[data-sidebar] ul[data-menu="task"] {
border-bottom: 1px solid var(--color-border);
}
nav[data-sidebar] ul li a {
......@@ -61,7 +63,7 @@ export default class CollectionsidebarComponent extends BaseComponent {
nav[data-sidebar] ul li a[data-selected] {
background-color: var(--color-active);
}
nav[data-sidebar] ul li a[data-status="inactive"] {
nav[data-sidebar] ul li a[data-state="inactive"] {
display: none;
}
nav[data-sidebar] ul li a span[data-name] {
......@@ -99,44 +101,44 @@ export default class CollectionsidebarComponent extends BaseComponent {
</style>
<nav data-sidebar>
<ul data-menu="activity">
<ul data-menu="task">
<li>
<a href="#" data-action="update" data-status="inactive">
<a href="#" data-action="update" data-state="inactive">
<span data-name>Update</span>
<span data-count="0">0</span>
</a>
</li>
<li>
<a href="#" data-action="download" data-status="active">
<a href="#" data-action="download" data-state="active">
<span data-name>Download</span>
<span data-count="0">0</span>
</a>
</li>
</ul>
<h4>Installed</h4>
<ul data-menu="category"></ul>
<ul data-menu="categories"></ul>
</nav>
`;
}
_categoryMenuItemsHtml(state) {
const menuItems = [];
_categoriesMenuItemSetHtml(state) {
let listItemSet = '';
if (state.count) {
const categorizedInstalledItems = {};
const categorizedItems = {};
for (const [key, value] of Object.entries(state.installedItems)) {
if (!categorizedInstalledItems[value.install_type]) {
categorizedInstalledItems[value.install_type] = {};
if (!categorizedItems[value.install_type]) {
categorizedItems[value.install_type] = {};
}
categorizedInstalledItems[value.install_type][key] = value;
categorizedItems[value.install_type][key] = value;
}
const categories = [];
for (const installType of Object.keys(categorizedInstalledItems)) {
for (const installType of Object.keys(categorizedItems)) {
categories.push({
installType: installType,
name: state.installTypes[installType].name,
count: Object.keys(categorizedInstalledItems[installType]).length
count: Object.keys(categorizedItems[installType]).length
});
}
categories.sort((a, b) => {
......@@ -152,35 +154,33 @@ export default class CollectionsidebarComponent extends BaseComponent {
});
for (const category of categories) {
menuItems.push(`
listItemSet += `
<li>
<a href="#" data-action="installed" data-install-type="${category.installType}">
<span data-name>${category.name}</span>
<span data-count="${category.count}">${category.count}</span>
</a>
</li>
`);
`;
}
}
return menuItems.join('');
return listItemSet;
}
_handleClick(event) {
if (event.target.closest('a[data-action]')) {
event.preventDefault();
const targetMenuItem = event.target.closest('a[data-action]');
const target = event.target.closest('a[data-action]');
for (const menuItem of this.contentRoot.querySelectorAll('a[data-action]')) {
menuItem.removeAttribute('data-selected');
if (this.contentRoot.querySelector('a[data-action][data-selected]')) {
this.contentRoot.querySelector('a[data-action][data-selected]').removeAttribute('data-selected');
}
targetMenuItem.setAttribute('data-selected', 'data-selected');
target.setAttribute('data-selected', 'data-selected');
switch (targetMenuItem.getAttribute('data-action')) {
switch (target.getAttribute('data-action')) {
case 'installed':
this.dispatch('ocsManager_installedItemsByType', {
installType: targetMenuItem.getAttribute('data-install-type')
});
this.dispatch('ocsManager_installedItemsByType', {installType: target.getAttribute('data-install-type')});
this.dispatch('collectionsidebar_select', {select: 'installed'});
break;
case 'update':
......@@ -194,15 +194,14 @@ export default class CollectionsidebarComponent extends BaseComponent {
}
_viewHandler_ocsManager_installedItems(state) {
const categoryMenu = this.contentRoot.querySelector('nav ul[data-menu="category"]');
const categoriesMenu = this.contentRoot.querySelector('ul[data-menu="categories"]');
const selectedMenuItem = categoryMenu.querySelector('a[data-selected]');
const selectedMenuItem = categoriesMenu.querySelector('a[data-selected]');
const installType = selectedMenuItem ? selectedMenuItem.getAttribute('data-install-type') : '';
categoryMenu.innerHTML = this._categoryMenuItemsHtml(state);
const menuItem = installType ? categoryMenu.querySelector(`a[data-install-type="${installType}"]`) : null;
categoriesMenu.innerHTML = this._categoriesMenuItemSetHtml(state);
const menuItem = installType ? categoriesMenu.querySelector(`a[data-install-type="${installType}"]`) : null;
if (menuItem) {
menuItem.click();
}
......@@ -210,7 +209,7 @@ export default class CollectionsidebarComponent extends BaseComponent {
_viewHandler_ocsManager_updateAvailableItems(state) {
const menuItem = this.contentRoot.querySelector('a[data-action="update"]');
menuItem.setAttribute('data-status', state.count ? 'active' : 'inactive');
menuItem.setAttribute('data-state', state.count ? 'active' : 'inactive');
const badge = menuItem.querySelector('span[data-count]');
badge.setAttribute('data-count', '' + state.count);
......@@ -219,7 +218,7 @@ export default class CollectionsidebarComponent extends BaseComponent {
_viewHandler_ocsManager_metadataSet(state) {
const menuItem = this.contentRoot.querySelector('a[data-action="download"]');
//menuItem.setAttribute('data-status', state.count ? 'active' : 'inactive');
//menuItem.setAttribute('data-state', state.count ? 'active' : 'inactive');
const badge = menuItem.querySelector('span[data-count]');
badge.setAttribute('data-count', '' + state.count);
......
......@@ -23,7 +23,9 @@ export default class CollectionupdateComponent extends BaseComponent {
render() {
return `
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
</style>
<style>
:host {
......@@ -90,7 +92,7 @@ export default class CollectionupdateComponent extends BaseComponent {
nav[data-action] button:hover {
border-color: rgba(0,0,0,0.3);
}
nav[data-action] button[data-status="inactive"] {
nav[data-action] button[data-state="inactive"] {
display: none;
}
</style>
......@@ -99,14 +101,14 @@ export default class CollectionupdateComponent extends BaseComponent {
`;
}
_listItemsHtml(state) {
const listItems = [];
_listItemSetHtml(state) {
let listItemSet = '';
if (state.count) {
for (const [key, value] of Object.entries(state.updateAvailableItems)) {
const file = value.files[0];
const previewpicUrl = `file://${state.previewpicDirectory}/${this.convertItemKeyToPreviewpicFilename(key)}`;
listItems.push(`
listItemSet += `
<li data-item-key="${key}">
<figure data-previewpic style="background-image: url('${previewpicUrl}');"></figure>
<div data-main>
......@@ -115,39 +117,37 @@ export default class CollectionupdateComponent extends BaseComponent {
<p data-message></p>
</div>
<nav data-action>
<button data-action="ocsManager_update" data-item-key="${key}" data-status="active">Update</button>
<button data-action="ocsManager_update" data-item-key="${key}" data-state="active">Update</button>
</nav>
</li>
`);
`;
}
}
return listItems.join('');
return listItemSet;
}
_handleClick(event) {
if (event.target.closest('button[data-action]')) {
const button = event.target.closest('button[data-action]');
switch (button.getAttribute('data-action')) {
const target = event.target.closest('button[data-action]');
switch (target.getAttribute('data-action')) {
case 'ocsManager_update':
this.dispatch('ocsManager_update', {itemKey: button.getAttribute('data-item-key')});
button.setAttribute('data-status', 'inactive');
this.dispatch('ocsManager_update', {itemKey: target.getAttribute('data-item-key')});
target.setAttribute('data-state', 'inactive');
break;
}
}
}
_viewHandler_ocsManager_updateAvailableItems(state) {
this.contentRoot.querySelector('ul[data-container]')
.innerHTML = this._listItemsHtml(state);
this.contentRoot.querySelector('ul[data-container]').innerHTML = this._listItemSetHtml(state);
}
_viewHandler_ocsManager_updateProgress(state) {
const listItem = this.contentRoot.querySelector(`li[data-item-key="${state.itemKey}"]`);
if (listItem) {
listItem.querySelector('progress[data-progress]').value = '' + state.progress;
const message = `Updating... ${Math.ceil(state.progress * 100)}%`;
listItem.querySelector('p[data-message]').textContent = message;
listItem.querySelector('p[data-message]').textContent = `Updating... ${Math.ceil(state.progress * 100)}%`;
}
}
......
......@@ -3,7 +3,7 @@ import BaseComponent from './common/BaseComponent.js';
export default class OmniboxComponent extends BaseComponent {
static get componentObservedAttributes() {
return ['data-status', 'data-auto-close-status'];
return ['data-state', 'data-auto-close-state'];
}
init() {
......@@ -32,17 +32,18 @@ export default class OmniboxComponent extends BaseComponent {
}
render() {
const status = this.getAttribute('data-status') || 'inactive';
const autoCloseStatus = this.getAttribute('data-auto-close-status') || 'active';
const state = this.getAttribute('data-state') || 'inactive';
const autoCloseState = this.getAttribute('data-auto-close-state') || 'active';
const autoCloseAction = (autoCloseStatus === 'active') ? 'omnibox_autoClose' : '';
const autoCloseAction = (autoCloseState === 'active') ? 'omnibox_autoClose' : '';
return this.html`
<style>${this.sharedStyle}</style>
<style>
${this.sharedStyle}
@import url(styles/material-icons.css);
</style>
<style>
:host {
display: inline-block;
width: 500px;
......@@ -60,7 +61,8 @@ export default class OmniboxComponent extends BaseComponent {
div[data-omnibox]:hover {
background-color: var(--color-active);
}
div[data-omnibox] div[data-content] {
div[data-content] {
display: flex;
flex-flow: row nowrap;
align-items: center;
......@@ -68,7 +70,7 @@ export default class OmniboxComponent extends BaseComponent {
height: inherit;
line-height: 1;
}
div[data-omnibox] div[data-content] h3 {
div[data-content] h3 {
flex: 1 1 auto;
border-right: 1px solid var(--color-border);
overflow: hidden;
......@@ -78,14 +80,15 @@ export default class OmniboxComponent extends BaseComponent {
text-align: center;
cursor: pointer;
}
div[data-omnibox] div[data-content] div {
div[data-content] div {
display: flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
width: 30px;
}
div[data-omnibox] app-indicator {
app-indicator {
position: relative;
top: -2px;
}
......@@ -99,10 +102,10 @@ export default class OmniboxComponent extends BaseComponent {
padding: 1em;
border: 1px solid var(--color-border);
border-radius: 5px;
box-shadow: 0 10px 30px var(--color-shadow);
box-shadow: 0 5px 20px 0 var(--color-shadow);
background-color: var(--color-content);
}
div[data-palette][data-status="inactive"] {
div[data-palette][data-state="inactive"] {
display: none;
}
div[data-palette] h4 {
......@@ -153,7 +156,7 @@ export default class OmniboxComponent extends BaseComponent {
width: 100%;
height: 100%;
}
div[data-overlay][data-status="inactive"] {
div[data-overlay][data-state="inactive"] {
display: none;
}
</style>
......@@ -167,10 +170,10 @@ export default class OmniboxComponent extends BaseComponent {
data-title="Open in Browser" data-icon="open_in_browser" data-size="small"></app-iconbutton>
</div>
</div>
<app-indicator data-status="inactive"></app-indicator>
<app-indicator data-state="inactive"></app-indicator>
</div>
<div data-palette data-status="${status}" class="fade-in">
<div data-palette data-state="${state}" class="fade-in">
<h4><i class="material-icons md-small">home</i> Choose Startpage</h4>
<nav>
<ul>
......@@ -184,19 +187,19 @@ export default class OmniboxComponent extends BaseComponent {
</nav>
</div>
<div data-overlay data-status="${status}" data-action="${autoCloseAction}"></div>
<div data-overlay data-state="${state}" data-action="${autoCloseAction}"></div>
`;
}
open() {
this.contentRoot.querySelector('div[data-palette]').setAttribute('data-status', 'active');