Merge branch 'main' into patch-3

This commit is contained in:
Fernando Ochoa Olivares 2024-08-06 11:09:51 -06:00 committed by GitHub
commit 61015ff99d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
72 changed files with 1421 additions and 1508 deletions

2
.envrc
View File

@ -1,2 +0,0 @@
layout node
use flake .

3
.gitignore vendored
View File

@ -66,9 +66,6 @@ typings/
# dotenv environment variables file
.env
# direnv
.direnv
# IDE
.idea/*
*.iml

View File

@ -4,7 +4,7 @@ import articleBodyStyles from './articleBodyStyles';
import getUsername from '../utils/get-username';
import {ActivityPubAPI} from '../api/activitypub';
import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub';
import {Avatar, Button, ButtonGroup, Heading, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system';
import {Avatar, Button, ButtonGroup, Heading, Icon, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system';
import {useBrowseSite} from '@tryghost/admin-x-framework/api/site';
import {useQuery} from '@tanstack/react-query';
import {useRouting} from '@tryghost/admin-x-framework/routing';
@ -123,6 +123,7 @@ const ActivityPubComponent: React.FC = () => {
actor={activity.actor}
layout={selectedOption.value}
object={activity.object}
type={activity.type}
/>
</li>
))}
@ -167,6 +168,7 @@ const ActivityPubComponent: React.FC = () => {
actor={activity.actor}
layout={selectedOption.value}
object={activity.object}
type={activity.object.type}
/>
</li>
))}
@ -320,7 +322,7 @@ ${image &&
);
};
const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string }> = ({actor, object, layout}) => {
const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string, type: string }> = ({actor, object, layout, type}) => {
const parser = new DOMParser();
const doc = parser.parseFromString(object.content || '', 'text/html');
@ -392,27 +394,40 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp
setTimeout(() => setIsClicked(false), 300); // Reset the animation class after 300ms
};
let author = actor;
if (type === 'Announce' && object.type === 'Note') {
author = typeof object.attributedTo === 'object' ? object.attributedTo as ActorProperties : actor;
}
if (layout === 'feed') {
return (
<>
{object && (
<div className='group/article relative flex cursor-pointer items-start gap-4 pt-4'>
<img className='z-10 w-9 rounded' src={actor.icon?.url}/>
<div className='border-1 z-10 -mt-1 flex flex-col items-start justify-between border-b border-b-grey-200 pb-4' data-test-activity>
<div className='relative z-10 flex w-full overflow-visible text-[1.5rem]'>
<p className='mr-1 truncate whitespace-nowrap font-bold' data-test-activity-heading>{actor.name}</p>
<span className='truncate text-grey-700'>{getUsername(actor)}</span>
<span className='whitespace-nowrap text-grey-700 before:mx-1 before:content-["·"]'>{timestamp}</span>
</div>
<div className='relative z-10 w-full gap-4'>
<div className='flex flex-col'>
{object.name && <Heading className='mb-1 leading-tight' level={4} data-test-activity-heading>{object.name}</Heading>}
<p className='text-pretty text-[1.5rem] text-grey-900'>{plainTextContent}</p>
{/* <p className='text-pretty text-md text-grey-900'>{object.content}</p> */}
{renderAttachment()}
<div className='mt-3 flex gap-2'>
<Button className={`self-start text-grey-500 transition-all hover:text-grey-800 ${isClicked ? 'bump' : ''} ${isLiked ? 'ap-red-heart text-red *:!fill-red hover:text-red' : ''}`} hideLabel={true} icon='heart' id="like" size='md' unstyled={true} onClick={handleLikeClick}/>
<span className={`text-grey-800 ${isLiked ? 'opacity-100' : 'opacity-0'}`}>1</span>
<div className='group/article relative cursor-pointer pt-4'>
{(type === 'Announce' && object.type === 'Note') && <div className='z-10 mb-2 flex items-center gap-4 text-grey-700'>
<div className='z-10 flex w-10 justify-end'><Icon colorClass='text-grey-700' name='reload' size={'sm'}></Icon></div>
<span className='z-10'>{actor.name} reposted</span>
</div>}
<div className='flex items-start gap-4'>
<img className='z-10 w-10 rounded' src={author.icon?.url}/>
<div className='border-1 z-10 -mt-1 flex flex-col items-start justify-between border-b border-b-grey-200 pb-4' data-test-activity>
<div className='relative z-10 mb-2 flex w-full flex-col overflow-visible text-[1.5rem]'>
<span className='mr-1 truncate whitespace-nowrap font-bold' data-test-activity-heading>{author.name}</span>
<div className='flex'>
<span className='truncate text-grey-700'>{getUsername(author)}</span>
<span className='whitespace-nowrap text-grey-700 before:mx-1 before:content-["·"]'>{timestamp}</span>
</div>
</div>
<div className='relative z-10 w-full gap-4'>
<div className='flex flex-col'>
{object.name && <Heading className='mb-1 leading-tight' level={4} data-test-activity-heading>{object.name}</Heading>}
<div dangerouslySetInnerHTML={({__html: object.content})} className='ap-note-content text-pretty text-[1.5rem] text-grey-900'></div>
{/* <p className='text-pretty text-md text-grey-900'>{object.content}</p> */}
{renderAttachment()}
<div className='mt-3 flex gap-2'>
<Button className={`self-start text-grey-500 transition-all hover:text-grey-800 ${isClicked ? 'bump' : ''} ${isLiked ? 'ap-red-heart text-red *:!fill-red hover:text-red' : ''}`} hideLabel={true} icon='heart' id="like" size='md' unstyled={true} onClick={handleLikeClick}/>
<span className={`text-grey-800 ${isLiked ? 'opacity-100' : 'opacity-0'}`}>1</span>
</div>
</div>
</div>
</div>

View File

@ -22,4 +22,17 @@ animation: bump 0.3s ease-in-out;
.ap-red-heart path {
fill: #F50B23;
}
}
.ap-note-content a {
color: rgb(236 72 153) !important;
}
.ap-note-content a:hover {
color: rgb(219 39 119) !important;
}
.ap-note-content p + p {
margin-top: 1.5rem !important;
}

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" id="Button-Refresh-Arrows--Streamline-Ultimate.svg" height="24" width="24"><desc>Button Refresh Arrows Streamline Icon: https://streamlinehq.com</desc><path d="m5.25 14.248 0 4.5 -4.5 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m18.75 9.748 0 -4.5 4.5 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M19.032 5.245A9.752 9.752 0 0 1 8.246 21" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M4.967 18.751A9.753 9.753 0 0 1 15.754 3" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 819 B

View File

@ -13,7 +13,7 @@ export type ObjectProperties = {
name: string;
content: string;
url?: string | undefined;
attributedTo?: string | object[] | undefined;
attributedTo?: object | string | object[] | undefined;
image?: string;
published?: string;
preview?: {type: string, content: string};

View File

@ -44,16 +44,16 @@
},
"dependencies": {
"@headlessui/react": "1.7.19",
"@tiptap/core": "2.5.8",
"@tiptap/extension-blockquote": "2.5.8",
"@tiptap/extension-document": "2.5.8",
"@tiptap/extension-hard-break": "2.5.8",
"@tiptap/extension-link": "2.5.8",
"@tiptap/extension-paragraph": "2.5.8",
"@tiptap/extension-placeholder": "2.5.8",
"@tiptap/extension-text": "2.5.8",
"@tiptap/pm": "2.5.8",
"@tiptap/react": "2.5.8",
"@tiptap/core": "2.5.9",
"@tiptap/extension-blockquote": "2.5.9",
"@tiptap/extension-document": "2.5.9",
"@tiptap/extension-hard-break": "2.5.9",
"@tiptap/extension-link": "2.5.9",
"@tiptap/extension-paragraph": "2.5.9",
"@tiptap/extension-placeholder": "2.5.9",
"@tiptap/extension-text": "2.5.9",
"@tiptap/pm": "2.5.9",
"@tiptap/react": "2.5.9",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-string-replace": "1.1.1"

View File

@ -1,43 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1708501555,
"narHash": "sha256-zJaF0RkdIPbh8LTmnpW/E7tZYpqIE+MePzlWwUNob4c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b50a77c03d640716296021ad58950b1bb0345799",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"systems": "systems"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,49 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
systems.url = "github:nix-systems/default";
};
outputs = {
systems,
nixpkgs,
...
} @ inputs: let
yarn_overlay = final: prev: {
yarn = prev.yarn.overrideAttrs(finalAttrs: prevAttrs: {
# This is to make sure that yarn runs the correct node version
# https://github.com/NixOS/nixpkgs/issues/145634#issuecomment-1627476963
installPhase = prevAttrs.installPhase + ''
ln -fs $out/libexec/yarn/bin/yarn $out/bin/yarn
ln -fs $out/libexec/yarn/bin/yarn.js $out/bin/yarn.js
ln -fs $out/libexec/yarn/bin/yarn $out/bin/yarnpkg
'';
});
};
# This gives us a central place to set the node version
node_overlay = final: prev: {
nodejs = prev.nodejs-18_x;
};
eachSystem = f:
nixpkgs.lib.genAttrs (import systems) (
system:
f ((nixpkgs.legacyPackages.${system}.extend yarn_overlay).extend node_overlay)
);
in {
devShells = eachSystem (pkgs: {
default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
yarn
];
shellHook = ''
echo "node `${pkgs.nodejs}/bin/node --version`"
'';
};
});
};
}

View File

@ -24,6 +24,6 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2"
"@tryghost/errors": "1.3.5"
}
}

View File

@ -95,7 +95,9 @@
@placeholder={{@bodyPlaceholder}}
@cardConfig={{@cardOptions}}
@onChange={{@onBodyChange}}
@updateSecondaryInstanceModel={{@updateSecondaryInstanceModel}}
@registerAPI={{this.registerEditorAPI}}
@registerSecondaryAPI={{this.registerSecondaryEditorAPI}}
@cursorDidExitAtTop={{if this.feature.editorExcerpt this.focusExcerpt this.focusTitle}}
@updateWordCount={{@updateWordCount}}
@updatePostTkCount={{@updatePostTkCount}}

View File

@ -15,6 +15,7 @@ export default class GhKoenigEditorLexical extends Component {
uploadUrl = `${ghostPaths().apiRoot}/images/upload/`;
editorAPI = null;
secondaryEditorAPI = null;
skipFocusEditor = false;
@tracked titleIsHovered = false;
@ -232,6 +233,12 @@ export default class GhKoenigEditorLexical extends Component {
this.args.registerAPI(API);
}
@action
registerSecondaryEditorAPI(API) {
this.secondaryEditorAPI = API;
this.args.registerSecondaryAPI(API);
}
// focus the editor when the editor canvas is clicked below the editor content,
// otherwise the browser will defocus the editor and the cursor will disappear
@action

View File

@ -853,6 +853,7 @@
post=this.post
editorAPI=this.editorAPI
toggleSettingsMenu=this.toggleSettingsMenu
secondaryEditorAPI=this.secondaryEditorAPI
}}
@close={{this.closePostHistory}}
@modifier="total-overlay post-history" />

View File

@ -669,34 +669,43 @@ export default class KoenigLexicalEditor extends Component {
const multiplayerDocId = cardConfig.post.id;
const multiplayerUsername = this.session.user.name;
const KGEditorComponent = ({isInitInstance}) => {
return (
<div style={isInitInstance ? {visibility: 'hidden', position: 'absolute'} : {}}>
<KoenigComposer
editorResource={this.editorResource}
cardConfig={cardConfig}
enableMultiplayer={enableMultiplayer}
fileUploader={{useFileUpload, fileTypes}}
initialEditorState={this.args.lexical}
multiplayerUsername={multiplayerUsername}
multiplayerDocId={multiplayerDocId}
multiplayerEndpoint={multiplayerEndpoint}
onError={this.onError}
darkMode={this.feature.nightShift}
isTKEnabled={true}
>
<KoenigEditor
editorResource={this.editorResource}
cursorDidExitAtTop={isInitInstance ? null : this.args.cursorDidExitAtTop}
placeholderText={isInitInstance ? null : this.args.placeholderText}
darkMode={isInitInstance ? null : this.feature.nightShift}
onChange={isInitInstance ? this.args.updateSecondaryInstanceModel : this.args.onChange}
registerAPI={isInitInstance ? this.args.registerSecondaryAPI : this.args.registerAPI}
/>
<WordCountPlugin editorResource={this.editorResource} onChange={isInitInstance ? () => {} : this.args.updateWordCount} />
<TKCountPlugin editorResource={this.editorResource} onChange={isInitInstance ? () => {} : this.args.updatePostTkCount} />
</KoenigComposer>
</div>
);
};
return (
<div className={['koenig-react-editor', 'koenig-lexical', this.args.className].filter(Boolean).join(' ')}>
<ErrorHandler config={this.config}>
<Suspense fallback={<p className="koenig-react-editor-loading">Loading editor...</p>}>
<KoenigComposer
editorResource={this.editorResource}
cardConfig={cardConfig}
enableMultiplayer={enableMultiplayer}
fileUploader={{useFileUpload, fileTypes}}
initialEditorState={this.args.lexical}
multiplayerUsername={multiplayerUsername}
multiplayerDocId={multiplayerDocId}
multiplayerEndpoint={multiplayerEndpoint}
onError={this.onError}
darkMode={this.feature.nightShift}
isTKEnabled={true}
>
<KoenigEditor
editorResource={this.editorResource}
cursorDidExitAtTop={this.args.cursorDidExitAtTop}
placeholderText={this.args.placeholder}
darkMode={this.feature.nightShift}
onChange={this.args.onChange}
registerAPI={this.args.registerAPI}
/>
<WordCountPlugin editorResource={this.editorResource} onChange={this.args.updateWordCount} />
<TKCountPlugin editorResource={this.editorResource} onChange={this.args.updatePostTkCount} />
</KoenigComposer>
<KGEditorComponent />
<KGEditorComponent isInitInstance={true} />
</Suspense>
</ErrorHandler>
</div>

View File

@ -33,6 +33,7 @@
@lexical={{this.selectedRevision.lexical}}
@cardConfig={{this.cardConfig}}
@registerAPI={{this.registerSelectedEditorApi}}
@registerSecondaryAPI={{this.registerSecondarySelectedEditorApi}}
/>
</div>
</div>

View File

@ -31,6 +31,7 @@ export default class ModalPostHistory extends Component {
super(...arguments);
this.post = this.args.model.post;
this.editorAPI = this.args.model.editorAPI;
this.secondaryEditorAPI = this.args.model.secondaryEditorAPI;
this.toggleSettingsMenu = this.args.model.toggleSettingsMenu;
}
@ -101,6 +102,11 @@ export default class ModalPostHistory extends Component {
this.selectedEditor = api;
}
@action
registerSecondarySelectedEditorApi(api) {
this.secondarySelectedEditor = api;
}
@action
registerComparisonEditorApi(api) {
this.comparisonEditor = api;
@ -130,6 +136,7 @@ export default class ModalPostHistory extends Component {
updateEditor: () => {
const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical);
this.editorAPI.editorInstance.setEditorState(state);
this.secondaryEditorAPI.editorInstance.setEditorState(state);
},
closePostHistoryModal: () => {
this.closeModal();

View File

@ -297,6 +297,11 @@ export default class LexicalEditorController extends Controller {
this._timedSaveTask.perform();
}
@action
updateSecondaryInstanceModel(lexical) {
this.set('post.secondaryLexicalState', JSON.stringify(lexical));
}
@action
updateTitleScratch(title) {
this.set('post.titleScratch', title);
@ -423,6 +428,11 @@ export default class LexicalEditorController extends Controller {
this.editorAPI = API;
}
@action
registerSecondaryEditorAPI(API) {
this.secondaryEditorAPI = API;
}
@action
clearFeatureImage() {
this.post.set('featureImage', null);
@ -1221,7 +1231,6 @@ export default class LexicalEditorController extends Controller {
_timedSaveTask;
/* Private methods -------------------------------------------------------*/
_hasDirtyAttributes() {
let post = this.post;
@ -1229,8 +1238,7 @@ export default class LexicalEditorController extends Controller {
return false;
}
// if the Adapter failed to save the post isError will be true
// and we should consider the post still dirty.
// If the Adapter failed to save the post, isError will be true, and we should consider the post still dirty.
if (post.get('isError')) {
this._leaveModalReason = {reason: 'isError', context: post.errors.messages};
return true;
@ -1245,37 +1253,32 @@ export default class LexicalEditorController extends Controller {
return true;
}
// titleScratch isn't an attr so needs a manual dirty check
// Title scratch comparison
if (post.titleScratch !== post.title) {
this._leaveModalReason = {reason: 'title is different', context: {current: post.title, scratch: post.titleScratch}};
return true;
}
// scratch isn't an attr so needs a manual dirty check
// Lexical and scratch comparison
let lexical = post.get('lexical');
let scratch = post.get('lexicalScratch');
// additional guard in case we are trying to compare null with undefined
if (scratch || lexical) {
if (scratch !== lexical) {
// lexical can dynamically set direction on loading editor state (e.g. "rtl"/"ltr") per the DOM context
// and we need to ignore this as a change from the user; see https://github.com/facebook/lexical/issues/4998
const scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : [];
const lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : [];
let secondaryLexical = post.get('secondaryLexicalState');
// // nullling is typically faster than delete
scratchChildNodes.forEach(child => child.direction = null);
lexicalChildNodes.forEach(child => child.direction = null);
let lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : [];
let scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : [];
let secondaryLexicalChildNodes = secondaryLexical ? JSON.parse(secondaryLexical).root?.children : [];
if (JSON.stringify(scratchChildNodes) === JSON.stringify(lexicalChildNodes)) {
return false;
}
lexicalChildNodes.forEach(child => child.direction = null);
scratchChildNodes.forEach(child => child.direction = null);
secondaryLexicalChildNodes.forEach(child => child.direction = null);
this._leaveModalReason = {reason: 'lexical is different', context: {current: lexical, scratch}};
return true;
}
}
// Compare initLexical with scratch
let isSecondaryDirty = secondaryLexical && scratch && JSON.stringify(secondaryLexicalChildNodes) !== JSON.stringify(scratchChildNodes);
// new+unsaved posts always return `hasDirtyAttributes: true`
// Compare lexical with scratch
let isLexicalDirty = lexical && scratch && JSON.stringify(lexicalChildNodes) !== JSON.stringify(scratchChildNodes);
// New+unsaved posts always return `hasDirtyAttributes: true`
// so we need a manual check to see if any
if (post.get('isNew')) {
let changedAttributes = Object.keys(post.changedAttributes());
@ -1286,15 +1289,26 @@ export default class LexicalEditorController extends Controller {
return changedAttributes.length ? true : false;
}
// we've covered all the non-tracked cases we care about so fall
// We've covered all the non-tracked cases we care about so fall
// back on Ember Data's default dirty attribute checks
let {hasDirtyAttributes} = post;
if (hasDirtyAttributes) {
this._leaveModalReason = {reason: 'post.hasDirtyAttributes === true', context: post.changedAttributes()};
return true;
}
return hasDirtyAttributes;
// If either comparison is not dirty, return false, because scratch is always up to date.
if (!isSecondaryDirty || !isLexicalDirty) {
return false;
}
// If both comparisons are dirty, consider the post dirty
if (isSecondaryDirty && isLexicalDirty) {
this._leaveModalReason = {reason: 'initLexical and lexical are different from scratch', context: {secondaryLexical, lexical, scratch}};
return true;
}
return false;
}
_showSaveNotification(prevStatus, status, delayed) {

View File

@ -136,6 +136,9 @@ export default Model.extend(Comparable, ValidationEngine, {
scratch: null,
lexicalScratch: null,
titleScratch: null,
//This is used to store the initial lexical state from the
// secondary editor to get the schema up to date in case its outdated
secondaryLexicalState: null,
// For use by date/time pickers - will be validated then converted to UTC
// on save. Updated by an observer whenever publishedAtUTC changes.

View File

@ -73,6 +73,7 @@
@body={{readonly this.post.lexicalScratch}}
@bodyPlaceholder={{concat "Begin writing your " this.post.displayName "..."}}
@onBodyChange={{this.updateScratch}}
@updateSecondaryInstanceModel={{this.updateSecondaryInstanceModel}}
@headerOffset={{editor.headerHeight}}
@scrollContainerSelector=".gh-koenig-editor"
@scrollOffsetBottomSelector=".gh-mobile-nav-bar"
@ -97,6 +98,7 @@
}}
@postType={{this.post.displayName}}
@registerAPI={{this.registerEditorAPI}}
@registerSecondaryAPI={{this.registerSecondaryEditorAPI}}
@savePostTask={{this.savePostTask}}
/>
@ -136,6 +138,7 @@
@updateSlugTask={{this.updateSlugTask}}
@savePostTask={{this.savePostTask}}
@editorAPI={{this.editorAPI}}
@secondaryEditorAPI={{this.secondaryEditorAPI}}
@toggleSettingsMenu={{this.toggleSettingsMenu}}
/>
{{/if}}

View File

@ -208,7 +208,8 @@ describe('Unit: Controller: lexical-editor', function () {
titleScratch: 'this is a title',
status: 'published',
lexical: initialLexicalString,
lexicalScratch: initialLexicalString
lexicalScratch: initialLexicalString,
secondaryLexicalState: initialLexicalString
}));
// synthetically update the lexicalScratch as if the editor itself made the modifications on loading the initial editorState
@ -225,5 +226,47 @@ describe('Unit: Controller: lexical-editor', function () {
isDirty = controller.get('hasDirtyAttributes');
expect(isDirty).to.be.true;
});
it('dirty is false if secondaryLexical and scratch matches, but lexical is outdated', async function () {
const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
let controller = this.owner.lookup('controller:lexical-editor');
controller.set('post', EmberObject.create({
title: 'this is a title',
titleScratch: 'this is a title',
status: 'published',
lexical: initialLexicalString,
lexicalScratch: lexicalScratch,
secondaryLexicalState: secondLexicalInstance
}));
let isDirty = controller.get('hasDirtyAttributes');
expect(isDirty).to.be.false;
});
it('dirty is true if secondaryLexical and lexical does not match scratch', async function () {
const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content1234","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`;
let controller = this.owner.lookup('controller:lexical-editor');
controller.set('post', EmberObject.create({
title: 'this is a title',
titleScratch: 'this is a title',
status: 'published',
lexical: initialLexicalString,
lexicalScratch: lexicalScratch,
secondaryLexicalState: secondLexicalInstance
}));
controller.send('updateScratch',JSON.parse(lexicalScratch));
let isDirty = controller.get('hasDirtyAttributes');
expect(isDirty).to.be.true;
});
});
});

View File

@ -24,11 +24,11 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/promise": "0.3.10",
"@tryghost/tpl": "0.1.30",
"@tryghost/validator": "0.2.11",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/promise": "0.3.12",
"@tryghost/tpl": "0.1.32",
"@tryghost/validator": "0.2.14",
"jsonpath": "1.1.1",
"lodash": "4.17.21"
}

View File

@ -24,8 +24,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"bson-objectid": "2.0.4"
}
}

View File

@ -24,6 +24,6 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/logging": "2.4.15"
"@tryghost/logging": "2.4.18"
}
}

View File

@ -26,14 +26,14 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/in-memory-repository": "0.0.0",
"@tryghost/logging": "2.4.15",
"@tryghost/logging": "2.4.18",
"@tryghost/nql": "0.12.3",
"@tryghost/nql-filter-expansions": "0.0.0",
"@tryghost/post-events": "0.0.0",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"bson-objectid": "2.0.4",
"lodash": "4.17.21"
},

View File

@ -17,6 +17,7 @@ class StaffServiceWrapper {
const mailer = new GhostMailer();
const settingsCache = require('../../../shared/settings-cache');
const urlUtils = require('../../../shared/url-utils');
const {blogIcon} = require('../../../server/lib/image');
const settingsHelpers = require('../settings-helpers');
this.api = new StaffService({
@ -26,6 +27,7 @@ class StaffServiceWrapper {
settingsHelpers,
settingsCache,
urlUtils,
blogIcon,
DomainEvents,
memberAttributionService: memberAttribution.service,
labs

View File

@ -76,7 +76,7 @@
"@tryghost/api-framework": "0.0.0",
"@tryghost/api-version-compatibility-service": "0.0.0",
"@tryghost/audience-feedback": "0.0.0",
"@tryghost/bookshelf-plugins": "0.6.19",
"@tryghost/bookshelf-plugins": "0.6.21",
"@tryghost/bootstrap-socket": "0.0.0",
"@tryghost/collections": "0.0.0",
"@tryghost/color-utils": "0.2.2",
@ -84,24 +84,24 @@
"@tryghost/constants": "0.0.0",
"@tryghost/custom-theme-settings-service": "0.0.0",
"@tryghost/data-generator": "0.0.0",
"@tryghost/database-info": "0.3.24",
"@tryghost/debug": "0.1.30",
"@tryghost/database-info": "0.3.27",
"@tryghost/debug": "0.1.32",
"@tryghost/domain-events": "0.0.0",
"@tryghost/donations": "0.0.0",
"@tryghost/dynamic-routing-events": "0.0.0",
"@tryghost/email-analytics-provider-mailgun": "0.0.0",
"@tryghost/email-analytics-service": "0.0.0",
"@tryghost/email-content-generator": "0.0.0",
"@tryghost/email-mock-receiver": "0.3.6",
"@tryghost/email-mock-receiver": "0.3.8",
"@tryghost/email-service": "0.0.0",
"@tryghost/email-suppression-list": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/express-dynamic-redirects": "0.0.0",
"@tryghost/external-media-inliner": "0.0.0",
"@tryghost/ghost": "0.0.0",
"@tryghost/helpers": "1.1.90",
"@tryghost/html-to-plaintext": "0.0.0",
"@tryghost/http-cache-utils": "0.1.15",
"@tryghost/http-cache-utils": "0.1.17",
"@tryghost/i18n": "0.0.0",
"@tryghost/image-transform": "1.3.0",
"@tryghost/importer-handler-content-files": "0.0.0",
@ -119,7 +119,7 @@
"@tryghost/link-redirects": "0.0.0",
"@tryghost/link-replacer": "0.0.0",
"@tryghost/link-tracking": "0.0.0",
"@tryghost/logging": "2.4.15",
"@tryghost/logging": "2.4.18",
"@tryghost/magic-link": "0.0.0",
"@tryghost/mail-events": "0.0.0",
"@tryghost/mailgun-client": "0.0.0",
@ -143,16 +143,16 @@
"@tryghost/mw-session-from-token": "0.0.0",
"@tryghost/mw-version-match": "0.0.0",
"@tryghost/mw-vhost": "0.0.0",
"@tryghost/nodemailer": "0.3.42",
"@tryghost/nodemailer": "0.3.45",
"@tryghost/nql": "0.12.3",
"@tryghost/oembed-service": "0.0.0",
"@tryghost/package-json": "0.0.0",
"@tryghost/post-revisions": "0.0.0",
"@tryghost/posts-service": "0.0.0",
"@tryghost/pretty-cli": "1.2.42",
"@tryghost/promise": "0.3.10",
"@tryghost/pretty-cli": "1.2.44",
"@tryghost/promise": "0.3.12",
"@tryghost/recommendations": "0.0.0",
"@tryghost/request": "1.0.5",
"@tryghost/request": "1.0.8",
"@tryghost/security": "0.0.0",
"@tryghost/session-service": "0.0.0",
"@tryghost/settings-path-manager": "0.0.0",
@ -162,14 +162,14 @@
"@tryghost/stats-service": "0.0.0",
"@tryghost/string": "0.2.12",
"@tryghost/tiers": "0.0.0",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"@tryghost/update-check-service": "0.0.0",
"@tryghost/url-utils": "4.4.8",
"@tryghost/validator": "0.2.11",
"@tryghost/validator": "0.2.14",
"@tryghost/verification-trigger": "0.0.0",
"@tryghost/version": "0.1.28",
"@tryghost/version": "0.1.30",
"@tryghost/webmentions": "0.0.0",
"@tryghost/zip": "1.1.43",
"@tryghost/zip": "1.1.46",
"amperize": "0.6.1",
"body-parser": "1.20.2",
"bookshelf": "1.2.0",
@ -210,7 +210,7 @@
"knex-migrator": "5.2.1",
"lib0": "0.2.94",
"lodash": "4.17.21",
"luxon": "3.4.4",
"luxon": "3.5.0",
"moment": "2.24.0",
"moment-timezone": "0.5.45",
"multer": "1.4.4",
@ -237,8 +237,8 @@
"devDependencies": {
"@actions/core": "1.10.1",
"@playwright/test": "1.38.1",
"@tryghost/express-test": "0.13.12",
"@tryghost/webhook-mock-receiver": "0.2.12",
"@tryghost/express-test": "0.13.15",
"@tryghost/webhook-mock-receiver": "0.2.14",
"@types/common-tags": "1.8.4",
"c8": "8.0.1",
"cli-progress": "3.12.0",
@ -265,8 +265,8 @@
"toml": "3.0.0"
},
"resolutions": {
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"jackspeak": "2.1.1",
"moment": "2.24.0",
"moment-timezone": "0.5.45"

View File

@ -78,7 +78,7 @@ const createPage = async (page, {title = 'Hello world', body = 'This is my post
await page.locator('[data-test-editor-title-input]').fill(title);
// wait for editor to be ready
await expect(page.locator('[data-lexical-editor="true"]')).toBeVisible();
await expect(page.locator('[data-lexical-editor="true"]').first()).toBeVisible();
// Continue to the body by pressing enter
await page.keyboard.press('Enter');
@ -304,7 +304,7 @@ test.describe('Publishing', () => {
await expect(publishedHeader).toContainText(date.toFormat('LLL d, yyyy'));
// add some extra text to the post
await adminPage.locator('[data-kg="editor"]').click();
await adminPage.locator('[data-kg="editor"]').first().click();
await adminPage.waitForTimeout(200); //
await adminPage.keyboard.type(' This is some updated text.');

View File

@ -423,7 +423,7 @@ const createPostDraft = async (page, {title = 'Hello world', body = 'This is my
await page.locator('[data-test-editor-title-input]').fill(title);
// wait for editor to be ready
await expect(page.locator('[data-lexical-editor="true"]')).toBeVisible();
await expect(page.locator('[data-lexical-editor="true"]').first()).toBeVisible();
// Continue to the body by pressing enter
await page.keyboard.press('Enter');

View File

@ -25,10 +25,10 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/nql": "0.12.3",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"lodash": "4.17.21"
}
}

View File

@ -18,7 +18,7 @@
"lib"
],
"devDependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/debug": "0.1.32",
"c8": "8.0.1",
"knex": "2.4.2",
"mocha": "10.2.0",
@ -27,7 +27,7 @@
},
"dependencies": {
"@faker-js/faker": "7.6.0",
"@tryghost/root-utils": "0.3.28",
"@tryghost/root-utils": "0.3.30",
"@tryghost/string": "0.2.12",
"csv-writer": "1.6.0",
"probability-distributions": "0.9.1"

View File

@ -19,7 +19,7 @@
"lib"
],
"devDependencies": {
"@tryghost/logging": "2.4.15",
"@tryghost/logging": "2.4.18",
"c8": "8.0.1",
"mocha": "10.2.0",
"should": "13.2.3"

View File

@ -23,7 +23,7 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/debug": "0.1.32",
"lodash": "4.17.21"
}
}

View File

@ -27,12 +27,12 @@
"dependencies": {
"@tryghost/color-utils": "0.2.2",
"@tryghost/email-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/html-to-plaintext": "0.0.0",
"@tryghost/kg-default-cards": "10.0.6",
"@tryghost/logging": "2.4.15",
"@tryghost/tpl": "0.1.30",
"@tryghost/validator": "0.2.11",
"@tryghost/logging": "2.4.18",
"@tryghost/tpl": "0.1.32",
"@tryghost/validator": "0.2.14",
"bson-objectid": "2.0.4",
"cheerio": "0.22.0",
"handlebars": "4.7.8",

View File

@ -36,7 +36,7 @@
"@nestjs/common": "10.3.10",
"@nestjs/core": "10.3.10",
"@nestjs/platform-express": "10.3.10",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"bson-objectid": "2.0.4",
"express": "4.19.2",
"reflect-metadata": "0.1.14",

View File

@ -21,7 +21,7 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/debug": "0.1.32",
"@tryghost/kg-default-cards": "10.0.6",
"@tryghost/string": "0.2.12",
"lodash": "4.17.21",

View File

@ -28,8 +28,8 @@
},
"dependencies": {
"@breejs/later": "4.2.0",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"bree": "6.5.0",
"cron-validate": "1.4.5",
"fastq": "1.17.1",

View File

@ -24,10 +24,10 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/link-redirects": "0.0.0",
"@tryghost/nql": "0.12.3",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"bson-objectid": "2.0.4",
"lodash": "4.17.21",
"moment": "2.29.4"

View File

@ -26,9 +26,9 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/validator": "0.2.11",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"@tryghost/validator": "0.2.14",
"jsonwebtoken": "8.5.1"
}
}

View File

@ -25,9 +25,9 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/in-memory-repository": "0.0.0",
"@tryghost/tpl": "0.1.30"
"@tryghost/tpl": "0.1.32"
},
"c8": {
"exclude": [

View File

@ -24,8 +24,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/logging": "2.4.15",
"@tryghost/debug": "0.1.32",
"@tryghost/logging": "2.4.18",
"@tryghost/metrics": "1.0.34",
"form-data": "4.0.0",
"lodash": "4.17.21",

View File

@ -31,14 +31,14 @@
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/magic-link": "0.0.0",
"@tryghost/member-events": "0.0.0",
"@tryghost/members-payments": "0.0.0",
"@tryghost/nql": "0.12.3",
"@tryghost/tpl": "0.1.30",
"@tryghost/validator": "0.2.11",
"@tryghost/tpl": "0.1.32",
"@tryghost/validator": "0.2.14",
"@types/jsonwebtoken": "9.0.6",
"body-parser": "1.20.2",
"bson-objectid": "2.0.4",

View File

@ -26,8 +26,8 @@
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/member-events": "0.0.0",
"moment-timezone": "0.5.34"
}

View File

@ -25,11 +25,11 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/members-csv": "0.0.0",
"@tryghost/metrics": "1.0.34",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"moment-timezone": "0.5.45"
}
}

View File

@ -26,8 +26,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"cookies": "0.9.1",
"jsonwebtoken": "8.5.1"
}

View File

@ -23,7 +23,7 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"bson-objectid": "2.0.4"
}
}

View File

@ -24,9 +24,9 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"csso": "5.0.5",
"terser": "5.31.3",
"tiny-glob": "0.2.9"

View File

@ -18,7 +18,7 @@
"lib"
],
"devDependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"c8": "8.0.1",
"mocha": "10.2.0",
"sinon": "15.2.0"

View File

@ -23,10 +23,10 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/http-cache-utils": "0.1.15",
"@tryghost/tpl": "0.1.30",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/http-cache-utils": "0.1.17",
"@tryghost/tpl": "0.1.32",
"lodash": "4.17.21",
"semver": "7.6.3"
}

View File

@ -23,8 +23,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"semver": "7.6.3"
}
}

View File

@ -23,9 +23,9 @@
},
"dependencies": {
"@extractus/oembed-extractor": "3.2.1",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/tpl": "0.1.32",
"charset": "1.0.1",
"cheerio": "0.22.0",
"iconv-lite": "0.6.3",

View File

@ -26,7 +26,7 @@
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/mongo-utils": "0.6.2",
"@tryghost/string": "0.2.12",
"lodash": "4.17.21"

View File

@ -25,8 +25,8 @@
"tmp": "0.2.1"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"fs-extra": "11.2.0",
"lodash": "4.17.21"
}

View File

@ -25,7 +25,7 @@
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/members-offers": "0.0.0",
"@tryghost/tiers": "0.0.0"
}

View File

@ -23,10 +23,10 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/nql": "0.12.3",
"@tryghost/post-events": "0.0.0",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"bson-objectid": "2.0.4"
}
}

View File

@ -30,10 +30,10 @@
"typescript": "5.4.5"
},
"dependencies": {
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/in-memory-repository": "0.0.0",
"@tryghost/bookshelf-repository": "0.0.0",
"@tryghost/logging": "2.4.15"
"@tryghost/logging": "2.4.18"
}
}

View File

@ -25,6 +25,6 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2"
"@tryghost/errors": "1.3.5"
}
}

View File

@ -24,8 +24,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/tpl": "0.1.30",
"@tryghost/errors": "1.3.5",
"@tryghost/tpl": "0.1.32",
"date-fns": "2.30.0"
}
}

View File

@ -23,9 +23,9 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/validator": "0.2.11",
"@tryghost/version": "0.1.28",
"@tryghost/errors": "1.3.5",
"@tryghost/validator": "0.2.14",
"@tryghost/version": "0.1.30",
"got": "9.6.0"
}
}

View File

@ -4,7 +4,7 @@ const {MilestoneCreatedEvent} = require('@tryghost/milestones');
// @NOTE: 'StaffService' is a vague name that does not describe what it's actually doing.
// Possibly, "StaffNotificationService" or "StaffEventNotificationService" would be a more accurate name
class StaffService {
constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, DomainEvents, labs, memberAttributionService}) {
constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, blogIcon, DomainEvents, labs, memberAttributionService}) {
this.logging = logging;
this.labs = labs;
/** @private */
@ -22,6 +22,7 @@ class StaffService {
settingsHelpers,
settingsCache,
urlUtils,
blogIcon,
labs
});
}

View File

@ -5,12 +5,13 @@ const glob = require('glob');
const {EmailAddressParser} = require('@tryghost/email-addresses');
class StaffServiceEmails {
constructor({logging, models, mailer, settingsHelpers, settingsCache, urlUtils, labs}) {
constructor({logging, models, mailer, settingsHelpers, settingsCache, blogIcon, urlUtils, labs}) {
this.logging = logging;
this.models = models;
this.mailer = mailer;
this.settingsHelpers = settingsHelpers;
this.settingsCache = settingsCache;
this.blogIcon = blogIcon;
this.urlUtils = urlUtils;
this.labs = labs;
@ -44,6 +45,7 @@ class StaffServiceEmails {
attributionUrl: attribution?.url || '',
referrerSource: attribution?.referrerSource,
siteTitle: this.settingsCache.get('title'),
siteIconUrl: this.blogIcon.getIconUrl(true),
siteUrl: this.urlUtils.getSiteUrl(),
siteDomain: this.siteDomain,
accentColor: this.settingsCache.get('accent_color'),
@ -103,6 +105,7 @@ class StaffServiceEmails {
offerData,
subscriptionData,
siteTitle: this.settingsCache.get('title'),
siteIconUrl: this.blogIcon.getIconUrl(true),
siteUrl: this.urlUtils.getSiteUrl(),
siteDomain: this.siteDomain,
accentColor: this.settingsCache.get('accent_color'),
@ -153,6 +156,7 @@ class StaffServiceEmails {
tierData,
subscriptionData,
siteTitle: this.settingsCache.get('title'),
siteIconUrl: this.blogIcon.getIconUrl(true),
siteUrl: this.urlUtils.getSiteUrl(),
siteDomain: this.siteDomain,
accentColor: this.settingsCache.get('accent_color'),
@ -182,6 +186,7 @@ class StaffServiceEmails {
return {
siteTitle: this.settingsCache.get('title'),
siteIconUrl: this.blogIcon.getIconUrl(true),
siteUrl: this.urlUtils.getSiteUrl(),
siteDomain: this.siteDomain,
accentColor: this.settingsCache.get('accent_color'),
@ -282,6 +287,7 @@ class StaffServiceEmails {
const templateData = {
siteTitle: this.settingsCache.get('title'),
siteUrl: this.urlUtils.getSiteUrl(),
siteIconUrl: this.blogIcon.getIconUrl(true),
siteDomain: this.siteDomain,
fromEmail: this.fromEmailAddress,
toEmail: to,

View File

@ -21,21 +21,33 @@
<tr>
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
{{#if siteIconUrl}}
<tr>
<td align="center" style="padding-bottom: 56px; text-align: center;"><a href="{{siteUrl}}"><img src="{{siteIconUrl}}" alt="{{siteTitle}}" border="0" width="48" height="48"></a></td>
</tr>
{{/if}}
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; margin-bottom: 15px;">Cha-ching! 💰 You received {{donation.amount}} from {{donation.name}}.</h1>
<table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">Cha-ching! You received a {{donation.amount}} tip from {{donation.name}}.</h1>
<table width="100" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
<tbody>
<tr>
<td align="center" style="padding: 32px 24px; text-align: center;">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">Type:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 17px; line-height: 26px; padding: 0; text-align: left; margin: 0; margin-bottom: 20px; color: #15171A; font-weight: 700;">One-time payment</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">From:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 17px; line-height: 26px; padding: 0; text-align: left; margin: 0; margin-bottom: 20px; color: #15171A; font-weight: 700;">{{#if memberData}}<a style="color:{{accentColor}}" href="{{memberData.adminUrl}}">{{donation.name}} ({{donation.email}})</a>{{else}}{{donation.name}} ({{donation.email}}){{/if}}</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">Amount received:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 17px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.amount}}</p>
</td>
</tr>
<tr>
<td align="center" style="padding: 24px; text-align: center;">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">From:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; text-align: left; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 700;">{{donation.name}} (<span style="color:{{accentColor}}; text-decoration: none;">{{donation.email}}</span>)</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">Amount received:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.amount}}</p>
</td>
</tr>
<tr>
<td style="padding:0 24px 24px;">
{{#if memberData}}
<a href="{{memberData.adminUrl}}" style="border:solid 1px {{accentColor}};border-radius:8px;box-sizing:border-box;display:inline-block;font-size:15px;font-weight:normal;margin:0;padding:10px 20px;text-decoration:none;background-color:{{accentColor}};border-color:{{accentColor}};color:#ffffff">View member</a>
{{else}}
<a href="{{adminUrl}}" style="border:solid 1px {{accentColor}};border-radius:8px;box-sizing:border-box;display:inline-block;font-size:15px;font-weight:normal;margin:0;padding:10px 20px;text-decoration:none;background-color:{{accentColor}};border-color:{{accentColor}};color:#ffffff">View dashboard</a>
{{/if}}
</td>
</tr>
</tbody>
</table>
</td>
@ -43,13 +55,13 @@
<!-- START FOOTER -->
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 80px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{toEmail}}</a></p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top; padding-top: 56px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{toEmail}}</a></p>
</td>
</tr>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 2px">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">here</a>.</p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">here</a>.</p>
</td>
</tr>

View File

@ -26,64 +26,46 @@
<tr>
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
{{#if siteIconUrl}}
<tr>
<td align="center" style="padding-bottom: 56px; text-align: center;"><a href="{{siteUrl}}"><img src="{{siteIconUrl}}" alt="{{siteTitle}}" border="0" width="48" height="48"></a></td>
</tr>
{{/if}}
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Congratulations!</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">You have a <span style="font-weight: bold; color: #15212A;">new free member</span>.</p>
<table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
<tbody>
<tr>
<td align="left" style="padding: 16px;">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="padding-right: 14px; background-color: #F9F9FA; text-align: left; vertical-align: middle;" valign="middle">
<div style="width: 48px; height: 48px; background-color: #15171A; border-radius: 999px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 19px; color: #FFFFFF; text-align: center; vertical-align: center; font-weight: 500; line-height: 47px;">
{{memberData.initials}}
</div>
</td>
<td style="padding-right: 8px; background-color: #F9F9FA; text-align: left; vertical-align: middle;" valign="middle">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberData.name}}</p>
{{#if memberData.showEmail}}
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #394047; font-weight: 400;">{{memberData.email}}</p>
{{/if}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Created on {{memberData.createdAt}}{{#if memberData.location}} &#8226; {{memberData.location}} {{/if}}
</p>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<tr>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-top: 32px; padding-bottom: 12px;">
{{#if referrerSource}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Signup info</p>
<hr style="border-bottom: 1px solid #F4F4F5; margin-top: 4px; margin-bottom: 8px;" />
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600; padding-bottom: 4px;">Source
<span style="font-weight: normal; color:#3A464C;"> - {{referrerSource}}</span>
</p>
{{#if attributionTitle}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">Page
<span style="font-weight: normal; color:#3A464C;"> - <a href="{{attributionUrl}}" style="font-weight: normal; color:#3A464C;text-decoration:none">{{attributionTitle}}</a></span>
</p>
{{/if}}
{{/if}}
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">You have a new free member</h1>
<table width="100" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
<tbody>
<tr>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-top: 32px; padding-bottom: 12px;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<td align="left" style="padding: 24px;">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
{{#if referrerSource}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
{{#if attributionTitle}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
{{/if}}
{{/if}}
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<tbody>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View member</a></td>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; vertical-align: top; background-color: {{accentColor}}; border-radius: 8px; text-align: center;"> <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 8px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 15px; font-weight: normal; margin: 0; padding: 10px 20px; border-color: {{accentColor}};">View member</a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
@ -91,21 +73,29 @@
</tr>
</tbody>
</table>
<hr/>
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
<p class="text-link" style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{memberData.adminUrl}}</p>
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td align="left" style="padding-top: 24px;">
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #7c8b9a; font-weight: normal; margin: 0; line-height: 25px;">Or copy and paste this URL into your browser:</p>
<p class="text-link" style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin:0; color: #7c8b9a; font-weight: normal;">{{memberData.adminUrl}}</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!-- START FOOTER -->
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 80px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{toEmail}}</a></p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top; padding-top: 56px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{toEmail}}</a></p>
</td>
</tr>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 2px">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">here</a>.</p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">here</a>.</p>
</td>
</tr>

View File

@ -26,77 +26,52 @@
<tr>
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
{{#if siteIconUrl}}
<tr>
<td align="center" style="padding-bottom: 56px; text-align: center;"><a href="{{siteUrl}}"><img src="{{siteIconUrl}}" alt="{{siteTitle}}" border="0" width="48" height="48"></a></td>
</tr>
{{/if}}
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Congratulations!</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">You have a <span style="font-weight: bold; color: #15212A;">new paid member</span>.</p>
<table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
<tbody>
<tr>
<td align="left" style="padding: 16px;">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="padding-right: 14px; vertical-align: middle;" valign="middle">
<div style="width: 48px; height: 48px; background-color: #15171A; border-radius: 999px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 19px; color: #FFFFFF; text-align: center; vertical-align: center; font-weight: 500; line-height: 47px;">
{{memberData.initials}}
</div>
</td>
<td style="padding-right: 8px; vertical-align: middle;" valign="middle">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberData.name}}</p>
{{#if memberData.showEmail}}
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #394047; font-weight: 400;">{{memberData.email}}</p>
{{/if}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Subscription started on {{subscriptionData.startedOn}} </p>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<tr>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-top: 32px; padding-bottom: 12px;">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Tier</p>
<hr style="border-bottom: 1px solid #F4F4F5; margin-top: 4px; margin-bottom: 8px;" />
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600; padding-bottom: 32px;">{{tierData.name}}
{{#if tierData.details}} <span style="font-weight: normal; color:#3A464C;"> - {{tierData.details}}</span>{{/if}}
</p>
{{#if offerData}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Offer</p>
<hr style="border-bottom: 1px solid #F4F4F5; margin-top: 4px; margin-bottom: 8px;" />
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600; padding-bottom: 32px;">{{offerData.name}} <span style="font-weight: normal; color:#3A464C;"> - {{offerData.details}}</span></p>
{{/if}}
{{#if referrerSource}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">Signup info</p>
<hr style="border-bottom: 1px solid #F4F4F5; margin-top: 4px; margin-bottom: 8px;" />
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600; padding-bottom: 4px;">Source
<span style="font-weight: normal; color:#3A464C;"> - {{referrerSource}}</span>
</p>
{{#if attributionTitle}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">Page
<span style="font-weight: normal; color:#3A464C;"> - <a href="{{attributionUrl}}" style="font-weight: normal; color:#3A464C;text-decoration:none">{{attributionTitle}}</a></span>
</p>
{{/if}}
{{/if}}
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">You have a new paid subscriber</h1>
<table width="100" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
<tbody>
<tr>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-top: 32px; padding-bottom: 12px;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<td align="left" style="padding: 24px;">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Tier:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{tierData.name}}{{#if tierData.details}} &bull; {{tierData.details}}{{/if}}</p>
{{#if offerData}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Offer:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{offerData.name}} &bull; <span style="color: {{accentColor}};">{{offerData.details}}</span></p>
{{/if}}
{{#if referrerSource}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
{{#if attributionTitle}}
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
{{/if}}
{{/if}}
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
<tbody>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View member</a> </td>
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; vertical-align: top; background-color: {{accentColor}}; border-radius: 8px; text-align: center;"> <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 8px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 15px; font-weight: normal; margin: 0; padding: 10px 20px; border-color: {{accentColor}};">View member</a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
@ -104,21 +79,29 @@
</tr>
</tbody>
</table>
<hr/>
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
<p class="text-link" style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{memberData.adminUrl}}</p>
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td align="left" style="padding-top: 24px;">
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #7c8b9a; font-weight: normal; margin: 0; line-height: 25px;">Or copy and paste this URL into your browser:</p>
<p class="text-link" style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin:0; color: #7c8b9a; font-weight: normal;">{{memberData.adminUrl}}</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!-- START FOOTER -->
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 80px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{toEmail}}</a></p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top; padding-top: 56px;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{toEmail}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{toEmail}}</a></p>
</td>
</tr>
<tr>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 2px">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">here</a>.</p>
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top;">
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">Dont want to receive these emails? Manage your preferences <a class="small" href="{{staffUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">here</a>.</p>
</td>
</tr>

View File

@ -78,10 +78,6 @@ function testCommonPaidSubMailData({member, mailStub, getEmailAlertUsersStub}) {
mailStub.calledWith(
sinon.match.has('html', sinon.match('$50.00/month'))
).should.be.true();
mailStub.calledWith(
sinon.match.has('html', sinon.match('Subscription started on 1 Aug 2022'))
).should.be.true();
}
function testCommonPaidSubCancelMailData({mailStub, getEmailAlertUsersStub}) {
@ -149,6 +145,12 @@ describe('StaffService', function () {
}
};
const blogIcon = {
getIconUrl: () => {
return 'https://ghost.example/siteicon.png';
}
};
const settingsHelpers = {
getDefaultEmailDomain: () => {
return 'ghost.example';
@ -184,6 +186,7 @@ describe('StaffService', function () {
},
settingsCache,
urlUtils,
blogIcon,
settingsHelpers,
labs
});
@ -220,6 +223,7 @@ describe('StaffService', function () {
DomainEvents,
settingsCache,
urlUtils,
blogIcon,
settingsHelpers
});
service.subscribeEvents();
@ -339,6 +343,7 @@ describe('StaffService', function () {
},
settingsCache,
urlUtils,
blogIcon,
settingsHelpers,
labs: {
isSet: () => {
@ -430,9 +435,6 @@ describe('StaffService', function () {
mailStub.calledWith(
sinon.match.has('html', sinon.match('🥳 Free member signup: Ghost'))
).should.be.true();
mailStub.calledWith(
sinon.match.has('html', sinon.match('Created on 1 Aug 2022 &#8226; France'))
).should.be.true();
});
it('sends free member signup alert without member name', async function () {
@ -455,18 +457,13 @@ describe('StaffService', function () {
mailStub.calledWith(
sinon.match.has('html', sinon.match('🥳 Free member signup: member@example.com'))
).should.be.true();
mailStub.calledWith(
sinon.match.has('html', sinon.match('Created on 1 Aug 2022 &#8226; France'))
).should.be.true();
});
it('sends free member signup alert with attribution', async function () {
const member = {
name: 'Ghost',
email: 'member@example.com',
id: 'abc',
geolocation: '{"country": "France"}',
created_at: '2022-08-01T07:30:39.882Z'
id: 'abc'
};
const attribution = {
@ -487,9 +484,6 @@ describe('StaffService', function () {
mailStub.calledWith(
sinon.match.has('html', sinon.match('🥳 Free member signup: Ghost'))
).should.be.true();
mailStub.calledWith(
sinon.match.has('html', sinon.match('Created on 1 Aug 2022 &#8226; France'))
).should.be.true();
mailStub.calledWith(
sinon.match.has('html', sinon.match('Source'))
@ -520,9 +514,7 @@ describe('StaffService', function () {
member = {
name: 'Ghost',
email: 'member@example.com',
id: 'abc',
geolocation: '{"country": "France"}',
created_at: '2022-08-01T07:30:39.882Z'
id: 'abc'
};
offer = {
name: 'Half price',
@ -588,9 +580,7 @@ describe('StaffService', function () {
it('sends paid subscription start alert without member name', async function () {
let memberData = {
email: 'member@example.com',
id: 'abc',
geolocation: '{"country": "France"}',
created_at: '2022-08-01T07:30:39.882Z'
id: 'abc'
};
await service.emails.notifyPaidSubscriptionStarted({member: memberData, offer: null, tier, subscription}, options);

View File

@ -25,7 +25,7 @@
"@types/sinon": "10.0.16",
"c8": "8.0.1",
"knex": "2.4.2",
"luxon": "3.4.4",
"luxon": "3.5.0",
"mocha": "10.2.0",
"should": "13.2.3",
"sinon": "15.2.0",

View File

@ -25,10 +25,10 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/debug": "0.1.32",
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/member-events": "0.0.0",
"leaky-bucket": "2.2.0",
"lodash": "4.17.21",

View File

@ -22,9 +22,9 @@
"mocha": "10.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/string": "0.2.12",
"@tryghost/tpl": "0.1.30",
"@tryghost/tpl": "0.1.32",
"bson-objectid": "2.0.4"
}
}

View File

@ -25,10 +25,10 @@
"uuid": "9.0.1"
},
"dependencies": {
"@tryghost/debug": "0.1.30",
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/tpl": "0.1.30",
"@tryghost/debug": "0.1.32",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"@tryghost/tpl": "0.1.32",
"lodash": "4.17.21",
"moment": "2.24.0"
}

View File

@ -25,7 +25,7 @@
},
"dependencies": {
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "1.3.2",
"@tryghost/errors": "1.3.5",
"@tryghost/member-events": "0.0.0"
}
}

View File

@ -25,8 +25,8 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"cheerio": "0.22.0"
}
}

View File

@ -43,8 +43,8 @@
"prepare": "husky install .github/hooks"
},
"resolutions": {
"@tryghost/errors": "1.3.2",
"@tryghost/logging": "2.4.15",
"@tryghost/errors": "1.3.5",
"@tryghost/logging": "2.4.18",
"jackspeak": "2.1.1",
"moment": "2.24.0",
"moment-timezone": "0.5.45"
@ -115,9 +115,9 @@
"eslint-plugin-ghost": "3.4.0",
"eslint-plugin-react": "7.33.0",
"husky": "8.0.3",
"lint-staged": "15.2.7",
"lint-staged": "15.2.8",
"nx": "16.8.1",
"rimraf": "5.0.9",
"rimraf": "5.0.10",
"ts-node": "10.9.2",
"typescript": "5.4.5",
"inquirer": "8.2.6"

1989
yarn.lock

File diff suppressed because it is too large Load Diff