Fix bulk export
This commit is contained in:
parent
a7026bdeee
commit
44889f6158
|
@ -93,7 +93,8 @@
|
||||||
"description": "Message for backup"
|
"description": "Message for backup"
|
||||||
},
|
},
|
||||||
"bckpInstStyles": {
|
"bckpInstStyles": {
|
||||||
"message": "Export styles"
|
"message": "Local device",
|
||||||
|
"description": "Selected option to backup indicated styles to local device/drive"
|
||||||
},
|
},
|
||||||
"checkAllUpdates": {
|
"checkAllUpdates": {
|
||||||
"message": "Check all styles for updates",
|
"message": "Check all styles for updates",
|
||||||
|
@ -1238,6 +1239,10 @@
|
||||||
"message": "Click to open the filter, search and bulk actions panel",
|
"message": "Click to open the filter, search and bulk actions panel",
|
||||||
"description": "Text for button to apply the selected action"
|
"description": "Text for button to apply the selected action"
|
||||||
},
|
},
|
||||||
|
"bulkActionsError": {
|
||||||
|
"message": "Choose at least one style",
|
||||||
|
"description": "Error displayed in a tooltip when the user attempts to apply an action with no styles selected"
|
||||||
|
},
|
||||||
"sectionAdd": {
|
"sectionAdd": {
|
||||||
"message": "Add another section",
|
"message": "Add another section",
|
||||||
"description": "Label for the button to add a section"
|
"description": "Label for the button to add a section"
|
||||||
|
|
|
@ -27,6 +27,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
|
||||||
'manage.onlyEnabled.invert': false, // display only disabled styles
|
'manage.onlyEnabled.invert': false, // display only disabled styles
|
||||||
'manage.onlyLocal.invert': false, // display only externally installed styles
|
'manage.onlyLocal.invert': false, // display only externally installed styles
|
||||||
'manage.onlyUsercss.invert': false, // display only non-usercss (standard) styles
|
'manage.onlyUsercss.invert': false, // display only non-usercss (standard) styles
|
||||||
|
'manage.export.destination': 'local', // default export destination (local or dropbox)
|
||||||
|
|
||||||
'manage.newUI.favicons': false, // show favicons for the sites in applies-to
|
'manage.newUI.favicons': false, // show favicons for the sites in applies-to
|
||||||
'manage.newUI.faviconsGray': true, // gray out favicons
|
'manage.newUI.faviconsGray': true, // gray out favicons
|
||||||
|
|
12
manage.html
12
manage.html
|
@ -175,7 +175,7 @@
|
||||||
|
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" class="unfile-all-styles" i18n-text="retrieveBckp"></a>
|
<a href="#" class="unfile-all-styles" i18n-text="retrieveBckp"></a>
|
||||||
<a href="#" class="sync-dropbox-import" i18n-text="retrieveDropboxSync"></a>
|
<a href="#" class="sync-dropbox-import" i18n-text="syncDropboxStyles"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -360,7 +360,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<span class="select-resizer">
|
<span class="select-resizer">
|
||||||
<select id="manage.onlyLocal.invert" i18n-data-title="manageOnlyLocalTooltip">
|
<select id="manage.onlyLocal.invert" class="tt-s" i18n-data-title="manageOnlyLocalTooltip">
|
||||||
<option i18n-text="manageOnlyLocal" value="false"></option>
|
<option i18n-text="manageOnlyLocal" value="false"></option>
|
||||||
<option i18n-text="manageOnlyExternal" value="true"></option>
|
<option i18n-text="manageOnlyExternal" value="true"></option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -430,7 +430,7 @@
|
||||||
</select>
|
</select>
|
||||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||||
</span>
|
</span>
|
||||||
<button id="bulk-actions-apply" i18n-text="bulkActionsApply">
|
<button id="bulk-actions-apply" i18n-text="bulkActionsApply" class="tt-e" disabled>
|
||||||
<span id="update-progress"></span>
|
<span id="update-progress"></span>
|
||||||
</button>
|
</button>
|
||||||
<span id="bulk-info">
|
<span id="bulk-info">
|
||||||
|
@ -445,9 +445,9 @@
|
||||||
<span data-bulk="export" class="dropdown export hidden">
|
<span data-bulk="export" class="dropdown export hidden">
|
||||||
Export to:
|
Export to:
|
||||||
<span class="select-resizer">
|
<span class="select-resizer">
|
||||||
<select id="manage.onlyEnabled.invert">
|
<select id="manage.export.destination">
|
||||||
<option id="file-all-styles" i18n-text="bckpInstStyles"></option>
|
<option value="local" i18n-text="bckpInstStyles"></option>
|
||||||
<option id="sync-dropbox-export" i18n-text="syncDropboxStyles"></option>
|
<option value="dropbox" i18n-text="syncDropboxStyles"></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* global $ $$ handleEvent installed exportToFile checkUpdateAll */
|
/* global $ $$ API t prefs handleEvent installed exportToFile checkUpdateAll exportDropbox
|
||||||
|
messageBox */
|
||||||
/* exported bulk */
|
/* exported bulk */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -13,14 +14,13 @@ const bulk = {
|
||||||
handleSelect: event => {
|
handleSelect: event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$$('[data-bulk]').forEach(el => el.classList.add('hidden'));
|
$$('[data-bulk]').forEach(el => el.classList.add('hidden'));
|
||||||
console.log('select', this.value)
|
|
||||||
switch (event.target.value) {
|
switch (event.target.value) {
|
||||||
case 'enable':
|
case 'enable':
|
||||||
break;
|
break;
|
||||||
case 'disable':
|
case 'disable':
|
||||||
break;
|
break;
|
||||||
case 'export':
|
case 'export':
|
||||||
console.log('got here')
|
|
||||||
$('[data-bulk="export"]').classList.remove('hidden');
|
$('[data-bulk="export"]').classList.remove('hidden');
|
||||||
break;
|
break;
|
||||||
case 'update':
|
case 'update':
|
||||||
|
@ -51,10 +51,14 @@ console.log('select', this.value)
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'export':
|
case 'export': {
|
||||||
styles = entries.map(entry => entry.styleMeta);
|
styles = entries.map(entry => entry.styleMeta);
|
||||||
exportToFile(styles);
|
const destination = prefs.get('manage.export.destination');
|
||||||
break;
|
if (destination === 'dropbox') {
|
||||||
|
return exportDropbox(styles);
|
||||||
|
}
|
||||||
|
return exportToFile(styles);
|
||||||
|
}
|
||||||
case 'update':
|
case 'update':
|
||||||
checkUpdateAll(entries); // TO DO
|
checkUpdateAll(entries); // TO DO
|
||||||
break;
|
break;
|
||||||
|
@ -99,6 +103,12 @@ console.log('select', this.value)
|
||||||
}
|
}
|
||||||
const count = $$('.entry-filter-toggle').filter(entry => entry.checked).length;
|
const count = $$('.entry-filter-toggle').filter(entry => entry.checked).length;
|
||||||
$('#bulk-filter-count').textContent = count || '';
|
$('#bulk-filter-count').textContent = count || '';
|
||||||
|
|
||||||
|
if (count > 0 && $('#bulk-actions-select').value !== '') {
|
||||||
|
$('#bulk-actions-apply').removeAttribute('disabled');
|
||||||
|
} else {
|
||||||
|
$('#bulk-actions-apply').setAttribute('disabled', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -118,4 +128,4 @@ console.log('select', this.value)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
[data-title] {
|
[data-title] {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-title]:after {
|
[data-title]:after {
|
||||||
|
@ -161,4 +162,3 @@
|
||||||
.update-problem .check-update:before {
|
.update-problem .check-update:before {
|
||||||
border-right-color: var(--tooltip-error);
|
border-right-color: var(--tooltip-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
177
sync/import-export-dropbox.js
Normal file
177
sync/import-export-dropbox.js
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/* global messageBox Dropbox createZipFileFromText readZipFileFromBlob
|
||||||
|
launchWebAuthFlow getRedirectUrlAuthFlow importFromString resolve
|
||||||
|
$ $create t chromeLocal API getOwnTab */
|
||||||
|
/* exported exportDropbox */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const DROPBOX_API_KEY = 'zg52vphuapvpng9';
|
||||||
|
const FILENAME_ZIP_FILE = 'stylus.json';
|
||||||
|
const DROPBOX_FILE = 'stylus.zip';
|
||||||
|
const API_ERROR_STATUS_FILE_NOT_FOUND = 409;
|
||||||
|
const HTTP_STATUS_CANCEL = 499;
|
||||||
|
|
||||||
|
function messageProgressBar(data) {
|
||||||
|
return messageBox({
|
||||||
|
title: `${data.title}`,
|
||||||
|
className: 'config-dialog',
|
||||||
|
contents: [
|
||||||
|
$create('p', data.text)
|
||||||
|
],
|
||||||
|
buttons: [{
|
||||||
|
textContent: t('confirmClose'),
|
||||||
|
dataset: {cmd: 'close'},
|
||||||
|
}],
|
||||||
|
}).then(() => {
|
||||||
|
document.body.style.minWidth = '';
|
||||||
|
document.body.style.minHeight = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDropboxAccessToken() {
|
||||||
|
return chromeLocal.getValue('dropbox_access_token');
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestDropboxAccessToken() {
|
||||||
|
const client = new Dropbox.Dropbox({
|
||||||
|
clientId: DROPBOX_API_KEY,
|
||||||
|
fetch
|
||||||
|
});
|
||||||
|
const authUrl = client.getAuthenticationUrl(getRedirectUrlAuthFlow());
|
||||||
|
return launchWebAuthFlow({url: authUrl, interactive: true})
|
||||||
|
.then(urlReturned => {
|
||||||
|
const params = new URLSearchParams(new URL(urlReturned).hash.replace('#', ''));
|
||||||
|
chromeLocal.setValue('dropbox_access_token', params.get('access_token'));
|
||||||
|
return params.get('access_token');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFileDropbox(client, stylesText) {
|
||||||
|
return client.filesUpload({path: '/' + DROPBOX_FILE, contents: stylesText});
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportDropbox(styles) {
|
||||||
|
const mode = localStorage.installType;
|
||||||
|
const title = t('syncDropboxStyles');
|
||||||
|
const text = mode === 'normal' ? t('connectingDropbox') : t('connectingDropboxNotAllowed');
|
||||||
|
messageProgressBar({title, text});
|
||||||
|
if (mode !== 'normal') return;
|
||||||
|
|
||||||
|
hasDropboxAccessToken()
|
||||||
|
.then(token => token || requestDropboxAccessToken())
|
||||||
|
.then(token => {
|
||||||
|
const client = new Dropbox.Dropbox({
|
||||||
|
clientId: DROPBOX_API_KEY,
|
||||||
|
accessToken: token,
|
||||||
|
fetch
|
||||||
|
});
|
||||||
|
return client.filesDownload({path: '/' + DROPBOX_FILE})
|
||||||
|
.then(() => messageBox.confirm(t('overwriteFileExport')))
|
||||||
|
.then(ok => {
|
||||||
|
// deletes file if user want to
|
||||||
|
if (!ok) {
|
||||||
|
return Promise.reject({status: HTTP_STATUS_CANCEL});
|
||||||
|
}
|
||||||
|
return client.filesDelete({path: '/' + DROPBOX_FILE});
|
||||||
|
})
|
||||||
|
// file deleted with success, process styles and create a file
|
||||||
|
.then(() => {
|
||||||
|
messageProgressBar({title: title, text: t('gettingStyles')});
|
||||||
|
return JSON.stringify(styles, null, '\t');
|
||||||
|
})
|
||||||
|
// create zip file
|
||||||
|
.then(stylesText => {
|
||||||
|
messageProgressBar({title: title, text: t('zipStyles')});
|
||||||
|
return createZipFileFromText(FILENAME_ZIP_FILE, stylesText);
|
||||||
|
})
|
||||||
|
// create file dropbox
|
||||||
|
.then(zipedText => {
|
||||||
|
messageProgressBar({title: title, text: t('uploadingFile')});
|
||||||
|
return uploadFileDropbox(client, zipedText);
|
||||||
|
})
|
||||||
|
// gives feedback to user
|
||||||
|
.then(() => messageProgressBar({title: title, text: t('exportSavedSuccess')}))
|
||||||
|
// handle not found cases and cancel action
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
// saving file first time
|
||||||
|
if (error.status === API_ERROR_STATUS_FILE_NOT_FOUND) {
|
||||||
|
API.getAllStyles()
|
||||||
|
.then(styles => {
|
||||||
|
messageProgressBar({title: title, text: t('gettingStyles')});
|
||||||
|
return JSON.stringify(styles, null, '\t');
|
||||||
|
})
|
||||||
|
.then(stylesText => {
|
||||||
|
messageProgressBar({title: title, text: t('zipStyles')});
|
||||||
|
return createZipFileFromText(FILENAME_ZIP_FILE, stylesText);
|
||||||
|
})
|
||||||
|
.then(zipedText => {
|
||||||
|
messageProgressBar({title: title, text: t('uploadingFile')});
|
||||||
|
return uploadFileDropbox(client, zipedText);
|
||||||
|
})
|
||||||
|
.then(() => messageProgressBar({title: title, text: t('exportSavedSuccess')}))
|
||||||
|
.catch(err => messageBox.alert(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// user cancelled the flow
|
||||||
|
if (error.status === HTTP_STATUS_CANCEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#sync-dropbox-import').onclick = () => {
|
||||||
|
const mode = localStorage.installType;
|
||||||
|
const title = t('syncDropboxStyles');
|
||||||
|
const text = mode === 'normal' ? t('connectingDropbox') : t('connectingDropboxNotAllowed');
|
||||||
|
messageProgressBar({title, text});
|
||||||
|
if (mode !== 'normal') return;
|
||||||
|
|
||||||
|
hasDropboxAccessToken()
|
||||||
|
.then(token => token || requestDropboxAccessToken())
|
||||||
|
.then(token => {
|
||||||
|
const client = new Dropbox.Dropbox({
|
||||||
|
clientId: DROPBOX_API_KEY,
|
||||||
|
accessToken: token,
|
||||||
|
fetch
|
||||||
|
});
|
||||||
|
return client.filesDownload({path: '/' + DROPBOX_FILE})
|
||||||
|
.then(response => {
|
||||||
|
messageProgressBar({title: title, text: t('unzipStyles')});
|
||||||
|
return readZipFileFromBlob(response.fileBlob);
|
||||||
|
})
|
||||||
|
.then(zipedFileBlob => {
|
||||||
|
messageProgressBar({title: title, text: t('readingStyles')});
|
||||||
|
document.body.style.cursor = 'wait';
|
||||||
|
const fReader = new FileReader();
|
||||||
|
fReader.onloadend = event => {
|
||||||
|
const text = event.target.result;
|
||||||
|
const maybeUsercss = !/^[\s\r\n]*\[/.test(text) &&
|
||||||
|
(text.includes('==UserStyle==') || /==UserStyle==/i.test(text));
|
||||||
|
(!maybeUsercss ?
|
||||||
|
importFromString(text) :
|
||||||
|
getOwnTab().then(tab => {
|
||||||
|
tab.url = URL.createObjectURL(new Blob([text], {type: 'text/css'}));
|
||||||
|
return API.openUsercssInstallPage({direct: true, tab})
|
||||||
|
.then(() => URL.revokeObjectURL(tab.url));
|
||||||
|
})
|
||||||
|
).then(numStyles => {
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
resolve(numStyles);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
fReader.readAsText(zipedFileBlob, 'utf-8');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// no file
|
||||||
|
if (error.status === API_ERROR_STATUS_FILE_NOT_FOUND) {
|
||||||
|
messageBox.alert(t('noFileToImport'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messageBox.alert(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user