Fixed publishing
This commit is contained in:
parent
bdb01f407b
commit
d4624eba9d
@ -8,7 +8,9 @@ ENV V4_VERSION 4.3.22
|
|||||||
RUN npm pack stackedit@$V4_VERSION \
|
RUN npm pack stackedit@$V4_VERSION \
|
||||||
&& tar xzf stackedit-*.tgz --strip 1 \
|
&& tar xzf stackedit-*.tgz --strip 1 \
|
||||||
&& yarn \
|
&& yarn \
|
||||||
&& yarn cache clean
|
&& yarn cache clean \
|
||||||
|
&& rm -rf ~/.cache/bower \
|
||||||
|
&& rm -rf ~/.local/share/bower
|
||||||
|
|
||||||
WORKDIR /opt/stackedit
|
WORKDIR /opt/stackedit
|
||||||
|
|
||||||
|
@ -53,7 +53,9 @@ export default {
|
|||||||
this.ready = true;
|
this.ready = true;
|
||||||
tempFileSvc.setReady();
|
tempFileSvc.setReady();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err && err.message !== 'RELOAD') {
|
if (err && err.message === 'RELOAD') {
|
||||||
|
window.location.reload();
|
||||||
|
} else if (err && err.message !== 'RELOAD') {
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error(err); // eslint-disable-line no-console
|
||||||
this.$store.dispatch('notification/error', err);
|
this.$store.dispatch('notification/error', err);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tour-step__inner" v-else-if="step === 'editor'">
|
<div class="tour-step__inner" v-else-if="step === 'editor'">
|
||||||
<h2>Your Markdown editor</h2>
|
<h2>Your Markdown editor</h2>
|
||||||
<p>StackEdit renders your Markdown into HTML in real-time.</p>
|
<p>StackEdit converts your Markdown to HTML in real-time.</p>
|
||||||
<p>Click <icon-side-preview></icon-side-preview> to toggle the side preview.</p>
|
<p>Click <icon-side-preview></icon-side-preview> to toggle the side preview.</p>
|
||||||
<div class="tour-step__button-bar">
|
<div class="tour-step__button-bar">
|
||||||
<button class="button" @click="finish">Skip</button>
|
<button class="button" @click="finish">Skip</button>
|
||||||
@ -144,7 +144,7 @@ $tour-step-width: 240px;
|
|||||||
.tour-step__inner {
|
.tour-step__inner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: $tour-step-background;
|
background-color: $tour-step-background;
|
||||||
padding: 1em;
|
padding: 1.5em;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1.33;
|
line-height: 1.33;
|
||||||
width: $tour-step-width;
|
width: $tour-step-width;
|
||||||
@ -213,8 +213,10 @@ $tour-step-width: 240px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tour-step__button-bar {
|
.tour-step__button-bar {
|
||||||
margin-top: 1.75em;
|
margin-top: 1.5em;
|
||||||
text-align: right;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex--column">
|
<div class="flex flex--column">
|
||||||
<div>Import Markdown</div>
|
<div>Import Markdown</div>
|
||||||
<span>Open a plain text file.</span>
|
<span>Import a plain text file.</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<input class="hidden-file" id="import-html-file-input" type="file" @change="onImportHtml">
|
<input class="hidden-file" id="import-html-file-input" type="file" @change="onImportHtml">
|
||||||
|
@ -77,11 +77,8 @@ export default {
|
|||||||
...utils.queryParams,
|
...utils.queryParams,
|
||||||
exportWorkspace: true,
|
exportWorkspace: true,
|
||||||
}, true);
|
}, true);
|
||||||
const iframeElt = utils.createHiddenIframe(url);
|
window.location.href = url;
|
||||||
document.body.appendChild(iframeElt);
|
window.location.reload(true);
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(iframeElt);
|
|
||||||
}, 60000);
|
|
||||||
},
|
},
|
||||||
async settings() {
|
async settings() {
|
||||||
try {
|
try {
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
span {
|
span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
opacity: 0.6;
|
opacity: 0.67;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
|
|
||||||
.menu-entry__label {
|
.menu-entry__label {
|
||||||
|
@ -7,20 +7,32 @@
|
|||||||
<p v-if="publishLocations.length"><b>{{currentFileName}}</b> is published to the following location(s):</p>
|
<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-else><b>{{currentFileName}}</b> is not published yet.</p>
|
||||||
<div>
|
<div>
|
||||||
<div class="publish-entry flex flex--row flex--align-center" v-for="location in publishLocations" :key="location.id">
|
<div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id">
|
||||||
<div class="publish-entry__icon flex flex--column flex--center">
|
<div class="publish-entry__header flex flex--row flex--align-center">
|
||||||
<icon-provider :provider-id="location.providerId"></icon-provider>
|
<div class="publish-entry__icon flex flex--column flex--center">
|
||||||
|
<icon-provider :provider-id="location.providerId"></icon-provider>
|
||||||
|
</div>
|
||||||
|
<div class="publish-entry__description">
|
||||||
|
{{location.description}}
|
||||||
|
</div>
|
||||||
|
<div class="publish-entry__buttons flex flex--row flex--center">
|
||||||
|
<button class="publish-entry__button button" @click="remove(location)" v-title="'Remove location'">
|
||||||
|
<icon-delete></icon-delete>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="publish-entry__description">
|
<div class="publish-entry__row flex flex--row flex--align-center">
|
||||||
{{location.description}}
|
<div class="publish-entry__url">
|
||||||
</div>
|
{{location.url}}
|
||||||
<div class="publish-entry__buttons flex flex--row flex--center">
|
</div>
|
||||||
<a class="publish-entry__button button" :href="location.url" target="_blank" v-title="'Open location'">
|
<div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url">
|
||||||
<icon-open-in-new></icon-open-in-new>
|
<button class="publish-entry__button button" v-clipboard="location.url" @click="info('Location URL copied to clipboard!')" v-title="'Copy URL'">
|
||||||
</a>
|
<icon-content-copy></icon-content-copy>
|
||||||
<button class="publish-entry__button button" @click="remove(location)" v-title="'Remove location'">
|
</button>
|
||||||
<icon-delete></icon-delete>
|
<a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'Open location'">
|
||||||
</button>
|
<icon-open-in-new></icon-open-in-new>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -35,7 +47,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import ModalInner from './common/ModalInner';
|
import ModalInner from './common/ModalInner';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -54,6 +66,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapActions('notification', [
|
||||||
|
'info',
|
||||||
|
]),
|
||||||
remove(location) {
|
remove(location) {
|
||||||
this.$store.commit('publishLocation/deleteItem', location.id);
|
this.$store.commit('publishLocation/deleteItem', location.id);
|
||||||
},
|
},
|
||||||
@ -65,40 +80,70 @@ export default {
|
|||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.publish-entry {
|
.publish-entry {
|
||||||
padding: 0.5rem 0.25rem;
|
margin: 1.5em 0;
|
||||||
border-bottom: 1px solid $hr-color;
|
height: auto;
|
||||||
|
font-size: 17px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child {
|
$button-size: 30px;
|
||||||
border-bottom: none;
|
$small-button-size: 22px;
|
||||||
}
|
|
||||||
|
.publish-entry__header {
|
||||||
|
line-height: $button-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.publish-entry__row {
|
||||||
|
margin-top: 1px;
|
||||||
|
padding-top: 1px;
|
||||||
|
border-top: 1px solid rgba(128, 128, 128, 0.15);
|
||||||
|
line-height: $small-button-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.publish-entry__icon {
|
.publish-entry__icon {
|
||||||
height: 30px;
|
height: 22px;
|
||||||
width: 30px;
|
width: 22px;
|
||||||
margin-right: 0.75rem;
|
margin-right: 0.75rem;
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.publish-entry__description {
|
.publish-entry__description {
|
||||||
opacity: 0.5;
|
|
||||||
line-height: 1.4;
|
|
||||||
font-size: 0.9em;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.publish-entry__url {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 0.67em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.publish-entry__buttons {
|
.publish-entry__buttons {
|
||||||
margin-left: 0.75rem;
|
margin-left: 0.75rem;
|
||||||
|
|
||||||
|
.publish-entry__row & {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.publish-entry__button {
|
.publish-entry__button {
|
||||||
width: 38px;
|
width: $button-size;
|
||||||
height: 38px;
|
height: $button-size;
|
||||||
padding: 6px;
|
padding: 4px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
|
|
||||||
|
.publish-entry__row & {
|
||||||
|
width: $small-button-size;
|
||||||
|
height: $small-button-size;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form-entry label="Template">
|
<form-entry label="Template" v-if="format === 'html'">
|
||||||
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
|
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
|
||||||
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
|
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
|
||||||
{{ template.name }}
|
{{ template.name }}
|
||||||
@ -84,7 +84,7 @@ export default modalTemplate({
|
|||||||
resolve() {
|
resolve() {
|
||||||
// Return new location
|
// Return new location
|
||||||
const location = googleDriveProvider.makeLocation(this.config.token, this.fileId);
|
const location = googleDriveProvider.makeLocation(this.config.token, this.fileId);
|
||||||
if (this.format) {
|
if (this.format === 'html') {
|
||||||
location.templateId = this.selectedTemplate;
|
location.templateId = this.selectedTemplate;
|
||||||
}
|
}
|
||||||
this.config.resolve(location);
|
this.config.resolve(location);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<input slot="field" class="textfield" type="text" v-model.trim="domain" @keydown.enter="resolve()">
|
<input slot="field" class="textfield" type="text" v-model.trim="domain" @keydown.enter="resolve()">
|
||||||
<div class="form-entry__info">
|
<div class="form-entry__info">
|
||||||
<b>Example:</b> example.wordpress.com<br>
|
<b>Example:</b> example.wordpress.com<br>
|
||||||
<b>Jetpack plugin</b> is required for self-hosted sites.
|
<b>Note:</b> Jetpack is required for self-hosted sites.
|
||||||
</div>
|
</div>
|
||||||
</form-entry>
|
</form-entry>
|
||||||
<form-entry label="Existing post ID" info="optional">
|
<form-entry label="Existing post ID" info="optional">
|
||||||
|
@ -332,7 +332,6 @@ const localDbSvc = {
|
|||||||
// Clean data stored in localStorage
|
// Clean data stored in localStorage
|
||||||
localStorage.removeItem(`data/${id}`);
|
localStorage.removeItem(`data/${id}`);
|
||||||
});
|
});
|
||||||
window.location.reload();
|
|
||||||
throw new Error('RELOAD');
|
throw new Error('RELOAD');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +348,7 @@ const localDbSvc = {
|
|||||||
type: 'text/plain;charset=utf-8',
|
type: 'text/plain;charset=utf-8',
|
||||||
});
|
});
|
||||||
FileSaver.saveAs(blob, 'StackEdit workspace.json');
|
FileSaver.saveAs(blob, 'StackEdit workspace.json');
|
||||||
return;
|
throw new Error('RELOAD');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch workspace deletions and persist them as soon as possible
|
// Watch workspace deletions and persist them as soon as possible
|
||||||
|
@ -87,7 +87,7 @@ export default {
|
|||||||
date,
|
date,
|
||||||
}) {
|
}) {
|
||||||
const refreshedToken = await this.refreshToken(token);
|
const refreshedToken = await this.refreshToken(token);
|
||||||
await request(refreshedToken, {
|
return request(refreshedToken, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `https://public-api.wordpress.com/rest/v1.2/sites/${siteId || domain}/posts/${postId || 'new'}`,
|
url: `https://public-api.wordpress.com/rest/v1.2/sites/${siteId || domain}/posts/${postId || 'new'}`,
|
||||||
body: {
|
body: {
|
||||||
|
@ -122,6 +122,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Build the tree
|
// Build the tree
|
||||||
|
const parentsMap = {};
|
||||||
Object.entries(nodeMap).forEach(([, node]) => {
|
Object.entries(nodeMap).forEach(([, node]) => {
|
||||||
let parentNode = nodeMap[node.item.parentId];
|
let parentNode = nodeMap[node.item.parentId];
|
||||||
if (!parentNode || !parentNode.isFolder) {
|
if (!parentNode || !parentNode.isFolder) {
|
||||||
@ -129,6 +130,18 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parentNode = rootNode;
|
parentNode = rootNode;
|
||||||
|
} else if (node.isFolder) {
|
||||||
|
// Detect circular reference
|
||||||
|
const parentParents = parentsMap[node.item.parentId] || {};
|
||||||
|
if (parentParents[node.item.id]) {
|
||||||
|
// Node is already a parent of its supposed parent
|
||||||
|
parentNode = rootNode;
|
||||||
|
} else {
|
||||||
|
parentsMap[node.item.id] = {
|
||||||
|
...parentParents,
|
||||||
|
[node.item.parentId]: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (node.isFolder) {
|
if (node.isFolder) {
|
||||||
parentNode.folders.push(node);
|
parentNode.folders.push(node);
|
||||||
|
@ -83,33 +83,25 @@ const store = new Vuex.Store({
|
|||||||
},
|
},
|
||||||
pathsByItemId: (state, getters) => {
|
pathsByItemId: (state, getters) => {
|
||||||
const result = {};
|
const result = {};
|
||||||
const foldersById = state.folder.itemsById;
|
const processNode = (node, parentPath = '') => {
|
||||||
const getPath = (item) => {
|
let path = parentPath;
|
||||||
let itemPath = result[item.id];
|
if (node.item.id) {
|
||||||
if (!itemPath) {
|
path += node.item.name;
|
||||||
if (item.parentId === 'trash') {
|
if (node.isTrash) {
|
||||||
itemPath = `.stackedit-trash/${item.name}`;
|
path = '.stackedit-trash/';
|
||||||
} else {
|
} else if (node.isFolder) {
|
||||||
let { name } = item;
|
path += '/';
|
||||||
if (foldersById[item.id]) {
|
|
||||||
name += '/';
|
|
||||||
}
|
|
||||||
const parentFolder = foldersById[item.parentId];
|
|
||||||
if (parentFolder) {
|
|
||||||
itemPath = getPath(parentFolder) + name;
|
|
||||||
} else {
|
|
||||||
itemPath = name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
result[node.item.id] = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isFolder) {
|
||||||
|
node.folders.forEach(child => processNode(child, path));
|
||||||
|
node.files.forEach(child => processNode(child, path));
|
||||||
}
|
}
|
||||||
result[item.id] = itemPath;
|
|
||||||
return itemPath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[
|
processNode(getters['explorer/rootNode']);
|
||||||
...getters['folder/items'],
|
|
||||||
...getters['file/items'],
|
|
||||||
].forEach(item => getPath(item));
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
itemsByPath: (state, { allItemsById, pathsByItemId }) => {
|
itemsByPath: (state, { allItemsById, pathsByItemId }) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user