Skip to content

Commit

Permalink
Moved HTML card web visibility settings to beta
Browse files Browse the repository at this point in the history
closes https://linear.app/ghost/issue/PLG-339

- swapped `contentVisibilityAlpha` flag conditionals to `contentVisibility` where appropriate
- deleted code left in place to support previous beta behaviour
- added `ignoreCardSettings` option to our `assertHTML` test util and updated paste behaviour test so it's not dependent on changes that have no impact on what's being tested
  • Loading branch information
kevinansfield committed Feb 4, 2025
1 parent 939f01d commit 1f835cc
Show file tree
Hide file tree
Showing 11 changed files with 34 additions and 372 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const KoenigCardWrapper = ({nodeKey, width, wrapperStyle, IndicatorIcon, childre
}, [editor, isSelected, isEditing, nodeKey, containerRef]);

let isVisibilityActive = false;
if (cardConfig?.feature?.contentVisibilityAlpha) {
if (cardConfig?.feature?.contentVisibility) {
editor.getEditorState().read(() => {
const cardNode = $getNodeByKey(nodeKey);
isVisibilityActive = cardNode?.getIsVisibilityActive?.();
Expand Down
2 changes: 1 addition & 1 deletion packages/koenig-lexical/src/components/ui/CardWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const CardWrapper = React.forwardRef(({
};

let indicatorIcon;
if (feature?.contentVisibilityAlpha && isVisibilityActive) {
if (isVisibilityActive) {
indicatorIcon = (
<div className="sticky top-0 lg:top-8">
<VisibilityIndicator
Expand Down
91 changes: 2 additions & 89 deletions packages/koenig-lexical/src/components/ui/VisibilitySettings.jsx
Original file line number Diff line number Diff line change
@@ -1,93 +1,6 @@
import {DropdownSetting, ToggleSetting} from './SettingsPanel';
import {ToggleSetting} from './SettingsPanel';

// original beta visibility settings updated to use alpha visibility format
export function VisibilitySettings({isStripeEnabled, updateVisibility, visibilityData}) {
const webIsChecked = visibilityData.web.nonMembers && visibilityData.web.freeMembers && visibilityData.web.paidMembers;

function toggleWeb() {
const value = !webIsChecked;
const newVisibilityData = structuredClone(visibilityData);
newVisibilityData.web.nonMembers = value;
newVisibilityData.web.freeMembers = value;
newVisibilityData.web.paidMembers = value;
updateVisibility(newVisibilityData);
}

const emailIsChecked = visibilityData.email.freeMembers || visibilityData.email.paidMembers;

function toggleEmail() {
const value = !emailIsChecked;
const newVisibilityData = structuredClone(visibilityData);
newVisibilityData.email.freeMembers = value;
newVisibilityData.email.paidMembers = value;
updateVisibility(newVisibilityData);
}

let emailSegment = '';
if (visibilityData.email.freeMembers && visibilityData.email.paidMembers) {
emailSegment = 'status:free,status:-free';
} else if (visibilityData.email.freeMembers && !visibilityData.email.paidMembers) {
emailSegment = 'status:free';
} else if (!visibilityData.email.freeMembers && visibilityData.email.paidMembers) {
emailSegment = 'status:-free';
}

const dropdownOptions = [{
label: 'All members',
name: 'status:free,status:-free'
}, {
label: 'Free members',
name: 'status:free'
}, {
label: 'Paid members',
name: 'status:-free'
}];

function toggleEmailSegment(segment) {
const newVisibilityData = structuredClone(visibilityData);

if (segment === 'status:free,status:-free') {
newVisibilityData.email.freeMembers = true;
newVisibilityData.email.paidMembers = true;
} else if (segment === 'status:free') {
newVisibilityData.email.freeMembers = true;
newVisibilityData.email.paidMembers = false;
} else if (segment === 'status:-free') {
newVisibilityData.email.freeMembers = false;
newVisibilityData.email.paidMembers = true;
}

updateVisibility(newVisibilityData);
}

return (
<>
<ToggleSetting
dataTestId="visibility-show-on-web"
isChecked={webIsChecked}
label="Show on web"
onChange={toggleWeb}
/>
<ToggleSetting
dataTestId="visibility-show-on-email"
isChecked={emailIsChecked}
label="Show in email newsletter"
onChange={toggleEmail}
/>
{emailIsChecked && isStripeEnabled && (
<DropdownSetting
dataTestId="visibility-dropdown-segment"
label="Email audience"
menu={dropdownOptions}
value={emailSegment}
onChange={segment => toggleEmailSegment(segment)}
/>
)}
</>
);
}

export function VisibilitySettingsAlpha({visibilityOptions, toggleVisibility}) {
export function VisibilitySettings({visibilityOptions, toggleVisibility}) {
const settingGroups = visibilityOptions.map((group, index) => {
const toggles = group.toggles.map((toggle) => {
return (
Expand Down
16 changes: 2 additions & 14 deletions packages/koenig-lexical/src/components/ui/cards/HtmlCard.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import '@tryghost/kg-simplemde/dist/simplemde.min.css';
import HtmlEditor from './HtmlCard/HtmlEditor';
import KoenigComposerContext from '../../../context/KoenigComposerContext.jsx';
import PropTypes from 'prop-types';
import React from 'react';
import {CardVisibilityMessage} from '../CardVisibilityMessage.jsx';
import {sanitizeHtml} from '../../../utils/sanitize-html';

export function HtmlCard({html, updateHtml, isEditing, darkMode, visibilityMessage}) {
const {cardConfig} = React.useContext(KoenigComposerContext);
const {feature = {}} = cardConfig;
const {contentVisibility, contentVisibilityAlpha} = feature;

const displayVisibilityMessage = contentVisibility && !contentVisibilityAlpha;

export function HtmlCard({html, updateHtml, isEditing, darkMode}) {
return (
<>
{isEditing
? (
<>
{displayVisibilityMessage && <CardVisibilityMessage message={visibilityMessage} />}
<HtmlEditor
darkMode={darkMode}
html={html}
Expand All @@ -27,7 +18,6 @@ export function HtmlCard({html, updateHtml, isEditing, darkMode, visibilityMessa
</>
)
: <div>
{displayVisibilityMessage && <CardVisibilityMessage message={visibilityMessage} />}
<HtmlDisplay html={html} />
<div className="absolute inset-0 z-50 mt-0"></div>
</div>
Expand All @@ -50,7 +40,5 @@ HtmlCard.propTypes = {
html: PropTypes.string,
updateHtml: PropTypes.func,
isEditing: PropTypes.bool,
darkMode: PropTypes.bool,
contentVisibility: PropTypes.element,
visibilityMessage: PropTypes.string
darkMode: PropTypes.bool
};
19 changes: 1 addition & 18 deletions packages/koenig-lexical/src/hooks/useVisibilityToggle.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,22 @@
import {$getNodeByKey} from 'lexical';
import {generateVisibilityMessage, getVisibilityOptions, parseVisibilityToToggles, serializeOptionsToVisibility, serializeTogglesToVisibility} from '../utils/visibility';
import {getVisibilityOptions, parseVisibilityToToggles, serializeOptionsToVisibility} from '../utils/visibility';

export const useVisibilityToggle = (editor, nodeKey, cardConfig) => {
const isStripeEnabled = cardConfig?.stripeEnabled;
const isContentVisibilityAlphaEnabled = cardConfig?.feature?.contentVisibilityAlpha || false;

let currentVisibility;
let isVisibilityActive = false;

editor.getEditorState().read(() => {
const htmlNode = $getNodeByKey(nodeKey);
currentVisibility = htmlNode.visibility;
isVisibilityActive = htmlNode.getIsVisibilityActive();
});

const visibilityData = parseVisibilityToToggles(currentVisibility);
const visibilityOptions = getVisibilityOptions(currentVisibility, {isStripeEnabled});

let visibilityMessage = '';
if (isVisibilityActive && !isContentVisibilityAlphaEnabled) {
visibilityMessage = generateVisibilityMessage(currentVisibility);
}

return {
visibilityData,
visibilityOptions,
visibilityMessage,
// used with contentVisibility
updateVisibility: (newVisibilityData) => {
editor.update(() => {
const node = $getNodeByKey(nodeKey);
node.visibility = serializeTogglesToVisibility(newVisibilityData);
});
},
// used with contentVisibilityAlpha
toggleVisibility: (type, key, value) => {
editor.update(() => {
const newVisibilityOptions = structuredClone(visibilityOptions);
Expand Down
16 changes: 4 additions & 12 deletions packages/koenig-lexical/src/nodes/HtmlNodeComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {HtmlCard} from '../components/ui/cards/HtmlCard';
import {SettingsPanel} from '../components/ui/SettingsPanel.jsx';
import {SnippetActionToolbar} from '../components/ui/SnippetActionToolbar.jsx';
import {ToolbarMenu, ToolbarMenuItem, ToolbarMenuSeparator} from '../components/ui/ToolbarMenu.jsx';
import {VisibilitySettings, VisibilitySettingsAlpha} from '../components/ui/VisibilitySettings.jsx';
import {VisibilitySettings} from '../components/ui/VisibilitySettings.jsx';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {useVisibilityToggle} from '../hooks/useVisibilityToggle.js';

Expand All @@ -19,20 +19,14 @@ export function HtmlNodeComponent({nodeKey, html}) {
const [showSnippetToolbar, setShowSnippetToolbar] = React.useState(false);

const isContentVisibilityEnabled = cardConfig?.feature?.contentVisibility || false;
const isContentVisibilityAlphaEnabled = cardConfig?.feature?.contentVisibilityAlpha || false;

const {visibilityData, visibilityOptions, visibilityMessage, toggleVisibility, updateVisibility} = useVisibilityToggle(editor, nodeKey, cardConfig);
const {visibilityOptions, toggleVisibility} = useVisibilityToggle(editor, nodeKey, cardConfig);

const settingsTabs = [
{id: 'visibility', label: 'Visibility'}
];

let visibilitySettings;
if (isContentVisibilityAlphaEnabled) {
visibilitySettings = <VisibilitySettingsAlpha toggleVisibility={toggleVisibility} visibilityOptions={visibilityOptions} />;
} else {
visibilitySettings = <VisibilitySettings isStripeEnabled={cardConfig?.stripeEnabled} updateVisibility={updateVisibility} visibilityData={visibilityData} />;
}
const visibilitySettings = <VisibilitySettings toggleVisibility={toggleVisibility} visibilityOptions={visibilityOptions} />;

const settingsTabContents = {
visibility: visibilitySettings
Expand All @@ -51,6 +45,7 @@ export function HtmlNodeComponent({nodeKey, html}) {
editor.dispatchCommand(EDIT_CARD_COMMAND, {cardKey: nodeKey, focusEditor: false});
};

// TODO: this isn't used? <HtmlCard> does not have a prop for `onBlur`
const onBlur = (event) => {
if (event?.relatedTarget?.className !== 'kg-prose') {
editor.dispatchCommand(DESELECT_CARD_COMMAND, {cardKey: nodeKey});
Expand All @@ -63,10 +58,7 @@ export function HtmlNodeComponent({nodeKey, html}) {
darkMode={darkMode}
html={html}
isEditing={cardContext.isEditing}
nodeKey={nodeKey}
unsplashConf={cardConfig.unsplash}
updateHtml={updateHtml}
visibilityMessage={visibilityMessage}
onBlur={onBlur}
/>

Expand Down
29 changes: 0 additions & 29 deletions packages/koenig-lexical/src/utils/visibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,32 +105,3 @@ export function serializeOptionsToVisibility(options) {
}
};
}

export function generateVisibilityMessage(visibility) {
const toggles = parseVisibilityToToggles(visibility);
const showOnWeb = toggles.web.nonMembers || toggles.web.freeMembers || toggles.web.paidMembers;
const showOnEmail = toggles.email.freeMembers || toggles.email.paidMembers;

let hiddenNewsletter;
if (!toggles.email.paidMembers && toggles.email.freeMembers) {
hiddenNewsletter = 'paid newsletter';
} else if (toggles.email.paidMembers && !toggles.email.freeMembers) {
hiddenNewsletter = 'free newsletter';
}

let message = '';

if (!showOnWeb && !showOnEmail) {
message = 'Hidden on website and newsletter';
} else if (showOnWeb && !showOnEmail) {
message = 'Hidden in newsletter';
} else if (showOnWeb && showOnEmail && hiddenNewsletter) {
message = `Hidden in ${hiddenNewsletter}`;
} else if (!showOnWeb && showOnEmail && !hiddenNewsletter) {
message = 'Hidden on website';
} else if (!showOnWeb && showOnEmail && hiddenNewsletter) {
message = `Hidden on website and ${hiddenNewsletter}`;
}

return message;
}
Loading

0 comments on commit 1f835cc

Please sign in to comment.