主文档空间支持GitHub登录
This commit is contained in:
parent
97b8d3c288
commit
39167fb193
@ -78,10 +78,10 @@ StackEdit中文版
|
|||||||
- 支持分享文档(2023-03-30)
|
- 支持分享文档(2023-03-30)
|
||||||
- 支持ChatGPT生成内容(2023-04-10)
|
- 支持ChatGPT生成内容(2023-04-10)
|
||||||
- GitLab授权接口调整(2023-08-26)
|
- GitLab授权接口调整(2023-08-26)
|
||||||
|
- 主文档空间支持GitHub登录(2023-10-19)
|
||||||
|
|
||||||
## 国外开源版本弊端:
|
## 国外开源版本弊端:
|
||||||
- 作者已经不维护了
|
- 作者已经不维护了或很少维护了
|
||||||
- Github授权登录存在问题
|
|
||||||
- 不支持国内常用Gitee
|
- 不支持国内常用Gitee
|
||||||
- 强依赖GoogleDrive,而Google Drive在国内不能正常访问
|
- 强依赖GoogleDrive,而Google Drive在国内不能正常访问
|
||||||
|
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stackedit",
|
"name": "stackedit",
|
||||||
"version": "5.15.20",
|
"version": "5.15.21",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stackedit",
|
"name": "stackedit",
|
||||||
"version": "5.15.20",
|
"version": "5.15.21",
|
||||||
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
|
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
|
||||||
"author": "Benoit Schweblin, 豆萁",
|
"author": "Benoit Schweblin, 豆萁",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
@ -72,6 +72,7 @@ module.exports = (app) => {
|
|||||||
}));
|
}));
|
||||||
// Serve share.html
|
// Serve share.html
|
||||||
app.get('/share.html', (req, res) => res.sendFile(resolvePath('static/landing/share.html')));
|
app.get('/share.html', (req, res) => res.sendFile(resolvePath('static/landing/share.html')));
|
||||||
|
app.get('/gistshare.html', (req, res) => res.sendFile(resolvePath('static/landing/gistshare.html')));
|
||||||
|
|
||||||
// Serve static resources
|
// Serve static resources
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<div class="modal__button-bar">
|
<div class="modal__button-bar">
|
||||||
<button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button>
|
<button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button>
|
||||||
<button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button>
|
<button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button>
|
||||||
|
<button v-for="(item, idx) in (simpleModal.resolveArray || [])" class="button button--resolve" @click="config.resolve(item.value)">{{item.text}}</button>
|
||||||
</div>
|
</div>
|
||||||
</modal-inner>
|
</modal-inner>
|
||||||
</div>
|
</div>
|
||||||
@ -187,6 +188,7 @@ export default {
|
|||||||
// User has to sign in
|
// User has to sign in
|
||||||
await store.dispatch('modal/open', 'signInForSponsorship');
|
await store.dispatch('modal/open', 'signInForSponsorship');
|
||||||
await giteeHelper.signin();
|
await giteeHelper.signin();
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
}
|
}
|
||||||
if (!store.getters.isSponsor) {
|
if (!store.getters.isSponsor) {
|
||||||
|
@ -26,6 +26,7 @@ import store from '../store';
|
|||||||
import DropdownMenu from './common/DropdownMenu';
|
import DropdownMenu from './common/DropdownMenu';
|
||||||
import publishSvc from '../services/publishSvc';
|
import publishSvc from '../services/publishSvc';
|
||||||
import giteeGistProvider from '../services/providers/giteeGistProvider';
|
import giteeGistProvider from '../services/providers/giteeGistProvider';
|
||||||
|
import gistProvider from '../services/providers/gistProvider';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -107,12 +108,15 @@ export default {
|
|||||||
store.dispatch('notification/info', '登录主文档空间之后才可使用分享功能!');
|
store.dispatch('notification/info', '登录主文档空间之后才可使用分享功能!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let giteeGistId = null;
|
let tempGistId = null;
|
||||||
const filterLocations = this.publishLocations.filter(it => it.providerId === 'giteegist' && it.url && it.gistId);
|
const isGithub = mainToken.providerId === 'githubAppData';
|
||||||
|
const gistProviderId = isGithub ? 'gist' : 'giteegist';
|
||||||
|
const filterLocations = this.publishLocations.filter(it => it.providerId === gistProviderId
|
||||||
|
&& it.url && it.gistId);
|
||||||
if (filterLocations.length > 0) {
|
if (filterLocations.length > 0) {
|
||||||
giteeGistId = filterLocations[0].gistId;
|
tempGistId = filterLocations[0].gistId;
|
||||||
}
|
}
|
||||||
const location = giteeGistProvider.makeLocation(
|
const location = (isGithub ? gistProvider : giteeGistProvider).makeLocation(
|
||||||
mainToken,
|
mainToken,
|
||||||
`分享-${currentFile.name}`,
|
`分享-${currentFile.name}`,
|
||||||
true,
|
true,
|
||||||
@ -120,9 +124,10 @@ export default {
|
|||||||
);
|
);
|
||||||
location.templateId = 'styledHtmlWithTheme';
|
location.templateId = 'styledHtmlWithTheme';
|
||||||
location.fileId = currentFile.id;
|
location.fileId = currentFile.id;
|
||||||
location.gistId = giteeGistId;
|
location.gistId = tempGistId;
|
||||||
const { gistId } = await publishSvc.publishLocationAndStore(location);
|
const { gistId } = await publishSvc.publishLocationAndStore(location);
|
||||||
const url = `${window.location.protocol}//${window.location.host}/share.html?id=${gistId}`;
|
const sharePage = mainToken.providerId === 'githubAppData' ? 'gistshare.html' : 'share.html';
|
||||||
|
const url = `${window.location.protocol}//${window.location.host}/${sharePage}?id=${gistId}`;
|
||||||
await store.dispatch('modal/open', { type: 'shareHtml', name: currentFile.name, url });
|
await store.dispatch('modal/open', { type: 'shareHtml', name: currentFile.name, url });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> 以同步您的主文档空间。</p>
|
<p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> 或 <a href="javascript:void(0)" @click="signinWithGithub">登录 GitHub</a> 以同步您的主文档空间。</p>
|
||||||
<p v-else-if="loading">历史版本加载中…</p>
|
<p v-else-if="loading">历史版本加载中…</p>
|
||||||
<p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p>
|
<p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p>
|
||||||
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else>
|
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else>
|
||||||
@ -55,6 +55,7 @@ import EditorClassApplier from '../common/EditorClassApplier';
|
|||||||
import PreviewClassApplier from '../common/PreviewClassApplier';
|
import PreviewClassApplier from '../common/PreviewClassApplier';
|
||||||
import utils from '../../services/utils';
|
import utils from '../../services/utils';
|
||||||
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
||||||
|
import githubHelper from '../../services/providers/helpers/githubHelper';
|
||||||
import syncSvc from '../../services/syncSvc';
|
import syncSvc from '../../services/syncSvc';
|
||||||
import store from '../../store';
|
import store from '../../store';
|
||||||
import badgeSvc from '../../services/badgeSvc';
|
import badgeSvc from '../../services/badgeSvc';
|
||||||
@ -168,6 +169,16 @@ export default {
|
|||||||
async signin() {
|
async signin() {
|
||||||
try {
|
try {
|
||||||
await giteeHelper.signin();
|
await giteeHelper.signin();
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
|
syncSvc.requestSync();
|
||||||
|
} catch (e) {
|
||||||
|
// Cancel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async signinWithGithub() {
|
||||||
|
try {
|
||||||
|
await githubHelper.signin();
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Cancel
|
// Cancel
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
<span v-if="currentWorkspace.providerId === 'giteeAppData'">
|
<span v-if="currentWorkspace.providerId === 'giteeAppData'">
|
||||||
<b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步。
|
<b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步。
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="currentWorkspace.providerId === 'githubAppData'">
|
||||||
|
<b>{{currentWorkspace.name}}</b> 与您的 GitHub 默认文档空间仓库同步。
|
||||||
|
</span>
|
||||||
<span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'">
|
<span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'">
|
||||||
<b>{{currentWorkspace.name}}</b> 与 <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步。
|
<b>{{currentWorkspace.name}}</b> 与 <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步。
|
||||||
</span>
|
</span>
|
||||||
@ -45,6 +48,11 @@
|
|||||||
<div>使用 Gitee 登录</div>
|
<div>使用 Gitee 登录</div>
|
||||||
<span>同步您的主文档空间并解锁功能。</span>
|
<span>同步您的主文档空间并解锁功能。</span>
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
|
<menu-entry v-if="!loginToken" @click.native="signinWithGithub">
|
||||||
|
<icon-login slot="icon"></icon-login>
|
||||||
|
<div>使用 GitHub 登录</div>
|
||||||
|
<span>同步您的主文档空间并解锁功能。</span>
|
||||||
|
</menu-entry>
|
||||||
<menu-entry @click.native="setPanel('workspaces')">
|
<menu-entry @click.native="setPanel('workspaces')">
|
||||||
<icon-database slot="icon"></icon-database>
|
<icon-database slot="icon"></icon-database>
|
||||||
<div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div>
|
<div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div>
|
||||||
@ -142,6 +150,7 @@ import MenuEntry from './common/MenuEntry';
|
|||||||
import providerRegistry from '../../services/providers/common/providerRegistry';
|
import providerRegistry from '../../services/providers/common/providerRegistry';
|
||||||
import UserImage from '../UserImage';
|
import UserImage from '../UserImage';
|
||||||
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
import giteeHelper from '../../services/providers/helpers/giteeHelper';
|
||||||
|
import githubHelper from '../../services/providers/helpers/githubHelper';
|
||||||
import syncSvc from '../../services/syncSvc';
|
import syncSvc from '../../services/syncSvc';
|
||||||
import userSvc from '../../services/userSvc';
|
import userSvc from '../../services/userSvc';
|
||||||
import store from '../../store';
|
import store from '../../store';
|
||||||
@ -194,6 +203,16 @@ export default {
|
|||||||
async signin() {
|
async signin() {
|
||||||
try {
|
try {
|
||||||
await giteeHelper.signin();
|
await giteeHelper.signin();
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
|
syncSvc.requestSync();
|
||||||
|
} catch (e) {
|
||||||
|
// Cancel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async signinWithGithub() {
|
||||||
|
try {
|
||||||
|
await githubHelper.signin();
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Cancel
|
// Cancel
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div class="workspace" v-for="(workspace, id) in workspacesById" :key="id">
|
<div class="workspace" v-for="(workspace, id) in workspacesById" :key="id">
|
||||||
<menu-entry :href="workspace.url" target="_blank">
|
<menu-entry :href="workspace.url" target="_blank">
|
||||||
<icon-provider slot="icon" :provider-id="workspace.providerId"></icon-provider>
|
<icon-provider v-if="id === 'main' && !workspace.sub" slot="icon" :provider-id="'stackedit'"></icon-provider>
|
||||||
|
<icon-provider v-else slot="icon" :provider-id="workspace.providerId"></icon-provider>
|
||||||
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div>
|
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div>
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
</div>
|
</div>
|
||||||
|
@ -89,14 +89,14 @@ export default {
|
|||||||
badgeSvc.addBadge('removePublishLocation');
|
badgeSvc.addBadge('removePublishLocation');
|
||||||
},
|
},
|
||||||
shareUrl(location) {
|
shareUrl(location) {
|
||||||
if (location.providerId !== 'giteegist') {
|
if (location.providerId !== 'giteegist' && location.providerId !== 'gist') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!location.url) {
|
if (!location.url || !location.gistId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const splitIndex = location.url.lastIndexOf('/');
|
const sharePage = location.providerId === 'gist' ? 'gistshare.html' : 'share.html';
|
||||||
return `${window.location.protocol}//${window.location.host}/share.html?id=${location.url.substr(splitIndex + 1)}`;
|
return `${window.location.protocol}//${window.location.host}/${sharePage}?id=${location.gistId}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
<div class="flex flex--column">
|
<div class="flex flex--column">
|
||||||
<div class="workspace-entry__header flex flex--row flex--align-center">
|
<div class="workspace-entry__header flex flex--row flex--align-center">
|
||||||
<div class="workspace-entry__icon">
|
<div class="workspace-entry__icon">
|
||||||
<icon-provider :provider-id="workspace.providerId"></icon-provider>
|
<icon-provider v-if="id === 'main' && !workspace.sub" :provider-id="'stackedit'"></icon-provider>
|
||||||
|
<icon-provider v-else :provider-id="workspace.providerId"></icon-provider>
|
||||||
</div>
|
</div>
|
||||||
<input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
|
<input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
|
||||||
<div class="workspace-entry__name" v-else>{{workspace.name}}</div>
|
<div class="workspace-entry__name" v-else>{{workspace.name}}</div>
|
||||||
@ -17,7 +18,7 @@
|
|||||||
<button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'">
|
<button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'">
|
||||||
<icon-pen></icon-pen>
|
<icon-pen></icon-pen>
|
||||||
</button>
|
</button>
|
||||||
<template v-if="workspace.providerId === 'giteeAppData' || workspace.providerId === 'githubWorkspace'
|
<template v-if="workspace.providerId === 'giteeAppData' || workspace.providerId === 'githubAppData' || workspace.providerId === 'githubWorkspace'
|
||||||
|| workspace.providerId === 'giteeWorkspace' || workspace.providerId === 'gitlabWorkspace' || workspace.providerId === 'giteaWorkspace'">
|
|| workspace.providerId === 'giteeWorkspace' || workspace.providerId === 'gitlabWorkspace' || workspace.providerId === 'giteaWorkspace'">
|
||||||
<button class="workspace-entry__button button" @click="stopAutoSync(id)" v-if="workspace.autoSync == undefined || workspace.autoSync" v-title="'关闭自动同步'">
|
<button class="workspace-entry__button button" @click="stopAutoSync(id)" v-if="workspace.autoSync == undefined || workspace.autoSync" v-title="'关闭自动同步'">
|
||||||
<icon-sync-auto></icon-sync-auto>
|
<icon-sync-auto></icon-sync-auto>
|
||||||
|
@ -68,6 +68,8 @@ shortcuts:
|
|||||||
mod+shift+t: table
|
mod+shift+t: table
|
||||||
mod+shift+u: ulist
|
mod+shift+u: ulist
|
||||||
mod+shift+f: inlineformula
|
mod+shift+f: inlineformula
|
||||||
|
# 切换编辑与预览模式
|
||||||
|
mod+shift+e: toggleeditor
|
||||||
'= = > space':
|
'= = > space':
|
||||||
method: expand
|
method: expand
|
||||||
params:
|
params:
|
||||||
|
@ -158,7 +158,19 @@ export default [
|
|||||||
new Feature(
|
new Feature(
|
||||||
'sponsor',
|
'sponsor',
|
||||||
'赞助',
|
'赞助',
|
||||||
'使用 Google 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)',
|
'使用 Gitee 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
new Feature(
|
||||||
|
'githubSignIn',
|
||||||
|
'登录',
|
||||||
|
'使用 Gitee 登录,同步您的主文档空间并解锁功能。',
|
||||||
|
[
|
||||||
|
new Feature(
|
||||||
|
'githubSyncMainWorkspace',
|
||||||
|
'主文档空间已同步',
|
||||||
|
'使用 GitHub 登录以将您的主文档空间与您的默认空间stackedit-app-data仓库数据同步。',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const simpleModal = (contentHtml, rejectText, resolveText) => ({
|
const simpleModal = (contentHtml, rejectText, resolveText, resolveArray) => ({
|
||||||
contentHtml: typeof contentHtml === 'function' ? contentHtml : () => contentHtml,
|
contentHtml: typeof contentHtml === 'function' ? contentHtml : () => contentHtml,
|
||||||
rejectText,
|
rejectText,
|
||||||
|
resolveArray,
|
||||||
resolveText,
|
resolveText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,21 +66,35 @@ export default {
|
|||||||
'关闭窗口',
|
'关闭窗口',
|
||||||
),
|
),
|
||||||
shareHtmlPre: simpleModal(
|
shareHtmlPre: simpleModal(
|
||||||
config => `<p>将给文档 "${config.name}" 创建分享链接,创建后将会把文档公开发布到GiteeGist中。您确定吗?</p>`,
|
config => `<p>将给文档 "${config.name}" 创建分享链接,创建后将会把文档公开发布到默认空间账号的Gist中。您确定吗?</p>`,
|
||||||
'取消',
|
'取消',
|
||||||
'确认分享',
|
'确认分享',
|
||||||
),
|
),
|
||||||
signInForComment: simpleModal(
|
signInForComment: simpleModal(
|
||||||
`<p>您必须使用 Google 登录才能开始评论。</p>
|
`<p>您必须使用 Gitee或GitHub 登录默认文档空间后才能开始评论。</p>
|
||||||
<div class="modal__info"><b>注意:</b> 这将同步您的主文档空间。</div>`,
|
<div class="modal__info"><b>注意:</b> 这将同步您的主文档空间。</div>`,
|
||||||
'取消',
|
'取消',
|
||||||
'确认登录',
|
'',
|
||||||
|
[{
|
||||||
|
text: 'Gitee登录',
|
||||||
|
value: 'gitee',
|
||||||
|
}, {
|
||||||
|
text: 'GitHub登录',
|
||||||
|
value: 'github',
|
||||||
|
}],
|
||||||
),
|
),
|
||||||
signInForSponsorship: simpleModal(
|
signInForSponsorship: simpleModal(
|
||||||
`<p>您必须使用 Google 登录才能赞助。</p>
|
`<p>您必须使用 Gitee或GitHub 登录才能赞助。</p>
|
||||||
<div class="modal__info"><b>注意:</b> 这将同步您的主文档空间。</div>`,
|
<div class="modal__info"><b>注意:</b> 这将同步您的主文档空间。</div>`,
|
||||||
'取消',
|
'取消',
|
||||||
'确认登录',
|
'',
|
||||||
|
[{
|
||||||
|
text: 'Gitee登录',
|
||||||
|
value: 'gitee',
|
||||||
|
}, {
|
||||||
|
text: 'GitHub登录',
|
||||||
|
value: 'github',
|
||||||
|
}],
|
||||||
),
|
),
|
||||||
sponsorOnly: simpleModal(
|
sponsorOnly: simpleModal(
|
||||||
'<p>此功能仅限于赞助商,因为它依赖于服务器资源。</p>',
|
'<p>此功能仅限于赞助商,因为它依赖于服务器资源。</p>',
|
||||||
|
@ -15,6 +15,7 @@ export default {
|
|||||||
return 'google-drive';
|
return 'google-drive';
|
||||||
case 'googlePhotos':
|
case 'googlePhotos':
|
||||||
return 'google-photos';
|
return 'google-photos';
|
||||||
|
case 'githubAppData':
|
||||||
case 'githubWorkspace':
|
case 'githubWorkspace':
|
||||||
return 'github';
|
return 'github';
|
||||||
case 'gist':
|
case 'gist':
|
||||||
@ -31,6 +32,8 @@ export default {
|
|||||||
case 'giteeWorkspace':
|
case 'giteeWorkspace':
|
||||||
case 'giteegist':
|
case 'giteegist':
|
||||||
return 'gitee';
|
return 'gitee';
|
||||||
|
case 'stackedit':
|
||||||
|
return 'stackedit';
|
||||||
default:
|
default:
|
||||||
return this.providerId;
|
return this.providerId;
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ import store from '../../store';
|
|||||||
import editorSvc from '../../services/editorSvc';
|
import editorSvc from '../../services/editorSvc';
|
||||||
import syncSvc from '../../services/syncSvc';
|
import syncSvc from '../../services/syncSvc';
|
||||||
|
|
||||||
// Skip shortcuts if modal is open or editor is hidden
|
// Skip shortcuts if modal is open
|
||||||
Mousetrap.prototype.stopCallback = () => store.getters['modal/config'] || !store.getters['content/isCurrentEditable'];
|
Mousetrap.prototype.stopCallback = () => store.getters['modal/config'];
|
||||||
|
|
||||||
const pagedownHandler = name => () => {
|
const pagedownHandler = name => () => {
|
||||||
editorSvc.pagedownEditor.uiManager.doClick(name);
|
editorSvc.pagedownEditor.uiManager.doClick(name);
|
||||||
@ -20,6 +20,14 @@ const findReplaceOpener = type => () => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleEditor = () => () => {
|
||||||
|
store.dispatch('data/toggleEditor', !store.getters['data/layoutSettings'].showEditor);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 非编辑模式下支持的快捷键
|
||||||
|
const noEditableShortcutMethods = ['toggleeditor'];
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
bold: pagedownHandler('bold'),
|
bold: pagedownHandler('bold'),
|
||||||
italic: pagedownHandler('italic'),
|
italic: pagedownHandler('italic'),
|
||||||
@ -36,6 +44,7 @@ const methods = {
|
|||||||
inline: pagedownHandler('heading'),
|
inline: pagedownHandler('heading'),
|
||||||
hr: pagedownHandler('hr'),
|
hr: pagedownHandler('hr'),
|
||||||
inlineformula: pagedownHandler('inlineformula'),
|
inlineformula: pagedownHandler('inlineformula'),
|
||||||
|
toggleeditor: toggleEditor(),
|
||||||
sync() {
|
sync() {
|
||||||
if (syncSvc.isSyncPossible()) {
|
if (syncSvc.isSyncPossible()) {
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
@ -80,7 +89,10 @@ store.watch(
|
|||||||
}
|
}
|
||||||
if (Object.prototype.hasOwnProperty.call(methods, method)) {
|
if (Object.prototype.hasOwnProperty.call(methods, method)) {
|
||||||
try {
|
try {
|
||||||
Mousetrap.bind(`${key}`, () => !methods[method].apply(null, params));
|
// editor is editable or 一些非编辑模式下支持的快捷键
|
||||||
|
if (store.getters['content/isCurrentEditable'] || noEditableShortcutMethods.indexOf(method) !== -1) {
|
||||||
|
Mousetrap.bind(`${key}`, () => !methods[method].apply(null, params));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
|
292
src/services/providers/githubAppDataProvider.js
Normal file
292
src/services/providers/githubAppDataProvider.js
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
import store from '../../store';
|
||||||
|
import githubHelper from './helpers/githubHelper';
|
||||||
|
import Provider from './common/Provider';
|
||||||
|
import gitWorkspaceSvc from '../gitWorkspaceSvc';
|
||||||
|
import userSvc from '../userSvc';
|
||||||
|
|
||||||
|
const appDataRepo = 'stackedit-app-data';
|
||||||
|
const appDataBranch = 'master';
|
||||||
|
|
||||||
|
export default new Provider({
|
||||||
|
id: 'githubAppData',
|
||||||
|
name: 'Gitee应用数据',
|
||||||
|
getToken() {
|
||||||
|
return store.getters['workspace/syncToken'];
|
||||||
|
},
|
||||||
|
getWorkspaceParams() {
|
||||||
|
// No param as it's the main workspace
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
getWorkspaceLocationUrl() {
|
||||||
|
// No direct link to app data
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
getSyncDataUrl() {
|
||||||
|
// No direct link to app data
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
getSyncDataDescription({ id }) {
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
async initWorkspace() {
|
||||||
|
// Nothing much to do since the main workspace isn't necessarily synchronized
|
||||||
|
// Return the main workspace
|
||||||
|
return store.getters['workspace/workspacesById'].main;
|
||||||
|
},
|
||||||
|
getChanges() {
|
||||||
|
const token = this.getToken();
|
||||||
|
return githubHelper.getTree({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prepareChanges(tree) {
|
||||||
|
return gitWorkspaceSvc.makeChanges(tree);
|
||||||
|
},
|
||||||
|
async saveWorkspaceItem({ item }) {
|
||||||
|
const syncData = {
|
||||||
|
id: store.getters.gitPathsByItemId[item.id],
|
||||||
|
type: item.type,
|
||||||
|
hash: item.hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Files and folders are not in git, only contents
|
||||||
|
if (item.type === 'file' || item.type === 'folder') {
|
||||||
|
return { syncData };
|
||||||
|
}
|
||||||
|
|
||||||
|
// locations are stored as paths, so we upload an empty file
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
await githubHelper.uploadFile({
|
||||||
|
owner: syncToken.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token: syncToken,
|
||||||
|
path: syncData.id,
|
||||||
|
content: '',
|
||||||
|
sha: gitWorkspaceSvc.shaByPath[syncData.id],
|
||||||
|
commitMessage: item.commitMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return sync data to save
|
||||||
|
return { syncData };
|
||||||
|
},
|
||||||
|
async removeWorkspaceItem({ syncData }) {
|
||||||
|
if (gitWorkspaceSvc.shaByPath[syncData.id]) {
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
await githubHelper.removeFile({
|
||||||
|
owner: syncToken.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token: syncToken,
|
||||||
|
path: syncData.id,
|
||||||
|
sha: gitWorkspaceSvc.shaByPath[syncData.id],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async downloadWorkspaceContent({
|
||||||
|
token,
|
||||||
|
contentId,
|
||||||
|
contentSyncData,
|
||||||
|
fileSyncData,
|
||||||
|
}) {
|
||||||
|
const { sha, data } = await githubHelper.downloadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
path: fileSyncData.id,
|
||||||
|
});
|
||||||
|
gitWorkspaceSvc.shaByPath[fileSyncData.id] = sha;
|
||||||
|
const content = Provider.parseContent(data, contentId);
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
contentSyncData: {
|
||||||
|
...contentSyncData,
|
||||||
|
hash: content.hash,
|
||||||
|
sha,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async downloadFile({ token, path }) {
|
||||||
|
const { sha, data } = await githubHelper.downloadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: data,
|
||||||
|
sha,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async downloadWorkspaceData({ token, syncData }) {
|
||||||
|
if (!syncData) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const path = `.stackedit-data/${syncData.id}.json`;
|
||||||
|
// const path = store.getters.gitPathsByItemId[syncData.id];
|
||||||
|
// const path = syncData.id;
|
||||||
|
const { sha, data } = await githubHelper.downloadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
if (!sha) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
gitWorkspaceSvc.shaByPath[path] = sha;
|
||||||
|
const item = JSON.parse(data);
|
||||||
|
return {
|
||||||
|
item,
|
||||||
|
syncData: {
|
||||||
|
...syncData,
|
||||||
|
hash: item.hash,
|
||||||
|
sha,
|
||||||
|
type: 'data',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async uploadWorkspaceContent({
|
||||||
|
token,
|
||||||
|
content,
|
||||||
|
file,
|
||||||
|
commitMessage,
|
||||||
|
}) {
|
||||||
|
const isImg = file.type === 'img';
|
||||||
|
const path = !isImg ? store.getters.gitPathsByItemId[file.id] : file.path;
|
||||||
|
const res = await githubHelper.uploadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
content: !isImg ? Provider.serializeContent(content) : file.content,
|
||||||
|
sha: gitWorkspaceSvc.shaByPath[!isImg ? path : file.path],
|
||||||
|
isImg,
|
||||||
|
commitMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isImg) {
|
||||||
|
return {
|
||||||
|
sha: res.content.sha,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Return new sync data
|
||||||
|
return {
|
||||||
|
contentSyncData: {
|
||||||
|
id: store.getters.gitPathsByItemId[content.id],
|
||||||
|
type: content.type,
|
||||||
|
hash: content.hash,
|
||||||
|
sha: res.content.sha,
|
||||||
|
},
|
||||||
|
fileSyncData: {
|
||||||
|
id: path,
|
||||||
|
type: 'file',
|
||||||
|
hash: file.hash,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async uploadWorkspaceData({
|
||||||
|
token,
|
||||||
|
item,
|
||||||
|
syncData,
|
||||||
|
}) {
|
||||||
|
const path = `.stackedit-data/${item.id}.json`;
|
||||||
|
// const path = store.getters.gitPathsByItemId[item.id];
|
||||||
|
// const path = syncData.id;
|
||||||
|
const res = await githubHelper.uploadFile({
|
||||||
|
token,
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
path,
|
||||||
|
content: JSON.stringify(item),
|
||||||
|
sha: gitWorkspaceSvc.shaByPath[path],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
syncData: {
|
||||||
|
...syncData,
|
||||||
|
type: item.type,
|
||||||
|
hash: item.hash,
|
||||||
|
data: item.data,
|
||||||
|
sha: res.content.sha,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async listFileRevisions({ token, fileSyncDataId }) {
|
||||||
|
const { owner, repo, branch } = {
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
};
|
||||||
|
const entries = await githubHelper.getCommits({
|
||||||
|
token,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
sha: branch,
|
||||||
|
path: fileSyncDataId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return entries.map(({
|
||||||
|
author,
|
||||||
|
committer,
|
||||||
|
commit,
|
||||||
|
sha,
|
||||||
|
}) => {
|
||||||
|
let user;
|
||||||
|
if (author && author.login) {
|
||||||
|
user = author;
|
||||||
|
} else if (committer && committer.login) {
|
||||||
|
user = committer;
|
||||||
|
}
|
||||||
|
const sub = `${githubHelper.subPrefix}:${user.login}`;
|
||||||
|
if (user.avatar_url && user.avatar_url.endsWith('.png') && !user.avatar_url.endsWith('no_portrait.png')) {
|
||||||
|
user.avatar_url = `${user.avatar_url}!avatar60`;
|
||||||
|
}
|
||||||
|
userSvc.addUserInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
||||||
|
const date = (commit.author && commit.author.date)
|
||||||
|
|| (commit.committer && commit.committer.date)
|
||||||
|
|| 1;
|
||||||
|
return {
|
||||||
|
id: sha,
|
||||||
|
sub,
|
||||||
|
message: commit.message,
|
||||||
|
created: new Date(date).getTime(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async loadFileRevision() {
|
||||||
|
// Revisions are already loaded
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
async getFileRevisionContent({
|
||||||
|
token,
|
||||||
|
contentId,
|
||||||
|
fileSyncDataId,
|
||||||
|
revisionId,
|
||||||
|
}) {
|
||||||
|
const { data } = await githubHelper.downloadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: revisionId,
|
||||||
|
token,
|
||||||
|
path: fileSyncDataId,
|
||||||
|
});
|
||||||
|
return Provider.parseContent(data, contentId);
|
||||||
|
},
|
||||||
|
getFilePathUrl(path) {
|
||||||
|
const token = this.getToken();
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return `https://github.com/${token.name}/${appDataRepo}/blob/${appDataBranch}${path}`;
|
||||||
|
},
|
||||||
|
});
|
@ -147,6 +147,7 @@ export default {
|
|||||||
sub: `${user.login}`,
|
sub: `${user.login}`,
|
||||||
};
|
};
|
||||||
if (isMain) {
|
if (isMain) {
|
||||||
|
token.providerId = 'giteeAppData';
|
||||||
// 检查 stackedit-app-data 仓库是否已经存在 如果不存在则创建该仓库
|
// 检查 stackedit-app-data 仓库是否已经存在 如果不存在则创建该仓库
|
||||||
await this.checkAndCreateRepo(token);
|
await this.checkAndCreateRepo(token);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import badgeSvc from '../../badgeSvc';
|
|||||||
|
|
||||||
const getScopes = token => [token.repoFullAccess ? 'repo' : 'public_repo', 'gist'];
|
const getScopes = token => [token.repoFullAccess ? 'repo' : 'public_repo', 'gist'];
|
||||||
|
|
||||||
|
const appDataRepo = 'stackedit-app-data';
|
||||||
|
|
||||||
const request = (token, options) => networkSvc.request({
|
const request = (token, options) => networkSvc.request({
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
@ -62,7 +64,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* https://developer.github.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/
|
* https://developer.github.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/
|
||||||
*/
|
*/
|
||||||
async startOauth2(scopes, sub = null, silent = false) {
|
async startOauth2(scopes, sub = null, silent = false, isMain) {
|
||||||
await networkSvc.getServerConf();
|
await networkSvc.getServerConf();
|
||||||
const clientId = store.getters['data/serverConf'].githubClientId;
|
const clientId = store.getters['data/serverConf'].githubClientId;
|
||||||
|
|
||||||
@ -110,16 +112,26 @@ export default {
|
|||||||
const token = {
|
const token = {
|
||||||
scopes,
|
scopes,
|
||||||
accessToken,
|
accessToken,
|
||||||
|
// 主文档空间的登录 标识登录
|
||||||
|
isLogin: !!isMain || (oldToken && !!oldToken.isLogin),
|
||||||
name: user.login,
|
name: user.login,
|
||||||
sub: `${user.id}`,
|
sub: `${user.id}`,
|
||||||
imgStorages: oldToken && oldToken.imgStorages,
|
imgStorages: oldToken && oldToken.imgStorages,
|
||||||
repoFullAccess: scopes.includes('repo'),
|
repoFullAccess: scopes.includes('repo'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isMain) {
|
||||||
|
token.providerId = 'githubAppData';
|
||||||
|
// check stackedit-app-data repo exist?
|
||||||
|
await this.checkAndCreateRepo(token);
|
||||||
|
}
|
||||||
// Add token to github tokens
|
// Add token to github tokens
|
||||||
store.dispatch('data/addGithubToken', token);
|
store.dispatch('data/addGithubToken', token);
|
||||||
return token;
|
return token;
|
||||||
},
|
},
|
||||||
|
signin() {
|
||||||
|
return this.startOauth2(['repo', 'gist'], null, false, true);
|
||||||
|
},
|
||||||
async addAccount(repoFullAccess = false) {
|
async addAccount(repoFullAccess = false) {
|
||||||
const token = await this.startOauth2(getScopes({ repoFullAccess }));
|
const token = await this.startOauth2(getScopes({ repoFullAccess }));
|
||||||
badgeSvc.addBadge('addGitHubAccount');
|
badgeSvc.addBadge('addGitHubAccount');
|
||||||
@ -148,6 +160,30 @@ export default {
|
|||||||
return tree;
|
return tree;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async checkAndCreateRepo(token) {
|
||||||
|
const url = `https://api.github.com/repos/${encodeURIComponent(token.name)}/${encodeURIComponent(appDataRepo)}`;
|
||||||
|
try {
|
||||||
|
await request(token, { url });
|
||||||
|
} catch (err) {
|
||||||
|
// create
|
||||||
|
if (err.status === 404) {
|
||||||
|
await request(token, {
|
||||||
|
method: 'POST',
|
||||||
|
url: 'https://api.github.com/repos/mafgwo/stackeditplus-appdata-template/generate',
|
||||||
|
body: {
|
||||||
|
owner: token.name,
|
||||||
|
name: appDataRepo,
|
||||||
|
description: 'StackEdit中文版默认空间.',
|
||||||
|
include_all_branches: false,
|
||||||
|
private: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
|
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
|
||||||
*/
|
*/
|
||||||
|
@ -6,6 +6,7 @@ import diffUtils from './diffUtils';
|
|||||||
import networkSvc from './networkSvc';
|
import networkSvc from './networkSvc';
|
||||||
import providerRegistry from './providers/common/providerRegistry';
|
import providerRegistry from './providers/common/providerRegistry';
|
||||||
import giteeAppDataProvider from './providers/giteeAppDataProvider';
|
import giteeAppDataProvider from './providers/giteeAppDataProvider';
|
||||||
|
import githubAppDataProvider from './providers/githubAppDataProvider';
|
||||||
import './providers/couchdbWorkspaceProvider';
|
import './providers/couchdbWorkspaceProvider';
|
||||||
import './providers/githubWorkspaceProvider';
|
import './providers/githubWorkspaceProvider';
|
||||||
import './providers/giteeWorkspaceProvider';
|
import './providers/giteeWorkspaceProvider';
|
||||||
@ -830,7 +831,7 @@ const syncWorkspace = async (skipContents = false) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (workspace.id === 'main') {
|
if (workspace.id === 'main') {
|
||||||
badgeSvc.addBadge('syncMainWorkspace');
|
badgeSvc.addBadge(workspace.providerId === 'giteeAppData' ? 'syncMainWorkspace' : 'githubSyncMainWorkspace');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err && err.message === 'TOO_LATE') {
|
if (err && err.message === 'TOO_LATE') {
|
||||||
@ -969,6 +970,15 @@ const requestSync = (addTriggerSyncBadge = false) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const afterSignIn = async () => {
|
||||||
|
if (store.getters['workspace/currentWorkspace'].id === 'main' && workspaceProvider) {
|
||||||
|
const mainToken = store.getters['workspace/mainWorkspaceToken'];
|
||||||
|
// Try to find a suitable workspace sync provider
|
||||||
|
workspaceProvider = mainToken.providerId === 'githubAppData' ? githubAppDataProvider : giteeAppDataProvider;
|
||||||
|
await workspaceProvider.initWorkspace();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async init() {
|
async init() {
|
||||||
// Load workspaces and tokens from localStorage
|
// Load workspaces and tokens from localStorage
|
||||||
@ -980,10 +990,11 @@ export default {
|
|||||||
await actionProvider.initAction();
|
await actionProvider.initAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mainToken = store.getters['workspace/mainWorkspaceToken'];
|
||||||
// Try to find a suitable workspace sync provider
|
// Try to find a suitable workspace sync provider
|
||||||
workspaceProvider = providerRegistry.providersById[utils.queryParams.providerId];
|
workspaceProvider = providerRegistry.providersById[utils.queryParams.providerId];
|
||||||
if (!workspaceProvider || !workspaceProvider.initWorkspace) {
|
if (!workspaceProvider || !workspaceProvider.initWorkspace) {
|
||||||
workspaceProvider = giteeAppDataProvider;
|
workspaceProvider = mainToken && mainToken.providerId === 'githubAppData' ? githubAppDataProvider : giteeAppDataProvider;
|
||||||
}
|
}
|
||||||
const workspace = await workspaceProvider.initWorkspace();
|
const workspace = await workspaceProvider.initWorkspace();
|
||||||
// Fix the URL hash
|
// Fix the URL hash
|
||||||
@ -1041,6 +1052,7 @@ export default {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
afterSignIn,
|
||||||
syncImg,
|
syncImg,
|
||||||
isSyncPossible,
|
isSyncPossible,
|
||||||
requestSync,
|
requestSync,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import utils from '../services/utils';
|
import utils from '../services/utils';
|
||||||
import giteeHelper from '../services/providers/helpers/giteeHelper';
|
import giteeHelper from '../services/providers/helpers/giteeHelper';
|
||||||
|
import githubHelper from '../services/providers/helpers/githubHelper';
|
||||||
import syncSvc from '../services/syncSvc';
|
import syncSvc from '../services/syncSvc';
|
||||||
|
|
||||||
const idShifter = offset => (state, getters) => {
|
const idShifter = offset => (state, getters) => {
|
||||||
@ -136,8 +137,13 @@ export default {
|
|||||||
const loginToken = rootGetters['workspace/loginToken'];
|
const loginToken = rootGetters['workspace/loginToken'];
|
||||||
if (!loginToken) {
|
if (!loginToken) {
|
||||||
try {
|
try {
|
||||||
await dispatch('modal/open', 'signInForComment', { root: true });
|
const signInWhere = await dispatch('modal/open', 'signInForComment', { root: true });
|
||||||
await giteeHelper.signin();
|
if (signInWhere === 'github') {
|
||||||
|
await githubHelper.signin();
|
||||||
|
} else {
|
||||||
|
await giteeHelper.signin();
|
||||||
|
}
|
||||||
|
await syncSvc.afterSignIn();
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
await dispatch('createNewDiscussion', selection);
|
await dispatch('createNewDiscussion', selection);
|
||||||
} catch (e) { /* cancel */ }
|
} catch (e) { /* cancel */ }
|
||||||
|
@ -22,7 +22,7 @@ export default {
|
|||||||
Object.entries(rootGetters['data/workspaces']).forEach(([id, workspace]) => {
|
Object.entries(rootGetters['data/workspaces']).forEach(([id, workspace]) => {
|
||||||
const sanitizedWorkspace = {
|
const sanitizedWorkspace = {
|
||||||
id,
|
id,
|
||||||
providerId: 'giteeAppData',
|
providerId: (mainWorkspaceToken && mainWorkspaceToken.providerId) || 'giteeAppData',
|
||||||
sub: mainWorkspaceToken && mainWorkspaceToken.sub,
|
sub: mainWorkspaceToken && mainWorkspaceToken.sub,
|
||||||
...workspace,
|
...workspace,
|
||||||
};
|
};
|
||||||
@ -47,17 +47,19 @@ export default {
|
|||||||
|| currentWorkspace.providerId === 'giteeWorkspace'
|
|| currentWorkspace.providerId === 'giteeWorkspace'
|
||||||
|| currentWorkspace.providerId === 'gitlabWorkspace'
|
|| currentWorkspace.providerId === 'gitlabWorkspace'
|
||||||
|| currentWorkspace.providerId === 'giteaWorkspace'
|
|| currentWorkspace.providerId === 'giteaWorkspace'
|
||||||
|| currentWorkspace.providerId === 'giteeAppData',
|
|| currentWorkspace.providerId === 'giteeAppData'
|
||||||
|
|| currentWorkspace.providerId === 'githubAppData',
|
||||||
currentWorkspaceHasUniquePaths: (state, { currentWorkspace }) =>
|
currentWorkspaceHasUniquePaths: (state, { currentWorkspace }) =>
|
||||||
currentWorkspace.providerId === 'githubWorkspace'
|
currentWorkspace.providerId === 'githubWorkspace'
|
||||||
|| currentWorkspace.providerId === 'giteeWorkspace'
|
|| currentWorkspace.providerId === 'giteeWorkspace'
|
||||||
|| currentWorkspace.providerId === 'gitlabWorkspace'
|
|| currentWorkspace.providerId === 'gitlabWorkspace'
|
||||||
|| currentWorkspace.providerId === 'giteaWorkspace'
|
|| currentWorkspace.providerId === 'giteaWorkspace'
|
||||||
|| currentWorkspace.providerId === 'giteeAppData',
|
|| currentWorkspace.providerId === 'giteeAppData'
|
||||||
|
|| currentWorkspace.providerId === 'githubAppData',
|
||||||
lastSyncActivityKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastSyncActivity`,
|
lastSyncActivityKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastSyncActivity`,
|
||||||
lastFocusKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastWindowFocus`,
|
lastFocusKey: (state, { currentWorkspace }) => `${currentWorkspace.id}/lastWindowFocus`,
|
||||||
mainWorkspaceToken: (state, getters, rootState, rootGetters) =>
|
mainWorkspaceToken: (state, getters, rootState, rootGetters) =>
|
||||||
utils.someResult(Object.values(rootGetters['data/giteeTokensBySub']), (token) => {
|
utils.someResult([...Object.values(rootGetters['data/giteeTokensBySub']), ...Object.values(rootGetters['data/githubTokensBySub'])], (token) => {
|
||||||
if (token.isLogin) {
|
if (token.isLogin) {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@ -85,8 +87,10 @@ export default {
|
|||||||
switch (currentWorkspace.providerId) {
|
switch (currentWorkspace.providerId) {
|
||||||
case 'googleDriveWorkspace':
|
case 'googleDriveWorkspace':
|
||||||
return 'google';
|
return 'google';
|
||||||
|
case 'githubAppData':
|
||||||
case 'githubWorkspace':
|
case 'githubWorkspace':
|
||||||
return 'github';
|
return 'github';
|
||||||
|
case 'giteeAppData':
|
||||||
case 'giteeWorkspace':
|
case 'giteeWorkspace':
|
||||||
default:
|
default:
|
||||||
return 'gitee';
|
return 'gitee';
|
||||||
|
165
static/landing/gistshare.html
Normal file
165
static/landing/gistshare.html
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>文章分享 - StackEdit中文版</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="canonical" href="https://stackedit.cn/">
|
||||||
|
<link rel="icon" href="static/landing/favicon.ico" type="image/x-icon">
|
||||||
|
<link rel="shortcut icon" href="static/landing/favicon.ico" type="image/x-icon">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="keywords" content="Markdown编辑器,StackEdit中文版,StackEdit汉化版,StackEdit,在线Markdown,笔记利器,Markdown笔记">
|
||||||
|
<meta name="description"
|
||||||
|
content="支持直接将码云(Gitee)、GitHub、Gitea等仓库作为笔记存储仓库且支持拖拽/粘贴上传图片,并且可以直接在页面编辑同步和管理的Markdown编辑器。">
|
||||||
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
<meta name="baidu-site-verification" content="code-tGpn2BT069" />
|
||||||
|
<meta name="msvalidate.01" content="90A9558158543277BD284CFA054E7F5B" />
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<style>
|
||||||
|
.share-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #383c4a;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header .logo {
|
||||||
|
margin: 0 0 -8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header nav li {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header nav a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-header nav a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-content {
|
||||||
|
transform: translateY(50px);
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function getQueryString(name) {
|
||||||
|
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
|
||||||
|
var r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) {
|
||||||
|
return unescape(r[2]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendTagHtml(newdoc, tagName, targetParentEle) {
|
||||||
|
const tags = newdoc.getElementsByTagName(tagName);
|
||||||
|
if (!tags) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < tags.length; i++) {
|
||||||
|
targetParentEle.append(tags[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
const gistId = getQueryString('id');
|
||||||
|
let accessToken = null;
|
||||||
|
const tokens = window.localStorage.getItem('data/tokens');
|
||||||
|
if (tokens) {
|
||||||
|
const tokensObj = JSON.parse(tokens);
|
||||||
|
if (tokensObj.data && tokensObj.data.github) {
|
||||||
|
const tokenArr = Object.keys(tokensObj.data.github).map(it => tokensObj.data.github[it]).filter(it => it && it.isLogin);
|
||||||
|
if (tokenArr.length > 0) {
|
||||||
|
accessToken = tokenArr[0].accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const url = `https://api.github.com/gists/${gistId}`;
|
||||||
|
xhr.open('GET', url);
|
||||||
|
if (accessToken) {
|
||||||
|
xhr.setRequestHeader('Authorization', `Bearer ${accessToken}`);
|
||||||
|
}
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const newdoc = document.implementation.createHTMLDocument("");
|
||||||
|
const body = JSON.parse(xhr.responseText);
|
||||||
|
for (let key in body.files) {
|
||||||
|
newdoc.documentElement.innerHTML = body.files[key].content;
|
||||||
|
}
|
||||||
|
const currHead = document.head;
|
||||||
|
// head
|
||||||
|
appendTagHtml(newdoc, 'style', currHead);
|
||||||
|
// title
|
||||||
|
document.title = newdoc.title + ' - StackEdit中文版';
|
||||||
|
// 内容
|
||||||
|
const shareContent = document.getElementsByClassName('share-content')[0];
|
||||||
|
shareContent.innerHTML = newdoc.body.innerHTML;
|
||||||
|
document.body.className = newdoc.body.className;
|
||||||
|
} else if (xhr.status === 403) {
|
||||||
|
const rateLimit = xhr.responseText && xhr.responseText.indexOf('Rate Limit') >= 0;
|
||||||
|
const appUri = `${window.location.protocol}//${window.location.host}/app`;
|
||||||
|
document.getElementById('div_info').innerHTML = `${rateLimit ? "请求太过频繁" : "无权限访问"},请使用GitHub登录 <a href="${appUri}" target="_brank">主文档空间</a> 后再刷新此页面!`;
|
||||||
|
} else {
|
||||||
|
console.error('An error occurred: ' + xhr.status);
|
||||||
|
document.getElementById('div_info').innerHTML = `分享内容获取失败或已失效!请使用GitHub登录 <a href="${appUri}" target="_brank">主文档空间</a> 后再刷新此页面!`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="share-header">
|
||||||
|
<nav>
|
||||||
|
<a class="logo" href="https://stackedit.cn" target="_blank">
|
||||||
|
<img src="static/landing/logo.svg" height="30px"/>
|
||||||
|
</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://stackedit.cn" target="_blank">首页</a></li>
|
||||||
|
<li><a href="https://stackedit.cn/app" target="_blank">写笔记</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="share-content stackedit">
|
||||||
|
<div id="div_info" style="text-align: center; height: 600px;">文章加载中......</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
<script>
|
||||||
|
var _hmt = _hmt || [];
|
||||||
|
(function() {
|
||||||
|
var hm = document.createElement("script");
|
||||||
|
hm.src = "https://hm.baidu.com/hm.js?20a1e7a201b42702c49074c87a1f1035";
|
||||||
|
var s = document.getElementsByTagName("script")[0];
|
||||||
|
s.parentNode.insertBefore(hm, s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -36,7 +36,7 @@ style.innerHTML = "/** activeblue 灵动蓝\n \
|
|||||||
top: 0;\n \
|
top: 0;\n \
|
||||||
width: 60px;\n \
|
width: 60px;\n \
|
||||||
height: 60px;\n \
|
height: 60px;\n \
|
||||||
background: url(https://my-wechat.mdnice.com/ape_blue.svg);\n \
|
background: url(https://imgs.qicoder.com/stackedit/ape_blue.svg);\n \
|
||||||
background-size: 100% 100%;\n \
|
background-size: 100% 100%;\n \
|
||||||
opacity: .12;\n \
|
opacity: .12;\n \
|
||||||
}\n \
|
}\n \
|
||||||
|
@ -100,7 +100,7 @@ style.innerHTML = "/* 草原绿 caoyuangreen\n \
|
|||||||
width:30px;\n \
|
width:30px;\n \
|
||||||
height:30px;\n \
|
height:30px;\n \
|
||||||
display:block;\n \
|
display:block;\n \
|
||||||
background-image:url(https://files.mdnice.com/grass-green.png);\n \
|
background-image:url(https://imgs.qicoder.com/stackedit/grass-green.png);\n \
|
||||||
background-position:center;\n \
|
background-position:center;\n \
|
||||||
background-size:30px;\n \
|
background-size:30px;\n \
|
||||||
margin:auto;\n \
|
margin:auto;\n \
|
||||||
|
Loading…
Reference in New Issue
Block a user