You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-16 00:14:34 +02:00
Doc: Add Joplin Cloud feature descriptions to Plans page
This commit is contained in:
@ -667,6 +667,30 @@ footer .bottom-links-row p {
|
||||
color: #0557ba;
|
||||
}
|
||||
|
||||
.joplin-cloud-feature-list .feature-description {
|
||||
max-width: 600px;
|
||||
font-size: .8em;
|
||||
color: #555555;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.joplin-cloud-feature-list .feature-title {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
margin-left: 10px;
|
||||
border: 2px solid black;
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 100px;
|
||||
height: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
WHAT'S NEW PAGE
|
||||
*****************************************************************/
|
||||
|
@ -86,11 +86,7 @@ https://github.com/laurent22/joplin/blob/dev/{{{sourceMarkdownFile}}}
|
||||
{{> footer}}
|
||||
</div>
|
||||
|
||||
<script
|
||||
src="{{jsBaseUrl}}/bootstrap5.0.2.bundle.min.js"
|
||||
rel="preload"
|
||||
as="script"
|
||||
></script>
|
||||
<script src="{{jsBaseUrl}}/bootstrap5.0.2.bundle.min.js" rel="preload" as="script"></script>
|
||||
<script src="{{{assetUrls.js.script}}}"></script>
|
||||
|
||||
{{> analytics}}
|
||||
|
@ -21,7 +21,7 @@
|
||||
</div>
|
||||
|
||||
{{#featureLabelsOn}}
|
||||
<p><i class="fas fa-check feature feature-on"></i>{{.}}</p>
|
||||
<p><i class="fas fa-check feature feature-on"></i>{{title}}</p>
|
||||
{{/featureLabelsOn}}
|
||||
|
||||
{{#featureLabelsOff}}
|
||||
|
@ -5,6 +5,9 @@
|
||||
<h1 translate class="text-center">
|
||||
Joplin Cloud <span class="frame-bg frame-bg-yellow">plans</span>
|
||||
</h1>
|
||||
|
||||
<button id="myButton">My button</button>
|
||||
|
||||
<p translate class="text-center sub-title">
|
||||
<a href="https://joplincloud.com">Joplin Cloud</a> allows you to synchronise your notes across devices. It also lets you publish notes, and collaborate on notebooks with your friends, family or colleagues.
|
||||
</p>
|
||||
@ -138,6 +141,14 @@
|
||||
} else {
|
||||
applyPeriod('yearly');
|
||||
}
|
||||
|
||||
$('.feature-description').hide();
|
||||
|
||||
$('.feature-title').click((event) => {
|
||||
event.preventDefault();
|
||||
const featureId = event.currentTarget.getAttribute('data-id');
|
||||
$('.feature-description-' + featureId).show();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
@ -19,6 +19,7 @@ export interface MarkdownTableHeader {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
filter?: Function;
|
||||
disableEscape?: boolean;
|
||||
disableHtmlEscape?: boolean;
|
||||
justify?: MarkdownTableJustify;
|
||||
}
|
||||
|
||||
@ -39,10 +40,12 @@ const markdownUtils = {
|
||||
return url;
|
||||
},
|
||||
|
||||
escapeTableCell(text: string) {
|
||||
escapeTableCell(text: string, escapeHtml = true) {
|
||||
// Disable HTML code
|
||||
text = text.replace(/</g, '<');
|
||||
text = text.replace(/>/g, '>');
|
||||
if (escapeHtml) {
|
||||
text = text.replace(/</g, '<');
|
||||
text = text.replace(/>/g, '>');
|
||||
}
|
||||
// Table cells can't contain new lines so replace with <br/>
|
||||
text = text.replace(/\n/g, '<br/>');
|
||||
// "|" is a reserved characters that should be escaped
|
||||
@ -173,7 +176,7 @@ const markdownUtils = {
|
||||
for (let j = 0; j < headers.length; j++) {
|
||||
const h = headers[j];
|
||||
const value = (h.filter ? h.filter(row[h.name]) : row[h.name]) || '';
|
||||
const valueMd = h.disableEscape ? value : markdownUtils.escapeTableCell(value);
|
||||
const valueMd = h.disableEscape ? value : markdownUtils.escapeTableCell(value, !h.disableHtmlEscape);
|
||||
rowMd.push(stringPadding(valueMd, minCellWidth, ' ', stringPadding.RIGHT));
|
||||
}
|
||||
output.push(`| ${rowMd.join(' | ')} |`);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from '../markdownUtils';
|
||||
import { _ } from '../locale';
|
||||
import { htmlentities } from '@joplin/utils/html';
|
||||
|
||||
type FeatureId = string;
|
||||
|
||||
@ -12,6 +13,7 @@ export enum PlanName {
|
||||
|
||||
interface PlanFeature {
|
||||
title: string;
|
||||
description?: string;
|
||||
basic: boolean;
|
||||
pro: boolean;
|
||||
teams: boolean;
|
||||
@ -32,7 +34,7 @@ export interface Plan {
|
||||
iconName: string;
|
||||
featuresOn: FeatureId[];
|
||||
featuresOff: FeatureId[];
|
||||
featureLabelsOn: string[];
|
||||
featureLabelsOn: any[];
|
||||
featureLabelsOff: string[];
|
||||
cfaLabel: string;
|
||||
cfaUrl: string;
|
||||
@ -112,6 +114,8 @@ export function findPrice(prices: StripePublicConfigPrice[], query: FindPriceQue
|
||||
}
|
||||
|
||||
const features = (): Record<FeatureId, PlanFeature> => {
|
||||
const shareNotebookTitle = _('Share a notebook with others');
|
||||
|
||||
return {
|
||||
maxItemSize: {
|
||||
title: _('Max note or attachment size'),
|
||||
@ -139,6 +143,7 @@ const features = (): Record<FeatureId, PlanFeature> => {
|
||||
},
|
||||
publishNote: {
|
||||
title: _('Publish notes to the internet'),
|
||||
description: 'You can publish a note from the Joplin app. You will get a link that you can share with other users, who can then view the note in their browser.',
|
||||
basic: true,
|
||||
pro: true,
|
||||
teams: true,
|
||||
@ -157,18 +162,21 @@ const features = (): Record<FeatureId, PlanFeature> => {
|
||||
},
|
||||
collaborate: {
|
||||
title: _('Collaborate on a notebook with others'),
|
||||
description: _('This allows another user to share a notebook with you, and you can then both collaborate on it. It does not however allow you to share a notebook with someone else, unless you have the feature "%s".', shareNotebookTitle),
|
||||
basic: true,
|
||||
pro: true,
|
||||
teams: true,
|
||||
},
|
||||
share: {
|
||||
title: _('Share a notebook with others'),
|
||||
title: shareNotebookTitle,
|
||||
description: 'You can share a notebook with other Joplin Cloud users, who can then view the notes and edit them.',
|
||||
basic: false,
|
||||
pro: true,
|
||||
teams: true,
|
||||
},
|
||||
emailToNote: {
|
||||
title: _('Email to Note'),
|
||||
description: 'You can save your emails in Joplin Cloud by forwarding your emails to a special email address. The subject of the email will become the note title, and the email body will become the note content.',
|
||||
basic: false,
|
||||
pro: true,
|
||||
teams: true,
|
||||
@ -185,8 +193,15 @@ const features = (): Record<FeatureId, PlanFeature> => {
|
||||
pro: false,
|
||||
teams: true,
|
||||
},
|
||||
sharingAccessControl: {
|
||||
title: _('Sharing access control'),
|
||||
// sharingAccessControl: {
|
||||
// title: _('Sharing access control'),
|
||||
// basic: false,
|
||||
// pro: false,
|
||||
// teams: true,
|
||||
// },
|
||||
sharePermissions: {
|
||||
title: _('Share permissions'),
|
||||
description: 'With this feature you can define whether a notebook you share with someone can be edited or is read-only. It can be useful for example to share documentation that you do not want to be modified.',
|
||||
basic: false,
|
||||
pro: false,
|
||||
teams: true,
|
||||
@ -235,7 +250,7 @@ export const getFeatureById = (featureId: FeatureId): PlanFeature => {
|
||||
export const getFeaturesByPlan = (planName: PlanName, featureOn: boolean): PlanFeature[] => {
|
||||
const output: PlanFeature[] = [];
|
||||
|
||||
for (const [, v] of Object.entries(features)) {
|
||||
for (const [, v] of Object.entries(features())) {
|
||||
if (v[planName] === featureOn) {
|
||||
output.push(v);
|
||||
}
|
||||
@ -261,6 +276,7 @@ export const createFeatureTableMd = () => {
|
||||
{
|
||||
name: 'featureLabel',
|
||||
label: 'Feature',
|
||||
disableHtmlEscape: true,
|
||||
},
|
||||
{
|
||||
name: 'basic',
|
||||
@ -285,9 +301,20 @@ export const createFeatureTableMd = () => {
|
||||
return '✔️';
|
||||
};
|
||||
|
||||
for (const [, feature] of Object.entries(features())) {
|
||||
const makeFeatureLabel = (featureId: string, feature: PlanFeature) => {
|
||||
const output: string[] = [
|
||||
`${htmlentities(feature.title)}`,
|
||||
];
|
||||
if (feature.description) {
|
||||
output.push(`<a data-id=${htmlentities(featureId)} class="feature-title" name="feature-${htmlentities(featureId)}" href="#feature-${htmlentities(featureId)}">i</a>`);
|
||||
output.push(`<div class="feature-description feature-description-${htmlentities(featureId)}">${htmlentities(feature.description)}</div>`);
|
||||
}
|
||||
return output.join('');
|
||||
};
|
||||
|
||||
for (const [id, feature] of Object.entries(features())) {
|
||||
const row: MarkdownTableRow = {
|
||||
featureLabel: feature.title,
|
||||
featureLabel: makeFeatureLabel(id, feature),
|
||||
basic: getCellInfo(PlanName.Basic, feature),
|
||||
pro: getCellInfo(PlanName.Pro, feature),
|
||||
teams: getCellInfo(PlanName.Teams, feature),
|
||||
@ -316,7 +343,7 @@ export function getPlans(stripeConfig: StripePublicConfig): Record<PlanName, Pla
|
||||
iconName: 'basic-icon',
|
||||
featuresOn: getFeatureIdsByPlan(PlanName.Basic, true),
|
||||
featuresOff: getFeatureIdsByPlan(PlanName.Basic, false),
|
||||
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Basic, true),
|
||||
featureLabelsOn: getFeaturesByPlan(PlanName.Basic, true),
|
||||
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Basic, false),
|
||||
cfaLabel: _('Try it now'),
|
||||
cfaUrl: '',
|
||||
@ -338,7 +365,7 @@ export function getPlans(stripeConfig: StripePublicConfig): Record<PlanName, Pla
|
||||
iconName: 'pro-icon',
|
||||
featuresOn: getFeatureIdsByPlan(PlanName.Pro, true),
|
||||
featuresOff: getFeatureIdsByPlan(PlanName.Pro, false),
|
||||
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Pro, true),
|
||||
featureLabelsOn: getFeaturesByPlan(PlanName.Pro, true),
|
||||
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Pro, false),
|
||||
cfaLabel: _('Try it now'),
|
||||
cfaUrl: '',
|
||||
@ -360,7 +387,7 @@ export function getPlans(stripeConfig: StripePublicConfig): Record<PlanName, Pla
|
||||
iconName: 'business-icon',
|
||||
featuresOn: getFeatureIdsByPlan(PlanName.Teams, true),
|
||||
featuresOff: getFeatureIdsByPlan(PlanName.Teams, false),
|
||||
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Teams, true),
|
||||
featureLabelsOn: getFeaturesByPlan(PlanName.Teams, true),
|
||||
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Teams, false),
|
||||
cfaLabel: _('Try it now'),
|
||||
cfaUrl: '',
|
||||
|
@ -46,6 +46,7 @@
|
||||
"@rmp135/sql-ts": "1.18.0",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/jest": "29.5.4",
|
||||
"@types/markdown-it": "13.0.1",
|
||||
"@types/mustache": "4.2.2",
|
||||
"@types/node": "18.17.14",
|
||||
"@types/node-fetch": "2.6.4",
|
||||
|
@ -15,6 +15,7 @@ import { setLocale } from '@joplin/lib/locale';
|
||||
import applyTranslations from './utils/applyTranslations';
|
||||
import { loadSponsors } from '../utils/loadSponsors';
|
||||
import convertLinksToLocale from './utils/convertLinksToLocale';
|
||||
import { copyFile } from 'fs/promises';
|
||||
|
||||
interface BuildConfig {
|
||||
env: Env;
|
||||
@ -89,6 +90,38 @@ const jsBasePath = `${websiteAssetDir}/js`;
|
||||
const jsBaseUrl = `${baseUrl}/js`;
|
||||
|
||||
async function getAssetUrls(): Promise<AssetUrls> {
|
||||
const scriptsToImport: any[] = [
|
||||
// {
|
||||
// id: 'tippy',
|
||||
// sourcePath: rootDir + '/packages/tools/node_modules/tippy.js/dist/tippy-bundle.umd.min.js',
|
||||
// md5: '',
|
||||
// filename: '',
|
||||
// },
|
||||
// {
|
||||
// id: 'popper',
|
||||
// sourcePath: rootDir + '/packages/tools/node_modules/@popperjs/core/dist/umd/popper.min.js',
|
||||
// md5: '',
|
||||
// filename: '',
|
||||
// },
|
||||
];
|
||||
|
||||
for (const s of scriptsToImport) {
|
||||
const filename = basename(s.sourcePath);
|
||||
const sourceMd5 = await md5File(s.sourcePath);
|
||||
const targetPath = `${websiteAssetDir}/js/${filename}`;
|
||||
const targetMd5 = await md5File(targetPath);
|
||||
s.md5 = sourceMd5;
|
||||
s.filename = filename;
|
||||
|
||||
// We check the MD5, otherwise it makes nodemon goes into an infinite building loop
|
||||
if (sourceMd5 !== targetMd5) await copyFile(s.sourcePath, targetPath);
|
||||
}
|
||||
|
||||
const importedJs: Record<string, string> = {};
|
||||
for (const s of scriptsToImport) {
|
||||
importedJs[s.id] = `${jsBaseUrl}/${s.filename}?h=${await md5File(`${websiteAssetDir}/js/${s.filename}`)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
css: {
|
||||
fontawesome: `${cssBaseUrl}/fontawesome-all.min.css?h=${await md5File(`${cssBasePath}/fontawesome-all.min.css`)}`,
|
||||
@ -96,6 +129,7 @@ async function getAssetUrls(): Promise<AssetUrls> {
|
||||
},
|
||||
js: {
|
||||
script: `${jsBaseUrl}/script.js?h=${await md5File(`${jsBasePath}/script.js`)}`,
|
||||
...importedJs,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { filename } from '@joplin/lib/path-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import { Partials, TemplateParams } from './types';
|
||||
import { headerAnchor } from '@joplin/renderer';
|
||||
const MarkdownIt = require('markdown-it');
|
||||
import * as MarkdownIt from 'markdown-it';
|
||||
|
||||
export async function loadMustachePartials(partialDir: string) {
|
||||
const output: Partials = {};
|
||||
|
11
yarn.lock
11
yarn.lock
@ -5178,6 +5178,7 @@ __metadata:
|
||||
"@rmp135/sql-ts": 1.18.0
|
||||
"@types/fs-extra": 11.0.1
|
||||
"@types/jest": 29.5.4
|
||||
"@types/markdown-it": 13.0.1
|
||||
"@types/mustache": 4.2.2
|
||||
"@types/node": 18.17.14
|
||||
"@types/node-fetch": 2.6.4
|
||||
@ -8094,6 +8095,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/markdown-it@npm:13.0.1":
|
||||
version: 13.0.1
|
||||
resolution: "@types/markdown-it@npm:13.0.1"
|
||||
dependencies:
|
||||
"@types/linkify-it": "*"
|
||||
"@types/mdurl": "*"
|
||||
checksum: 184d383ac21903a9e6be1639cde2b0cc082d0366b423fd8a69d0f37d9d1d36338f66611226ba4ef1da6148f370a62e08f688e8147ead43d429d6ff213c38c062
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/mdast@npm:^3.0.0":
|
||||
version: 3.0.12
|
||||
resolution: "@types/mdast@npm:3.0.12"
|
||||
|
Reference in New Issue
Block a user