feat(creator-studio): pay toggle and entry in the publish settings card
The card below the writing surface only offered cover and scheduling, so paid content could only be reached through the bottom bar's 发布 button. - The card gains a 付费内容 row (rendered only with canPay): a toggle bound to draft.pay.enabled, a status line that reflects whether resources were added, and a 配置价格与付费资源 link. - Turning the toggle on opens the publish drawer and scrolls it to the pay section, so pricing is the next thing the author sees. Entering from the bottom bar keeps the drawer at the top.
This commit is contained in:
1 parent
0e4e4670dd
commit
ac84e991d1
8 files changed
+126
-14
No files matched your search
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* 发布设置卡片(位于正文纸张下方)。
|
||||
*
|
||||
* 只承载与"这篇会被怎样发布"有关的低频选项:封面与定时发布。
|
||||
* 分类、标签、付费等配置仍在发布抽屉里;服务端 bootstrap 决定每项是否可用。
|
||||
* 承载与"这篇会被怎样发布"有关的低频选项:封面、付费资源与定时发布。
|
||||
* 分类、标签等配置仍在发布抽屉里;服务端 bootstrap 决定每项是否可用。
|
||||
*
|
||||
* @package Jio
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ import { useMemo, useRef, useState } from '@wordpress/element';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
import { apiUpload } from '../services/api';
|
||||
import type { PayState } from '../draft';
|
||||
|
||||
type Featured = { type: string; data: Record<string, any>; mediaId?: number };
|
||||
|
||||
@@ -20,8 +21,13 @@ type Props = {
|
||||
canSchedule: boolean;
|
||||
scheduleEnabled: boolean;
|
||||
scheduleAt: string;
|
||||
pay: PayState;
|
||||
canPay: boolean;
|
||||
onFeaturedChange: (featured: Featured) => void;
|
||||
onScheduleChange: (enabled: boolean, at: string) => void;
|
||||
onPayEnabledChange: (enabled: boolean) => void;
|
||||
/** 打开发布抽屉并直接定位到付费配置。 */
|
||||
onOpenPaySettings: () => void;
|
||||
};
|
||||
|
||||
const bootstrap: Record<string, any> = window['jioCreatorStudio'] || {};
|
||||
@@ -44,8 +50,12 @@ export function PublishSettings({
|
||||
canSchedule,
|
||||
scheduleEnabled,
|
||||
scheduleAt,
|
||||
pay,
|
||||
canPay,
|
||||
onFeaturedChange,
|
||||
onScheduleChange,
|
||||
onPayEnabledChange,
|
||||
onOpenPaySettings,
|
||||
}: Props) {
|
||||
const coverInput = useRef<HTMLInputElement | null>(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
@@ -133,6 +143,47 @@ export function PublishSettings({
|
||||
|
||||
{error && <p className="jio-creator-error">{error}</p>}
|
||||
|
||||
{/* 付费入口:开关放在卡片里,定价与付费资源仍在抽屉中配置。 */}
|
||||
{canPay && (
|
||||
<div className="jio-creator__setting-row">
|
||||
<div className="jio-creator__setting-copy">
|
||||
<span className="jio-creator__setting-label">{__('付费内容', 'jio')}</span>
|
||||
<p className="jio-creator__setting-help">
|
||||
{!pay.enabled
|
||||
? __('开启后可以设置价格、会员价并添加付费下载资源。', 'jio')
|
||||
: pay.downloads.length > 0
|
||||
? sprintf(
|
||||
/* translators: %d: number of paid download resources */
|
||||
__('已开启,含 %d 个付费下载资源。', 'jio'),
|
||||
pay.downloads.length
|
||||
)
|
||||
: __('已开启,未购买读者只能看到免费部分。', 'jio')}
|
||||
</p>
|
||||
{pay.enabled && (
|
||||
<button type="button" className="jio-creator__setting-link" onClick={onOpenPaySettings}>
|
||||
<i className="fa fas fa-coins" aria-hidden="true" />
|
||||
{__('配置价格与付费资源', 'jio')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<label className="jio-creator-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={pay.enabled}
|
||||
onChange={(event) => {
|
||||
const enabled = event.target.checked;
|
||||
onPayEnabledChange(enabled);
|
||||
// 开启后直接把作者带到定价 / 资源入口,省一次点击。
|
||||
if (enabled) {
|
||||
onOpenPaySettings();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span aria-hidden="true" />
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="jio-creator__setting-row">
|
||||
<div className="jio-creator__setting-copy">
|
||||
<span className="jio-creator__setting-label">{__('定时发布', 'jio')}</span>
|
||||
|
||||
@@ -90,6 +90,7 @@ function Studio(): JSX.Element {
|
||||
at: '',
|
||||
}));
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [drawerFocusPay, setDrawerFocusPay] = useState(false);
|
||||
const [blockDialog, setBlockDialog] = useState<BlockDialogKind | null>(null);
|
||||
const [notice, setNotice] = useState<{ tone: 'ok' | 'error'; text: string } | null>(null);
|
||||
const [recovery, setRecovery] = useState<null | { savedAt: string }>(null);
|
||||
@@ -765,8 +766,15 @@ function Studio(): JSX.Element {
|
||||
canSchedule={!!capabilities.canSchedule}
|
||||
scheduleEnabled={!!draft.schedule.enabled}
|
||||
scheduleAt={draft.schedule.at}
|
||||
pay={draft.pay}
|
||||
canPay={!!capabilities.canPay}
|
||||
onFeaturedChange={(featured) => patchDraft({ featured })}
|
||||
onScheduleChange={(enabled, at) => patchDraft({ schedule: { enabled, at } })}
|
||||
onPayEnabledChange={(enabled) => patchDraft({ pay: { ...draftRef.current.pay, enabled } })}
|
||||
onOpenPaySettings={() => {
|
||||
setDrawerFocusPay(true);
|
||||
setDrawerOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{blockDialog && (
|
||||
@@ -784,7 +792,10 @@ function Studio(): JSX.Element {
|
||||
saving={!editorReady || saveState.state === 'saving' || saveState.state === 'submitting'}
|
||||
onSaveDraft={() => void save(false)}
|
||||
onPreview={() => void preview()}
|
||||
onPublish={() => setDrawerOpen(true)}
|
||||
onPublish={() => {
|
||||
setDrawerFocusPay(false);
|
||||
setDrawerOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{drawerOpen && (
|
||||
@@ -799,7 +810,11 @@ function Studio(): JSX.Element {
|
||||
customDefs: bootstrap.customDefs || [],
|
||||
}}
|
||||
isPublishedEdit={isPublishedEdit}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
focusPay={drawerFocusPay}
|
||||
onClose={() => {
|
||||
setDrawerOpen(false);
|
||||
setDrawerFocusPay(false);
|
||||
}}
|
||||
onSaveDraft={() => void save(false)}
|
||||
onSubmit={(publishNow: boolean) => void submit(publishNow)}
|
||||
privateStorage={!!bootstrap.privateStorage}
|
||||
|
||||
@@ -38,6 +38,8 @@ type Props = {
|
||||
customDefs: Array<{ key: string; name: string; values: Array<{ key: string; name: string }> }>;
|
||||
};
|
||||
isPublishedEdit: boolean;
|
||||
/** 打开时直接定位到付费配置(从正文下方的发布设置卡片进入)。 */
|
||||
focusPay?: boolean;
|
||||
onClose: () => void;
|
||||
onSaveDraft: () => void;
|
||||
onSubmit: (publishNow: boolean) => void;
|
||||
@@ -57,6 +59,7 @@ export function PublishDrawer(props: Props) {
|
||||
limits,
|
||||
taxonomy,
|
||||
isPublishedEdit,
|
||||
focusPay,
|
||||
onClose,
|
||||
onSaveDraft,
|
||||
onSubmit,
|
||||
@@ -70,6 +73,7 @@ export function PublishDrawer(props: Props) {
|
||||
const [uploadError, setUploadError] = useState('');
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const captchaSlot = useRef<HTMLDivElement | null>(null);
|
||||
const paySection = useRef<HTMLElement | null>(null);
|
||||
|
||||
const payBootstrap: PayBootstrap = {
|
||||
freeAllowed: !!bootstrap.pay?.freeAllowed,
|
||||
@@ -106,6 +110,13 @@ export function PublishDrawer(props: Props) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 从发布设置卡片进入时,抽屉打开后直接滚到付费配置。
|
||||
useEffect(() => {
|
||||
if (focusPay) {
|
||||
paySection.current?.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
||||
}
|
||||
}, [focusPay]);
|
||||
|
||||
const titleLength = useMemo(() => (draft.title || '').trim().length, [draft.title]);
|
||||
const canSubmit = titleLength >= Number(limits.titleMin || 1);
|
||||
|
||||
@@ -282,7 +293,7 @@ export function PublishDrawer(props: Props) {
|
||||
))}
|
||||
|
||||
{capabilities.canPay && (
|
||||
<section className="jio-creator-field">
|
||||
<section className="jio-creator-field" ref={paySection}>
|
||||
<PayPanel
|
||||
pay={draft.pay}
|
||||
onChange={(pay) => setDraft({ pay })}
|
||||
|
||||
@@ -659,6 +659,31 @@ body.jio-page-submit.has-static-nav {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 发布设置卡片里的「配置价格与付费资源」入口。 */
|
||||
.jio-creator__setting-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--jc-accent-strong);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: var(--jc-accent);
|
||||
text-decoration: underline;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jio-creator__cover-control {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
Reference in new issue
Block a user