支持分享功能

This commit is contained in:
xiaoqi.cxq 2023-03-30 15:56:24 +08:00
parent 58c9144612
commit ae828cfb56
22 changed files with 562 additions and 57 deletions

View File

@ -71,6 +71,7 @@ StackEdit中文版
- 支持预览区域选择主题样式2022-12-04
- Gitlab的支持优化2023-02-23
- 导出HTML、PDF支持带预览主题导出2023-02-26
- 支持分享文档2023-03-30
## 国外开源版本弊端:
- 作者已经不维护了

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "stackedit",
"version": "5.15.17",
"version": "5.15.19",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "stackedit",
"version": "5.15.18",
"version": "5.15.19",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0",

View File

@ -61,13 +61,15 @@ module.exports = (app) => {
res.redirect(`./app#providerId=googleDrive&state=${encodeURIComponent(req.query.state)}`));
// Serve the static folder with 30 day max-age
app.use('/themes', serveStatic(resolvePath('static/themes'), {
maxAge: '1d',
maxAge: '5d',
}));
// Serve style.css with 1 day max-age
app.get('/style.css', (req, res) => res.sendFile(resolvePath('dist/style.css'), {
maxAge: '1d',
}));
// Serve share.html
app.get('/share.html', (req, res) => res.sendFile(resolvePath('static/landing/share.html')));
// Serve static resources
if (process.env.NODE_ENV === 'production') {

View File

@ -64,6 +64,8 @@ import GiteeOpenModal from './modals/providers/GiteeOpenModal';
import GiteeSaveModal from './modals/providers/GiteeSaveModal';
import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal';
import GiteePublishModal from './modals/providers/GiteePublishModal';
import GiteeGistSyncModal from './modals/providers/GiteeGistSyncModal';
import GiteeGistPublishModal from './modals/providers/GiteeGistPublishModal';
import GitlabAccountModal from './modals/providers/GitlabAccountModal';
import GitlabOpenModal from './modals/providers/GitlabOpenModal';
import GitlabPublishModal from './modals/providers/GitlabPublishModal';
@ -131,6 +133,8 @@ export default {
GiteeSaveModal,
GiteeWorkspaceModal,
GiteePublishModal,
GiteeGistSyncModal,
GiteeGistPublishModal,
GitlabAccountModal,
GitlabOpenModal,
GitlabPublishModal,

View File

@ -4,6 +4,9 @@
<li class="before">
<icon-ellipsis></icon-ellipsis>
</li>
<li title="分享">
<a href="javascript:void(0)" @click="share"><icon-share></icon-share></a>
</li>
<li title="切换预览主题">
<dropdown-menu :selected="selectedTheme" :options="allThemes" :closeOnItemClick="false" @change="changeTheme">
<icon-select-theme></icon-select-theme>
@ -21,6 +24,8 @@ import { mapGetters, mapActions } from 'vuex';
// import juice from 'juice';
import store from '../store';
import DropdownMenu from './common/DropdownMenu';
import publishSvc from '../services/publishSvc';
import giteeGistProvider from '../services/providers/giteeGistProvider';
export default {
components: {
@ -56,12 +61,16 @@ export default {
value: 'custom',
}],
baseCss: '',
sharing: false,
}),
computed: {
...mapGetters('theme', [
'currPreviewTheme',
'customPreviewThemeStyle',
]),
...mapGetters('publishLocation', {
publishLocations: 'current',
}),
selectedTheme() {
return {
value: this.currPreviewTheme || 'default',
@ -84,6 +93,43 @@ export default {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'help');
},
async share() {
if (this.sharing) {
store.dispatch('notification/info', '分享链接创建中...请稍后再试');
return;
}
try {
const currentFile = store.getters['file/current'];
await store.dispatch('modal/open', { type: 'shareHtmlPre', name: currentFile.name });
this.sharing = true;
const mainToken = store.getters['workspace/mainWorkspaceToken'];
if (!mainToken) {
store.dispatch('notification/info', '登录主文档空间之后才可使用分享功能!');
return;
}
let giteeGistId = null;
const filterLocations = this.publishLocations.filter(it => it.providerId === 'giteegist' && it.url && it.gistId);
if (filterLocations.length > 0) {
giteeGistId = filterLocations[0].gistId;
}
const location = giteeGistProvider.makeLocation(
mainToken,
`分享-${currentFile.name}`,
true,
null,
);
location.templateId = 'styledHtmlWithTheme';
location.fileId = currentFile.id;
location.gistId = giteeGistId;
const { gistId } = await publishSvc.publishLocationAndStore(location);
const url = `${window.location.protocol}//${window.location.host}/share.html?id=${gistId}`;
await store.dispatch('modal/open', { type: 'shareHtml', name: currentFile.name, url });
} catch (err) {
/* cancel */
} finally {
this.sharing = false;
}
},
},
};
</script>
@ -94,7 +140,7 @@ export default {
.preview-in-page-buttons {
position: absolute;
bottom: 10px;
right: -68px;
right: -98px;
height: 34px;
padding: 5px;
background-color: rgba(84, 96, 114, 0.4);

View File

@ -43,7 +43,7 @@
<div v-for="token in githubTokens" :key="token.sub">
<menu-entry @click.native="publishGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>发布到 Gist</div>
<div>发布到 GitHubGist</div>
<span>{{token.name}}</span>
</menu-entry>
<menu-entry @click.native="publishGithub(token)">
@ -53,6 +53,11 @@
</menu-entry>
</div>
<div v-for="token in giteeTokens" :key="token.sub">
<menu-entry @click.native="publishGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>发布到 GiteeGist</div>
<span>{{token.name}}</span>
</menu-entry>
<menu-entry @click.native="publishGitee(token)">
<icon-provider slot="icon" provider-id="gitee"></icon-provider>
<div>发布到 Gitee</div>
@ -289,8 +294,9 @@ export default {
publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'),
publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'),
publishGithub: publishModalOpener('githubPublish', 'publishToGithub'),
publishGitee: publishModalOpener('giteePublish', 'publishToGitee'),
publishGist: publishModalOpener('gistPublish', 'publishToGist'),
publishGitee: publishModalOpener('giteePublish', 'publishToGitee'),
publishGiteeGist: publishModalOpener('giteeGistPublish', 'publishGiteeGist'),
publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'),
publishGitea: publishModalOpener('giteaPublish', 'publishToGitea'),
publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'),

View File

@ -46,7 +46,7 @@
</menu-entry>
<menu-entry @click.native="saveGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>在Gist上保存</div>
<div>在GitHubGist上保存</div>
<span>{{token.name}}</span>
</menu-entry>
</div>
@ -61,6 +61,11 @@
<div>在Gitee上保存</div>
<span>{{token.name}}</span>
</menu-entry>
<menu-entry @click.native="saveGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>在GiteeGist上保存</div>
<span>{{token.name}}</span>
</menu-entry>
</div>
<div v-for="token in gitlabTokens" :key="token.sub">
<menu-entry @click.native="openGitlab(token)">
@ -330,6 +335,12 @@ export default {
badgeSvc.addBadge('saveOnGist');
} catch (e) { /* cancel */ }
},
async saveGiteeGist(token) {
try {
await openSyncModal(token, 'giteeGistSync');
badgeSvc.addBadge('saveOnGiteeGist');
} catch (e) { /* cancel */ }
},
async openGitlab(token) {
try {
const syncLocation = await store.dispatch('modal/open', {

View File

@ -4,8 +4,8 @@
<div class="modal__image">
<icon-upload></icon-upload>
</div>
<p v-if="publishLocations.length"><b>{{currentFileName}}</b> is published to the following location(s):</p>
<p v-else><b>{{currentFileName}}</b> is not published yet.</p>
<p v-if="publishLocations.length"><b>{{currentFileName}}</b> 被发布到了以下位置:</p>
<p v-else><b>{{currentFileName}}</b> 还没有被发布.</p>
<div>
<div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id">
<div class="publish-entry__header flex flex--row flex--align-center">
@ -26,7 +26,7 @@
{{location.url}}
</div>
<div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url">
<button class="publish-entry__button button" v-clipboard="location.url" @click="info('Location URL copied to clipboard!')" v-title="'复制URL'">
<button class="publish-entry__button button" v-clipboard="location.url" @click="info('位置URL已复制到剪贴板!')" v-title="'复制URL'">
<icon-content-copy></icon-content-copy>
</button>
<a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'打开位置'">
@ -34,6 +34,19 @@
</a>
</div>
</div>
<div class="publish-entry__row flex flex--row flex--align-center" v-if="shareUrl(location)">
<div class="publish-entry__url">
分享链接: {{shareUrl(location)}}
</div>
<div class="publish-entry__buttons flex flex--row flex--center">
<button class="publish-entry__button button" v-clipboard="shareUrl(location)" @click="info('分享URL已复制到剪贴板!')" v-title="'复制分享URL'">
<icon-content-copy></icon-content-copy>
</button>
<a class="publish-entry__button button" :href="shareUrl(location)" target="_blank" v-title="'打开分享'">
<icon-open-in-new></icon-open-in-new>
</a>
</div>
</div>
</div>
</div>
<div class="modal__info" v-if="publishLocations.length">
@ -75,6 +88,16 @@ export default {
store.commit('publishLocation/deleteItem', location.id);
badgeSvc.addBadge('removePublishLocation');
},
shareUrl(location) {
if (location.providerId !== 'giteegist') {
return null;
}
if (!location.url) {
return null;
}
const splitIndex = location.url.lastIndexOf('/');
return `${window.location.protocol}//${window.location.host}/share.html?id=${location.url.substr(splitIndex + 1)}`;
},
},
};
</script>

View File

@ -1,11 +1,11 @@
<template>
<modal-inner aria-label="发布到Gist">
<modal-inner aria-label="发布到GitHubGist">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="gist"></icon-provider>
</div>
<p>发布<b> {{CurrentFileName}} </b><b>Gist</b></p>
<form-entry label="Filename" error="filename">
<p>发布<b> {{CurrentFileName}} </b><b>GitHubGist</b></p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
@ -15,10 +15,10 @@
</label>
</div>
</div>
<form-entry label="Existing Gist ID" info="可选的">
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于Gist中则将被覆盖
如果文件存在于GitHubGist中则将被覆盖
</div>
</form-entry>
<form-entry label="Template">

View File

@ -1,11 +1,11 @@
<template>
<modal-inner aria-label=" Gist 同步">
<modal-inner aria-label=" GitHubGist 同步">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="gist"></icon-provider>
</div>
<p><b> {{currentFileName}} </b>保存到<b>Gist</b>并保持同步</p>
<form-entry label="Filename" error="filename">
<p><b> {{currentFileName}} </b>保存到<b>GitHubGist</b>并保持同步</p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
@ -15,10 +15,10 @@
</label>
</div>
</div>
<form-entry label="Existing Gist ID" info="可选的">
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于Gist中则将被覆盖
如果文件存在于GitHubGist中则将被覆盖
</div>
</form-entry>
</div>

View File

@ -0,0 +1,79 @@
<template>
<modal-inner aria-label="发布到GiteeGist">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p>发布<b> {{CurrentFileName}} </b><b>GiteeGist</b></p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
<form-entry label="Template">
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
{{ template.name }}
</option>
</select>
<div class="form-entry__actions">
<a href="javascript:void(0)" @click="configureTemplates">配置模板</a>
</div>
</form-entry>
<div class="modal__info">
<b>ProTip:</b> You can provide a value for <code>title</code> in the <a href="javascript:void(0)" @click="openFileProperties">file properties</a>.
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
selectedTemplate: 'gistPublishTemplate',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
location.templateId = this.selectedTemplate;
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -0,0 +1,64 @@
<template>
<modal-inner aria-label=" GiteeGist 同步">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p><b> {{currentFileName}} </b>保存到<b>GiteeGist</b>并保持同步</p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -316,6 +316,11 @@ export default [
'GitHub保存',
'使用“同步”菜单将文件保存在GitHub仓库中。',
),
new Feature(
'saveOnGist',
'GitHubGist保存',
'使用“同步”菜单将文件保存在GitHubGist中。',
),
new Feature(
'openFromGitee',
'Gitee阅读器',
@ -327,9 +332,9 @@ export default [
'使用“同步”菜单将文件保存在Gitee仓库中。',
),
new Feature(
'saveOnGist',
'Gist保存',
'使用“同步”菜单将文件保存在GIST中。',
'saveOnGiteeGist',
'GiteeGist保存',
'使用“同步”菜单将文件保存在GiteeGist中。',
),
new Feature(
'openFromGitlab',
@ -405,14 +410,19 @@ export default [
),
new Feature(
'publishToGist',
'Gist发布',
'使用“发布”菜单将文件发布到GIST。',
'GitHubGist发布',
'使用“发布”菜单将文件发布到GitHubGist。',
),
new Feature(
'publishToGitee',
'Gitee发布',
'使用“发布”菜单将文件发布到Gitee仓库。',
),
new Feature(
'publishToGiteeGist',
'GiteeGist发布',
'使用“发布”菜单将文件发布到GiteeGist。',
),
new Feature(
'publishToGitlab',
'GitLab发布',

View File

@ -60,6 +60,15 @@ export default {
'取消',
'确认清理',
),
shareHtml: simpleModal(
config => `<p>给文档 "${config.name}" 创建了分享链接如下:<br/><a href="${config.url}" target="_blank">${config.url}</a><br/>关闭该窗口后可以到发布中查看分享链接。</p>`,
'关闭窗口',
),
shareHtmlPre: simpleModal(
config => `<p>将给文档 "${config.name}" 创建分享链接创建后将会把文档公开发布到GiteeGist中。您确定吗</p>`,
'取消',
'确认分享',
),
signInForComment: simpleModal(
`<p>您必须使用 Google 登录才能开始评论。</p>
<div class="modal__info"><b>注意:</b> </div>`,

View File

@ -29,6 +29,7 @@ export default {
return 'couchdb';
case 'giteeAppData':
case 'giteeWorkspace':
case 'giteegist':
return 'gitee';
default:
return this.providerId;

3
src/icons/Share.vue Normal file
View File

@ -0,0 +1,3 @@
<template>
<svg t="1680140298859" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2766" width="32" height="32"><path d="M769.14815 670.390403c-44.430932 0-84.182284 19.999496-110.768803 51.471278L389.219117 565.736878c6.597255-16.571421 10.228969-34.653241 10.228969-53.594639 0-17.006326-2.940982-33.332153-8.320503-48.497551l270.88143-157.119457c26.511817 29.069059 64.702628 47.312562 107.138112 47.312562 80.055291 0 144.95337-64.899102 144.95337-144.95337 0-80.055291-64.898079-144.954393-144.95337-144.954393s-144.95337 64.899102-144.95337 144.954393c0 15.991206 2.600221 31.386848 7.382131 45.776579L359.655801 412.377048c-26.417673-27.833929-63.756069-45.181015-105.161085-45.181015-80.055291 0-144.954393 64.890916-144.954393 144.946206 0 80.055291 64.898079 144.967696 144.954393 144.967696 39.409568 0 75.128071-15.741519 101.256148-41.24845l274.172383 159.024853c-3.725858 12.8384-5.729491 26.409486-5.729491 40.457434 0 80.0645 64.898079 144.954393 144.95337 144.954393s144.95337-64.889893 144.95337-144.954393C914.101519 735.297692 849.20344 670.390403 769.14815 670.390403z" p-id="2767"></path></svg>
</template>

View File

@ -64,6 +64,7 @@ import FindReplace from './FindReplace';
import SelectTheme from './SelectTheme';
import Copy from './Copy';
import Ellipsis from './Ellipsis';
import Share from './Share';
Vue.component('iconProvider', Provider);
Vue.component('iconFormatBold', FormatBold);
@ -130,3 +131,4 @@ Vue.component('iconFindReplace', FindReplace);
Vue.component('iconSelectTheme', SelectTheme);
Vue.component('iconCopy', Copy);
Vue.component('iconEllipsis', Ellipsis);
Vue.component('iconShare', Share);

View File

@ -0,0 +1,95 @@
import store from '../../store';
import giteeHelper from './helpers/giteeHelper';
import Provider from './common/Provider';
import utils from '../utils';
import userSvc from '../userSvc';
export default new Provider({
id: 'giteegist',
name: 'GiteeGist',
getToken({ sub }) {
return store.getters['data/giteeTokensBySub'][sub];
},
getLocationUrl({ gistId }) {
return `https://gitee.com/mafgwo/codes/${gistId}`;
},
getLocationDescription({ filename }) {
return filename;
},
async downloadContent(token, syncLocation) {
const content = await giteeHelper.downloadGist({
...syncLocation,
token,
});
return Provider.parseContent(content, `${syncLocation.fileId}/content`);
},
async uploadContent(token, content, syncLocation) {
const file = store.state.file.itemsById[syncLocation.fileId];
const description = utils.sanitizeName(file && file.name);
const gist = await giteeHelper.uploadGist({
...syncLocation,
token,
description,
content: Provider.serializeContent(content),
});
return {
...syncLocation,
gistId: gist.id,
};
},
async publish(token, html, metadata, publishLocation) {
const gist = await giteeHelper.uploadGist({
...publishLocation,
token,
description: metadata.title,
content: html,
});
return {
...publishLocation,
gistId: gist.id,
};
},
makeLocation(token, filename, isPublic, gistId) {
return {
providerId: this.id,
sub: token.sub,
filename,
isPublic,
gistId,
};
},
async listFileRevisions({ token, syncLocation }) {
const entries = await giteeHelper.getGistCommits({
...syncLocation,
token,
});
return entries.map((entry) => {
const sub = `${giteeHelper.subPrefix}:${entry.user.id}`;
userSvc.addUserInfo({ id: sub, name: entry.user.login, imageUrl: entry.user.avatar_url });
return {
sub,
id: entry.version,
message: entry.commit && entry.commit.message,
created: new Date(entry.committed_at).getTime(),
};
});
},
async loadFileRevision() {
// Revision are already loaded
return false;
},
// async getFileRevisionContent({
// token,
// contentId,
// syncLocation,
// revisionId,
// }) {
// const data = await giteeHelper.downloadGistRevision({
// ...syncLocation,
// token,
// sha: revisionId,
// });
// return Provider.parseContent(data, contentId);
// },
});

View File

@ -346,8 +346,8 @@ export default {
},
/**
* https://developer.gitee.com/v3/gists/#create-a-gist
* https://developer.gitee.com/v3/gists/#edit-a-gist
* https://gitee.com/api/v5/swagger#/postV5Gists
* https://gitee.com/api/v5/swagger#/patchV5GistsId
*/
async uploadGist({
token,
@ -357,8 +357,7 @@ export default {
isPublic,
gistId,
}) {
const refreshedToken = await this.refreshToken(token);
const { body } = await request(refreshedToken, gistId ? {
const { body } = await request(token, gistId ? {
method: 'PATCH',
url: `https://gitee.com/api/v5/gists/${gistId}`,
body: {
@ -386,16 +385,15 @@ export default {
},
/**
* https://developer.gitee.com/v3/gists/#get-a-single-gist
* https://gitee.com/api/v5/swagger#/getV5Gists
*/
async downloadGist({
token,
gistId,
filename,
}) {
const refreshedToken = await this.refreshToken(token);
const result = (await request(refreshedToken, {
url: `https://gitee.com/api/v5/gists/${gistId}`,
const result = (await request(token, {
url: `https://api.github.com/gists/${gistId}`,
})).body.files[filename];
if (!result) {
throw new Error('Gist file not found.');
@ -404,35 +402,15 @@ export default {
},
/**
* https://developer.gitee.com/v3/gists/#list-gist-commits
* https://gitee.com/api/v5/swagger#/getV5GistsIdCommits
*/
async getGistCommits({
token,
gistId,
}) {
const refreshedToken = await this.refreshToken(token);
const { body } = await request(refreshedToken, {
const { body } = await request(token, {
url: `https://gitee.com/api/v5/gists/${gistId}/commits`,
});
return body;
},
/**
* https://developer.gitee.com/v3/gists/#get-a-specific-revision-of-a-gist
*/
async downloadGistRevision({
token,
gistId,
filename,
sha,
}) {
const refreshedToken = await this.refreshToken(token);
const result = (await request(refreshedToken, {
url: `https://gitee.com/api/v5/gists/${gistId}/${sha}`,
})).body.files[filename];
if (!result) {
throw new Error('Gist file not found.');
}
return result.content;
},
};

View File

@ -139,6 +139,12 @@ const requestPublish = () => {
});
};
const publishLocationAndStore = async (publishLocation, commitMsg) => {
const publishLocationToStore = await publish(publishLocation, commitMsg);
workspaceSvc.addPublishLocation(publishLocationToStore);
return publishLocationToStore;
};
const createPublishLocation = (publishLocation, featureId) => {
const currentFile = store.getters['file/current'];
publishLocation.fileId = currentFile.id;
@ -157,8 +163,7 @@ const createPublishLocation = (publishLocation, featureId) => {
return;
}
}
const publishLocationToStore = await publish(publishLocation, commitMsg);
workspaceSvc.addPublishLocation(publishLocationToStore);
await publishLocationAndStore(publishLocation, commitMsg);
store.dispatch('notification/info', `添加了一个新的发布位置 "${currentFile.name}".`);
if (featureId) {
badgeSvc.addBadge(featureId);
@ -169,5 +174,6 @@ const createPublishLocation = (publishLocation, featureId) => {
export default {
requestPublish,
publishLocationAndStore,
createPublishLocation,
};

165
static/landing/share.html Normal file
View 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 {
margin-top: 60px;
}
</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');
const workspaces = window.localStorage.getItem('data/workspaces');
let accessToken = null;
if (workspaces) {
const workspacesObj = JSON.parse(workspaces);
const sub = workspacesObj.data && workspacesObj.data.main && workspacesObj.data.main.sub;
if (sub) {
const tokens = window.localStorage.getItem('data/tokens');
if (tokens) {
const tokensObj = JSON.parse(tokens);
accessToken = tokensObj.data && tokensObj.data.gitee && tokensObj.data.gitee[sub] && tokensObj.data.gitee[sub].accessToken;
}
}
}
let url = `https://gitee.com/api/v5/gists/${gistId}`;
if (accessToken) {
url = `${url}?access_token=${accessToken}`;
}
xhr.open('GET', url);
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;
// 头部
appendTagHtml(newdoc, 'style', currHead);
// title
document.title = newdoc.title + ' - StackEdit中文版';
// 内容
const shareContent = document.getElementsByClassName('share-content')[0];
shareContent.innerHTML = newdoc.body.innerHTML;
} 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.getElementsByClassName('share-content')[0].innerHTML = `${rateLimit ? "请求太过频繁" : "无权限访问"},请登录 <a href="${appUri}" target="_brank">主文档空间</a> 后再刷新此页面!`;
} else {
console.error('An error occurred: ' + xhr.status);
document.getElementsByClassName('share-content')[0].innerHTML = '分享内容获取失败或已失效!';
}
};
xhr.send();
}
</script>
</head>
<body>
<div class="share-header">
<nav>
<a class="logo" href="/" target="_blank">
<img src="static/landing/logo.svg" height="30px"/>
</a>
<ul>
<li><a href="/" target="_blank">首页</a></li>
<li><a href="app" target="_blank">写笔记</a></li>
</ul>
</nav>
</div>
<div class="share-content stackedit">
<div 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>