diff --git a/README.md b/README.md
index d8bc5fe9..70aa5861 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,7 @@ StackEdit中文版
- 支持预览区域选择主题样式(2022-12-04)
- Gitlab的支持优化(2023-02-23)
- 导出HTML、PDF支持带预览主题导出(2023-02-26)
+- 支持分享文档(2023-03-30)
## 国外开源版本弊端:
- 作者已经不维护了
diff --git a/package-lock.json b/package-lock.json
index 8aa07282..bde12dd1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "stackedit",
- "version": "5.15.17",
+ "version": "5.15.19",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index c2a2b4cb..8819908e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "stackedit",
- "version": "5.15.18",
+ "version": "5.15.19",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0",
diff --git a/server/index.js b/server/index.js
index f04790a2..9ad7d738 100644
--- a/server/index.js
+++ b/server/index.js
@@ -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') {
diff --git a/src/components/Modal.vue b/src/components/Modal.vue
index b3f224bd..e6f3f441 100644
--- a/src/components/Modal.vue
+++ b/src/components/Modal.vue
@@ -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,
diff --git a/src/components/PreviewInPageButtons.vue b/src/components/PreviewInPageButtons.vue
index c7d15f20..4d6ce2ff 100644
--- a/src/components/PreviewInPageButtons.vue
+++ b/src/components/PreviewInPageButtons.vue
@@ -4,6 +4,9 @@
+
+
+
@@ -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;
+ }
+ },
},
};
@@ -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);
diff --git a/src/components/menus/PublishMenu.vue b/src/components/menus/PublishMenu.vue
index 48a4167a..9a835acf 100644
--- a/src/components/menus/PublishMenu.vue
+++ b/src/components/menus/PublishMenu.vue
@@ -43,7 +43,7 @@
- 发布到 Gist
+ 发布到 GitHubGist
{{token.name}}
@@ -53,6 +53,11 @@
+
+
+ 发布到 GiteeGist
+ {{token.name}}
+
发布到 Gitee
@@ -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'),
diff --git a/src/components/menus/SyncMenu.vue b/src/components/menus/SyncMenu.vue
index 2cef7982..d01555eb 100644
--- a/src/components/menus/SyncMenu.vue
+++ b/src/components/menus/SyncMenu.vue
@@ -46,7 +46,7 @@
- 在Gist上保存
+ 在GitHubGist上保存
{{token.name}}
@@ -61,6 +61,11 @@
在Gitee上保存
{{token.name}}
+
+
+ 在GiteeGist上保存
+ {{token.name}}
+
@@ -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', {
diff --git a/src/components/modals/PublishManagementModal.vue b/src/components/modals/PublishManagementModal.vue
index 52726eab..7e7cc49d 100644
--- a/src/components/modals/PublishManagementModal.vue
+++ b/src/components/modals/PublishManagementModal.vue
@@ -4,8 +4,8 @@
- {{currentFileName}} is published to the following location(s):
- {{currentFileName}} is not published yet.
+ {{currentFileName}} 被发布到了以下位置:
+ {{currentFileName}} 还没有被发布.
+
+
+ 分享链接: {{shareUrl(location)}}
+
+
+
@@ -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)}`;
+ },
},
};
diff --git a/src/components/modals/providers/GistPublishModal.vue b/src/components/modals/providers/GistPublishModal.vue
index b6dc2fb3..294e47df 100644
--- a/src/components/modals/providers/GistPublishModal.vue
+++ b/src/components/modals/providers/GistPublishModal.vue
@@ -1,11 +1,11 @@
-
+
-
发布 {{CurrentFileName}} 到Gist。
-
+ 发布 {{CurrentFileName}} 到GitHubGist。
+
@@ -15,10 +15,10 @@
-
+
- 如果文件存在于Gist中,则将被覆盖。
+ 如果文件存在于GitHubGist中,则将被覆盖。
diff --git a/src/components/modals/providers/GistSyncModal.vue b/src/components/modals/providers/GistSyncModal.vue
index 703734eb..5899e0ad 100644
--- a/src/components/modals/providers/GistSyncModal.vue
+++ b/src/components/modals/providers/GistSyncModal.vue
@@ -1,11 +1,11 @@
-
+
-
将 {{currentFileName}} 保存到Gist并保持同步。
-
+ 将 {{currentFileName}} 保存到GitHubGist并保持同步。
+
@@ -15,10 +15,10 @@
-
+
- 如果文件存在于Gist中,则将被覆盖。
+ 如果文件存在于GitHubGist中,则将被覆盖。
diff --git a/src/components/modals/providers/GiteeGistPublishModal.vue b/src/components/modals/providers/GiteeGistPublishModal.vue
new file mode 100644
index 00000000..f3da97c9
--- /dev/null
+++ b/src/components/modals/providers/GiteeGistPublishModal.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
发布 {{CurrentFileName}} 到GiteeGist。
+
+
+
+
+
+
+
+ 如果文件存在于GiteeGist中,则将被覆盖。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/modals/providers/GiteeGistSyncModal.vue b/src/components/modals/providers/GiteeGistSyncModal.vue
new file mode 100644
index 00000000..a23dd622
--- /dev/null
+++ b/src/components/modals/providers/GiteeGistSyncModal.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
将 {{currentFileName}} 保存到GiteeGist并保持同步。
+
+
+
+
+
+
+
+ 如果文件存在于GiteeGist中,则将被覆盖。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/data/features.js b/src/data/features.js
index f03f2724..e1773955 100644
--- a/src/data/features.js
+++ b/src/data/features.js
@@ -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发布',
diff --git a/src/data/simpleModals.js b/src/data/simpleModals.js
index 4a53490b..e404f47f 100644
--- a/src/data/simpleModals.js
+++ b/src/data/simpleModals.js
@@ -60,6 +60,15 @@ export default {
'取消',
'确认清理',
),
+ shareHtml: simpleModal(
+ config => `给文档 "${config.name}" 创建了分享链接如下:
${config.url}
关闭该窗口后可以到发布中查看分享链接。
`,
+ '关闭窗口',
+ ),
+ shareHtmlPre: simpleModal(
+ config => `将给文档 "${config.name}" 创建分享链接,创建后将会把文档公开发布到GiteeGist中。您确定吗?
`,
+ '取消',
+ '确认分享',
+ ),
signInForComment: simpleModal(
`您必须使用 Google 登录才能开始评论。
注意: 这将同步您的主文档空间。
`,
diff --git a/src/icons/Provider.vue b/src/icons/Provider.vue
index 3606880a..0faf7083 100644
--- a/src/icons/Provider.vue
+++ b/src/icons/Provider.vue
@@ -29,6 +29,7 @@ export default {
return 'couchdb';
case 'giteeAppData':
case 'giteeWorkspace':
+ case 'giteegist':
return 'gitee';
default:
return this.providerId;
diff --git a/src/icons/Share.vue b/src/icons/Share.vue
new file mode 100644
index 00000000..86e1a54b
--- /dev/null
+++ b/src/icons/Share.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/icons/index.js b/src/icons/index.js
index 0c8389aa..fbbc8ab8 100644
--- a/src/icons/index.js
+++ b/src/icons/index.js
@@ -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);
diff --git a/src/services/providers/giteeGistProvider.js b/src/services/providers/giteeGistProvider.js
new file mode 100644
index 00000000..eb05b966
--- /dev/null
+++ b/src/services/providers/giteeGistProvider.js
@@ -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);
+ // },
+});
diff --git a/src/services/providers/helpers/giteeHelper.js b/src/services/providers/helpers/giteeHelper.js
index a6ba57d6..f46f0c37 100644
--- a/src/services/providers/helpers/giteeHelper.js
+++ b/src/services/providers/helpers/giteeHelper.js
@@ -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;
- },
};
diff --git a/src/services/publishSvc.js b/src/services/publishSvc.js
index 7bcfc7b9..ab6a542c 100644
--- a/src/services/publishSvc.js
+++ b/src/services/publishSvc.js
@@ -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,
};
diff --git a/static/landing/share.html b/static/landing/share.html
new file mode 100644
index 00000000..eb0d450d
--- /dev/null
+++ b/static/landing/share.html
@@ -0,0 +1,165 @@
+
+
+
+ StackEdit中文版
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+