Reorganized side bar menu
This commit is contained in:
parent
8cf0b87f5f
commit
2a865ddb44
@ -2,7 +2,7 @@
|
||||
<div class="side-bar flex flex--column">
|
||||
<div class="side-title flex flex--row">
|
||||
<button v-if="panel !== 'menu'" class="side-title__button button" @click="setPanel('menu')" v-title="'Main menu'">
|
||||
<icon-arrow-left></icon-arrow-left>
|
||||
<icon-dots-horizontal></icon-dots-horizontal>
|
||||
</button>
|
||||
<div class="side-title__title">
|
||||
{{panelName}}
|
||||
@ -18,8 +18,8 @@
|
||||
<publish-menu v-else-if="panel === 'publish'"></publish-menu>
|
||||
<history-menu v-else-if="panel === 'history'"></history-menu>
|
||||
<export-menu v-else-if="panel === 'export'"></export-menu>
|
||||
<import-menu v-else-if="panel === 'import'"></import-menu>
|
||||
<more-menu v-else-if="panel === 'more'"></more-menu>
|
||||
<import-export-menu v-else-if="panel === 'importExport'"></import-export-menu>
|
||||
<workspace-backup-menu v-else-if="panel === 'workspaceBackup'"></workspace-backup-menu>
|
||||
<div v-else-if="panel === 'help'" class="side-bar__panel side-bar__panel--help">
|
||||
<pre class="markdown-highlighting" v-html="markdownSample"></pre>
|
||||
</div>
|
||||
@ -39,9 +39,8 @@ import WorkspacesMenu from './menus/WorkspacesMenu';
|
||||
import SyncMenu from './menus/SyncMenu';
|
||||
import PublishMenu from './menus/PublishMenu';
|
||||
import HistoryMenu from './menus/HistoryMenu';
|
||||
import ExportMenu from './menus/ExportMenu';
|
||||
import ImportMenu from './menus/ImportMenu';
|
||||
import MoreMenu from './menus/MoreMenu';
|
||||
import ImportExportMenu from './menus/ImportExportMenu';
|
||||
import WorkspaceBackupMenu from './menus/WorkspaceBackupMenu';
|
||||
import markdownSample from '../data/markdownSample.md';
|
||||
import markdownConversionSvc from '../services/markdownConversionSvc';
|
||||
import store from '../store';
|
||||
@ -54,8 +53,8 @@ const panelNames = {
|
||||
sync: 'Synchronize',
|
||||
publish: 'Publish',
|
||||
history: 'File history',
|
||||
export: 'Export to disk',
|
||||
import: 'Import from disk',
|
||||
importExport: 'Import/export',
|
||||
workspaceBackup: 'Workspace backup',
|
||||
};
|
||||
|
||||
export default {
|
||||
@ -66,9 +65,8 @@ export default {
|
||||
SyncMenu,
|
||||
PublishMenu,
|
||||
HistoryMenu,
|
||||
ExportMenu,
|
||||
ImportMenu,
|
||||
MoreMenu,
|
||||
ImportExportMenu,
|
||||
WorkspaceBackupMenu,
|
||||
},
|
||||
data: () => ({
|
||||
markdownSample: markdownConversionSvc.highlight(markdownSample),
|
||||
|
@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="side-bar__panel side-bar__panel--menu">
|
||||
<menu-entry @click.native="exportMarkdown">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div>Export as Markdown</div>
|
||||
<span>Save plain text file.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportHtml">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div>Export as HTML</div>
|
||||
<span>Generate an HTML page from a template.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportPdf">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div><div class="menu-entry__label" :class="{'menu-entry__label--warning': !isSponsor}">sponsor</div> Export as PDF</div>
|
||||
<span>Produce a PDF from an HTML template.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportPandoc">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div><div class="menu-entry__label" :class="{'menu-entry__label--warning': !isSponsor}">sponsor</div> Export with Pandoc</div>
|
||||
<span>Convert to PDF, Word, EPUB...</span>
|
||||
</menu-entry>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import MenuEntry from './common/MenuEntry';
|
||||
import exportSvc from '../../services/exportSvc';
|
||||
import store from '../../store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MenuEntry,
|
||||
},
|
||||
computed: mapGetters(['isSponsor']),
|
||||
methods: {
|
||||
exportMarkdown() {
|
||||
const currentFile = store.getters['file/current'];
|
||||
return exportSvc.exportToDisk(currentFile.id, 'md')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportHtml() {
|
||||
return store.dispatch('modal/open', 'htmlExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportPdf() {
|
||||
return store.dispatch('modal/open', 'pdfExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportPandoc() {
|
||||
return store.dispatch('modal/open', 'pandocExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
@ -20,16 +20,39 @@
|
||||
<span>Convert an HTML file to Markdown.</span>
|
||||
</div>
|
||||
</label>
|
||||
<hr>
|
||||
<menu-entry @click.native="exportMarkdown">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div>Export as Markdown</div>
|
||||
<span>Save plain text file.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportHtml">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div>Export as HTML</div>
|
||||
<span>Generate an HTML page from a template.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportPdf">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div><div class="menu-entry__label" :class="{'menu-entry__label--warning': !isSponsor}">sponsor</div> Export as PDF</div>
|
||||
<span>Produce a PDF from an HTML template.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="exportPandoc">
|
||||
<icon-download slot="icon"></icon-download>
|
||||
<div><div class="menu-entry__label" :class="{'menu-entry__label--warning': !isSponsor}">sponsor</div> Export with Pandoc</div>
|
||||
<span>Convert to PDF, Word, EPUB...</span>
|
||||
</menu-entry>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import TurndownService from 'turndown/lib/turndown.browser.umd';
|
||||
import htmlSanitizer from '../../libs/htmlSanitizer';
|
||||
import MenuEntry from './common/MenuEntry';
|
||||
import Provider from '../../services/providers/common/Provider';
|
||||
import store from '../../store';
|
||||
import workspaceSvc from '../../services/workspaceSvc';
|
||||
import exportSvc from '../../services/exportSvc';
|
||||
|
||||
const turndownService = new TurndownService(store.getters['data/computedSettings'].turndown);
|
||||
|
||||
@ -52,6 +75,7 @@ export default {
|
||||
components: {
|
||||
MenuEntry,
|
||||
},
|
||||
computed: mapGetters(['isSponsor']),
|
||||
methods: {
|
||||
async onImportMarkdown(evt) {
|
||||
const file = evt.target.files[0];
|
||||
@ -73,6 +97,23 @@ export default {
|
||||
});
|
||||
store.commit('file/setCurrentId', item.id);
|
||||
},
|
||||
exportMarkdown() {
|
||||
const currentFile = store.getters['file/current'];
|
||||
return exportSvc.exportToDisk(currentFile.id, 'md')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportHtml() {
|
||||
return store.dispatch('modal/open', 'htmlExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportPdf() {
|
||||
return store.dispatch('modal/open', 'pdfExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
exportPandoc() {
|
||||
return store.dispatch('modal/open', 'pandocExport')
|
||||
.catch(() => { /* Cancel */ });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
@ -75,13 +75,9 @@
|
||||
Markdown cheat sheet
|
||||
</menu-entry>
|
||||
<hr>
|
||||
<menu-entry @click.native="setPanel('import')">
|
||||
<menu-entry @click.native="setPanel('importExport')">
|
||||
<icon-content-save slot="icon"></icon-content-save>
|
||||
Import from disk
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="setPanel('export')">
|
||||
<icon-content-save slot="icon"></icon-content-save>
|
||||
Export to disk
|
||||
Import/export
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="print">
|
||||
<icon-printer slot="icon"></icon-printer>
|
||||
@ -104,19 +100,10 @@
|
||||
<span>Manage access to your external accounts.</span>
|
||||
</menu-entry>
|
||||
<hr>
|
||||
<menu-entry @click.native="exportWorkspace">
|
||||
<menu-entry @click.native="setPanel('workspaceBackup')">
|
||||
<icon-content-save slot="icon"></icon-content-save>
|
||||
Export workspace backup
|
||||
Workspace backup
|
||||
</menu-entry>
|
||||
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
|
||||
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
|
||||
<div class="menu-entry__icon flex flex--column flex--center">
|
||||
<icon-content-save></icon-content-save>
|
||||
</div>
|
||||
<div class="flex flex--column">
|
||||
Import workspace backup
|
||||
</div>
|
||||
</label>
|
||||
<menu-entry @click.native="reset">
|
||||
<icon-logout slot="icon"></icon-logout>
|
||||
<div>Reset application</div>
|
||||
@ -138,8 +125,6 @@ import UserImage from '../UserImage';
|
||||
import googleHelper from '../../services/providers/helpers/googleHelper';
|
||||
import syncSvc from '../../services/syncSvc';
|
||||
import userSvc from '../../services/userSvc';
|
||||
import backupSvc from '../../services/backupSvc';
|
||||
import utils from '../../services/utils';
|
||||
import store from '../../store';
|
||||
|
||||
export default {
|
||||
@ -199,29 +184,6 @@ export default {
|
||||
print() {
|
||||
window.print();
|
||||
},
|
||||
onImportBackup(evt) {
|
||||
const file = evt.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const text = e.target.result;
|
||||
if (text.match(/\uFFFD/)) {
|
||||
store.dispatch('notification/error', 'File is not readable.');
|
||||
} else {
|
||||
backupSvc.importBackup(text);
|
||||
}
|
||||
};
|
||||
const blob = file.slice(0, 10000000);
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
},
|
||||
exportWorkspace() {
|
||||
window.location.href = utils.addQueryParams('app', {
|
||||
...utils.queryParams,
|
||||
exportWorkspace: true,
|
||||
}, true);
|
||||
window.location.reload();
|
||||
},
|
||||
async settings() {
|
||||
try {
|
||||
const settings = await store.dispatch('modal/open', 'settings');
|
||||
|
@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<div class="side-bar__panel side-bar__panel--menu">
|
||||
<menu-entry @click.native="settings">
|
||||
<icon-settings slot="icon"></icon-settings>
|
||||
<div>Settings</div>
|
||||
<span>Tweak application and keyboard shortcuts.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="templates">
|
||||
<icon-code-braces slot="icon"></icon-code-braces>
|
||||
<div><div class="menu-entry__label menu-entry__label--count">{{templateCount}}</div> Templates</div>
|
||||
<span>Configure Handlebars templates for your exports.</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="reset">
|
||||
<icon-logout slot="icon"></icon-logout>
|
||||
<div>Reset application</div>
|
||||
<span>Sign out and clean all workspaces.</span>
|
||||
</menu-entry>
|
||||
<hr>
|
||||
<menu-entry @click.native="exportWorkspace">
|
||||
<icon-content-save slot="icon"></icon-content-save>
|
||||
Export workspace backup
|
||||
</menu-entry>
|
||||
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
|
||||
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
|
||||
<div class="menu-entry__icon flex flex--column flex--center">
|
||||
<icon-content-save></icon-content-save>
|
||||
</div>
|
||||
<div class="flex flex--column">
|
||||
Import workspace backup
|
||||
</div>
|
||||
</label>
|
||||
<hr>
|
||||
<menu-entry href="editor" target="_blank">
|
||||
<icon-open-in-new slot="icon"></icon-open-in-new>
|
||||
<span>StackEdit 4 — deprecated</span>
|
||||
</menu-entry>
|
||||
<menu-entry @click.native="about">
|
||||
<icon-help-circle slot="icon"></icon-help-circle>
|
||||
<span>About StackEdit</span>
|
||||
</menu-entry>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuEntry from './common/MenuEntry';
|
||||
import backupSvc from '../../services/backupSvc';
|
||||
import utils from '../../services/utils';
|
||||
import store from '../../store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MenuEntry,
|
||||
},
|
||||
computed: {
|
||||
templateCount() {
|
||||
return Object.keys(store.getters['data/allTemplatesById']).length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onImportBackup(evt) {
|
||||
const file = evt.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const text = e.target.result;
|
||||
if (text.match(/\uFFFD/)) {
|
||||
store.dispatch('notification/error', 'File is not readable.');
|
||||
} else {
|
||||
backupSvc.importBackup(text);
|
||||
}
|
||||
};
|
||||
const blob = file.slice(0, 10000000);
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
},
|
||||
exportWorkspace() {
|
||||
const url = utils.addQueryParams('app', {
|
||||
...utils.queryParams,
|
||||
exportWorkspace: true,
|
||||
}, true);
|
||||
window.location.href = url;
|
||||
window.location.reload(true);
|
||||
},
|
||||
async settings() {
|
||||
try {
|
||||
const settings = await store.dispatch('modal/open', 'settings');
|
||||
store.dispatch('data/setSettings', settings);
|
||||
} catch (e) {
|
||||
// Cancel
|
||||
}
|
||||
},
|
||||
async templates() {
|
||||
try {
|
||||
const { templates } = await store.dispatch('modal/open', 'templates');
|
||||
store.dispatch('data/setTemplatesById', templates);
|
||||
} catch (e) {
|
||||
// Cancel
|
||||
}
|
||||
},
|
||||
async reset() {
|
||||
try {
|
||||
await store.dispatch('modal/open', 'reset');
|
||||
window.location.href = '#reset=true';
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
// Cancel
|
||||
}
|
||||
},
|
||||
about() {
|
||||
store.dispatch('modal/open', 'about');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
@ -21,7 +21,7 @@
|
||||
</menu-entry>
|
||||
</div>
|
||||
<hr>
|
||||
<div v-for="token in bloggerTokens" :key="token.sub">
|
||||
<div v-for="token in bloggerTokens" :key="'blogger-' + token.sub">
|
||||
<menu-entry @click.native="publishBlogger(token)">
|
||||
<icon-provider slot="icon" provider-id="blogger"></icon-provider>
|
||||
<div>Publish to Blogger</div>
|
||||
|
55
src/components/menus/WorkspaceBackupMenu.vue
Normal file
55
src/components/menus/WorkspaceBackupMenu.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="side-bar__panel side-bar__panel--menu">
|
||||
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
|
||||
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
|
||||
<div class="menu-entry__icon flex flex--column flex--center">
|
||||
<icon-content-save></icon-content-save>
|
||||
</div>
|
||||
<div class="flex flex--column">
|
||||
Import workspace backup
|
||||
</div>
|
||||
</label>
|
||||
<menu-entry @click.native="exportWorkspace">
|
||||
<icon-content-save slot="icon"></icon-content-save>
|
||||
Export workspace backup
|
||||
</menu-entry>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MenuEntry from './common/MenuEntry';
|
||||
import utils from '../../services/utils';
|
||||
import store from '../../store';
|
||||
import backupSvc from '../../services/backupSvc';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MenuEntry,
|
||||
},
|
||||
methods: {
|
||||
onImportBackup(evt) {
|
||||
const file = evt.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const text = e.target.result;
|
||||
if (text.match(/\uFFFD/)) {
|
||||
store.dispatch('notification/error', 'File is not readable.');
|
||||
} else {
|
||||
backupSvc.importBackup(text);
|
||||
}
|
||||
};
|
||||
const blob = file.slice(0, 10000000);
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
},
|
||||
exportWorkspace() {
|
||||
window.location.href = utils.addQueryParams('app', {
|
||||
...utils.queryParams,
|
||||
exportWorkspace: true,
|
||||
}, true);
|
||||
window.location.reload();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
5
src/icons/DotsHorizontal.vue
Normal file
5
src/icons/DotsHorizontal.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M 16,12C 16,10.8954 16.8954,10 18,10C 19.1046,10 20,10.8954 20,12C 20,13.1046 19.1046,14 18,14C 16.8954,14 16,13.1046 16,12 Z M 10,12C 10,10.8954 10.8954,10 12,10C 13.1046,10 14,10.8954 14,12C 14,13.1046 13.1046,14 12,14C 10.8954,14 10,13.1046 10,12 Z M 4,12C 4,10.8954 4.89543,10 6,10C 7.10457,10 8,10.8954 8,12C 8,13.1046 7.10457,14 6,14C 4.89543,14 4,13.1046 4,12 Z "/>
|
||||
</svg>
|
||||
</template>
|
@ -52,6 +52,7 @@ import FormatListChecks from './FormatListChecks';
|
||||
import CheckCircle from './CheckCircle';
|
||||
import ContentCopy from './ContentCopy';
|
||||
import Key from './Key';
|
||||
import DotsHorizontal from './DotsHorizontal';
|
||||
|
||||
Vue.component('iconProvider', Provider);
|
||||
Vue.component('iconFormatBold', FormatBold);
|
||||
@ -106,3 +107,4 @@ Vue.component('iconFormatListChecks', FormatListChecks);
|
||||
Vue.component('iconCheckCircle', CheckCircle);
|
||||
Vue.component('iconContentCopy', ContentCopy);
|
||||
Vue.component('iconKey', Key);
|
||||
Vue.component('iconDotsHorizontal', DotsHorizontal);
|
||||
|
@ -7,7 +7,6 @@ const clientId = GOOGLE_CLIENT_ID;
|
||||
const apiKey = 'AIzaSyC_M4RA9pY6XmM9pmFxlT59UPMO7aHr9kk';
|
||||
const appsDomain = null;
|
||||
const tokenExpirationMargin = 5 * 60 * 1000; // 5 min (tokens expire after 1h)
|
||||
let googlePlusNotification = true;
|
||||
|
||||
const driveAppDataScopes = ['https://www.googleapis.com/auth/drive.appdata'];
|
||||
const getDriveScopes = token => [token.driveFullAccess
|
||||
@ -40,17 +39,18 @@ if (utils.queryParams.providerId === 'googleDrive') {
|
||||
* https://developers.google.com/people/api/rest/v1/people/get
|
||||
*/
|
||||
const getUser = async (sub, token) => {
|
||||
const url = `https://people.googleapis.com/v1/people/${sub}?personFields=names,photos`;
|
||||
const { body } = await networkSvc.request(token
|
||||
? {
|
||||
method: 'GET',
|
||||
url: `https://people.googleapis.com/v1/people/${sub}`,
|
||||
url,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
},
|
||||
}
|
||||
: {
|
||||
method: 'GET',
|
||||
url: `https://people.googleapis.com/v1/people/${sub}?key=${apiKey}`,
|
||||
url: `${url}&key=${apiKey}`,
|
||||
}, true);
|
||||
return body;
|
||||
};
|
||||
@ -60,10 +60,12 @@ userSvc.setInfoResolver('google', subPrefix, async (sub) => {
|
||||
try {
|
||||
const googleToken = Object.values(store.getters['data/googleTokensBySub'])[0];
|
||||
const body = await getUser(sub, googleToken);
|
||||
const name = body.names[0] || {};
|
||||
const photo = body.photos[0] || {};
|
||||
return {
|
||||
id: `${subPrefix}:${body.id}`,
|
||||
name: body.displayName,
|
||||
imageUrl: (body.image.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
||||
id: `${subPrefix}:${sub}`,
|
||||
name: name.displayName,
|
||||
imageUrl: (photo.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.status !== 404) {
|
||||
@ -141,41 +143,52 @@ export default {
|
||||
}
|
||||
|
||||
// Build token object including scopes and sub
|
||||
const existingToken = store.getters['data/googleTokensBySub'][body.sub] || {
|
||||
scopes: [],
|
||||
};
|
||||
const mergedScopes = [...new Set([...scopes, ...existingToken.scopes])];
|
||||
const existingToken = store.getters['data/googleTokensBySub'][body.sub];
|
||||
const token = {
|
||||
scopes: mergedScopes,
|
||||
scopes,
|
||||
accessToken,
|
||||
expiresOn: Date.now() + (expiresIn * 1000),
|
||||
idToken,
|
||||
sub: body.sub,
|
||||
name: existingToken.name || 'Unknown',
|
||||
isLogin: existingToken.isLogin || (!store.getters['workspace/mainWorkspaceToken'] &&
|
||||
mergedScopes.indexOf('https://www.googleapis.com/auth/drive.appdata') !== -1),
|
||||
isSponsor: existingToken.isSponsor || false,
|
||||
isDrive: mergedScopes.indexOf('https://www.googleapis.com/auth/drive') !== -1 ||
|
||||
mergedScopes.indexOf('https://www.googleapis.com/auth/drive.file') !== -1,
|
||||
isBlogger: mergedScopes.indexOf('https://www.googleapis.com/auth/blogger') !== -1,
|
||||
isPhotos: mergedScopes.indexOf('https://www.googleapis.com/auth/photos') !== -1,
|
||||
driveFullAccess: mergedScopes.indexOf('https://www.googleapis.com/auth/drive') !== -1,
|
||||
name: (existingToken || {}).name || 'Unknown',
|
||||
isLogin: !store.getters['workspace/mainWorkspaceToken'] &&
|
||||
scopes.indexOf('https://www.googleapis.com/auth/drive.appdata') !== -1,
|
||||
isSponsor: false,
|
||||
isDrive: scopes.indexOf('https://www.googleapis.com/auth/drive') !== -1 ||
|
||||
scopes.indexOf('https://www.googleapis.com/auth/drive.file') !== -1,
|
||||
isBlogger: scopes.indexOf('https://www.googleapis.com/auth/blogger') !== -1,
|
||||
isPhotos: scopes.indexOf('https://www.googleapis.com/auth/photos') !== -1,
|
||||
driveFullAccess: scopes.indexOf('https://www.googleapis.com/auth/drive') !== -1,
|
||||
};
|
||||
|
||||
// Call the user info endpoint
|
||||
const user = await getUser('me', token);
|
||||
if (user.displayName) {
|
||||
token.name = user.displayName;
|
||||
} else if (googlePlusNotification) {
|
||||
store.dispatch('notification/info', 'Please activate Google Plus to change your account name and photo.');
|
||||
googlePlusNotification = false;
|
||||
const userId = user.resourceName.split('/')[1];
|
||||
const name = user.names[0] || {};
|
||||
const photo = user.photos[0] || {};
|
||||
if (name.displayName) {
|
||||
token.name = name.displayName;
|
||||
}
|
||||
userSvc.addInfo({
|
||||
id: `${subPrefix}:${user.id}`,
|
||||
name: user.displayName,
|
||||
imageUrl: (user.image.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
||||
id: `${subPrefix}:${userId}`,
|
||||
name: name.displayName,
|
||||
imageUrl: (photo.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
||||
});
|
||||
|
||||
if (existingToken) {
|
||||
// We probably retrieved a new token with restricted scopes.
|
||||
// That's no problem, token will be refreshed later with merged scopes.
|
||||
// Restore flags
|
||||
Object.assign(token, {
|
||||
isLogin: existingToken.isLogin || token.isLogin,
|
||||
isSponsor: existingToken.isSponsor,
|
||||
isDrive: existingToken.isDrive || token.isDrive,
|
||||
isBlogger: existingToken.isBlogger || token.isBlogger,
|
||||
isPhotos: existingToken.isPhotos || token.isPhotos,
|
||||
driveFullAccess: existingToken.driveFullAccess || token.driveFullAccess,
|
||||
});
|
||||
}
|
||||
|
||||
if (token.isLogin) {
|
||||
try {
|
||||
token.isSponsor = (await networkSvc.request({
|
||||
|
@ -34,17 +34,24 @@ const empty = (id) => {
|
||||
};
|
||||
|
||||
// Item IDs that will be stored in the localStorage
|
||||
const lsItemIdSet = new Set(constants.localStorageDataIds);
|
||||
const localStorageIdSet = new Set(constants.localStorageDataIds);
|
||||
|
||||
// Getter/setter/patcher factories
|
||||
const getter = id => state => ((lsItemIdSet.has(id)
|
||||
const getter = id => (state) => {
|
||||
const itemsById = localStorageIdSet.has(id)
|
||||
? state.lsItemsById
|
||||
: state.itemsById)[id] || {}).data || empty(id).data;
|
||||
: state.itemsById;
|
||||
if (itemsById[id]) {
|
||||
return itemsById[id].data;
|
||||
}
|
||||
return empty(id).data;
|
||||
};
|
||||
const setter = id => ({ commit }, data) => commit('setItem', itemTemplate(id, data));
|
||||
const patcher = id => ({ state, commit }, data) => {
|
||||
const item = Object.assign(empty(id), (lsItemIdSet.has(id)
|
||||
const itemsById = localStorageIdSet.has(id)
|
||||
? state.lsItemsById
|
||||
: state.itemsById)[id]);
|
||||
: state.itemsById;
|
||||
const item = Object.assign(empty(id), itemsById[id]);
|
||||
commit('setItem', {
|
||||
...empty(id),
|
||||
data: typeof data === 'object' ? {
|
||||
@ -116,7 +123,7 @@ export default {
|
||||
});
|
||||
|
||||
// Store item in itemsById or lsItemsById if its stored in the localStorage
|
||||
Vue.set(lsItemIdSet.has(item.id) ? lsItemsById : itemsById, item.id, item);
|
||||
Vue.set(localStorageIdSet.has(item.id) ? lsItemsById : itemsById, item.id, item);
|
||||
},
|
||||
deleteItem({ itemsById }, id) {
|
||||
// Only used by localDbSvc to clean itemsById from object moved to localStorage
|
||||
@ -196,6 +203,7 @@ export default {
|
||||
gitlabTokensBySub: (state, { tokensByType }) => tokensByType.gitlab || {},
|
||||
wordpressTokensBySub: (state, { tokensByType }) => tokensByType.wordpress || {},
|
||||
zendeskTokensBySub: (state, { tokensByType }) => tokensByType.zendesk || {},
|
||||
badgesByFeatureId: getter('badges'),
|
||||
},
|
||||
actions: {
|
||||
setSettings: setter('settings'),
|
||||
|
Loading…
Reference in New Issue
Block a user