AI editing now requires Business tier, remove lifetime updates
This commit is contained in:
@ -194,9 +194,9 @@ export default function AIPanel({ onReprocess, whisperModel, setWhisperModel }:
|
|||||||
{!canUseAI ? (
|
{!canUseAI ? (
|
||||||
<div className="text-center py-8 px-4">
|
<div className="text-center py-8 px-4">
|
||||||
<Lock className="w-8 h-8 text-editor-text-muted mx-auto mb-3" />
|
<Lock className="w-8 h-8 text-editor-text-muted mx-auto mb-3" />
|
||||||
<p className="text-sm font-medium mb-1">AI editing requires a license</p>
|
<p className="text-sm font-medium mb-1">AI editing requires Business</p>
|
||||||
<p className="text-xs text-editor-text-muted mb-4">
|
<p className="text-xs text-editor-text-muted mb-4">
|
||||||
Upgrade to Pro or Business to unlock filler word removal, clip suggestions, and more.
|
Upgrade to Business to unlock filler word removal, clip suggestions, and more.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowLicenseDialog(true)}
|
onClick={() => setShowLicenseDialog(true)}
|
||||||
@ -305,9 +305,9 @@ export default function AIPanel({ onReprocess, whisperModel, setWhisperModel }:
|
|||||||
{!canUseAI ? (
|
{!canUseAI ? (
|
||||||
<div className="text-center py-8 px-4">
|
<div className="text-center py-8 px-4">
|
||||||
<Lock className="w-8 h-8 text-editor-text-muted mx-auto mb-3" />
|
<Lock className="w-8 h-8 text-editor-text-muted mx-auto mb-3" />
|
||||||
<p className="text-sm font-medium mb-1">AI clip suggestions require a license</p>
|
<p className="text-sm font-medium mb-1">AI clip suggestions require Business</p>
|
||||||
<p className="text-xs text-editor-text-muted mb-4">
|
<p className="text-xs text-editor-text-muted mb-4">
|
||||||
Upgrade to Pro or Business to find the best segments for social media clips.
|
Upgrade to Business to find the best segments for social media clips.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowLicenseDialog(true)}
|
onClick={() => setShowLicenseDialog(true)}
|
||||||
|
|||||||
@ -53,6 +53,12 @@ describe('licenseStore', () => {
|
|||||||
test('is true for Licensed status', () => {
|
test('is true for Licensed status', () => {
|
||||||
useLicenseStore.getState().setStatus({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 } });
|
useLicenseStore.getState().setStatus({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 } });
|
||||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||||
|
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('is true for Licensed Business status', () => {
|
||||||
|
useLicenseStore.getState().setStatus({ tag: 'Licensed', license: { license_id: 'x', tier: 'business', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 5 } });
|
||||||
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||||
expect(useLicenseStore.getState().canUseAI).toBe(true);
|
expect(useLicenseStore.getState().canUseAI).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -119,7 +125,7 @@ describe('licenseStore', () => {
|
|||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
expect(useLicenseStore.getState().status?.tag).toBe('Licensed');
|
expect(useLicenseStore.getState().status?.tag).toBe('Licensed');
|
||||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||||
expect(useLicenseStore.getState().canUseAI).toBe(true);
|
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns false on invalid key', async () => {
|
test('returns false on invalid key', async () => {
|
||||||
@ -168,7 +174,7 @@ describe('licenseStore', () => {
|
|||||||
|
|
||||||
test('handles API error', async () => {
|
test('handles API error', async () => {
|
||||||
mockElectronAPI({ deactivateLicense: vi.fn().mockRejectedValue(new Error('fail')) });
|
mockElectronAPI({ deactivateLicense: vi.fn().mockRejectedValue(new Error('fail')) });
|
||||||
useLicenseStore.setState({ status: { tag: 'Licensed', license: {} as any }, canEdit: true, canUseAI: true });
|
useLicenseStore.setState({ status: { tag: 'Licensed', license: { license_id: 'x', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 } }, canEdit: true, canUseAI: false });
|
||||||
await useLicenseStore.getState().deactivateLicense();
|
await useLicenseStore.getState().deactivateLicense();
|
||||||
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
|||||||
|
|
||||||
setStatus: (status) => {
|
setStatus: (status) => {
|
||||||
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
||||||
const canUseAI = status?.tag === 'Licensed';
|
const canUseAI = status?.tag === 'Licensed' && status.license.tier === 'business';
|
||||||
set({ status, isLoaded: true, canEdit, canUseAI });
|
set({ status, isLoaded: true, canEdit, canUseAI });
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
|||||||
try {
|
try {
|
||||||
const status = await window.electronAPI?.getAppStatus();
|
const status = await window.electronAPI?.getAppStatus();
|
||||||
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
||||||
const canUseAI = status?.tag === 'Licensed';
|
const canUseAI = status?.tag === 'Licensed' && status.license.tier === 'business';
|
||||||
set({ status: status || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
set({ status: status || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
||||||
} catch {
|
} catch {
|
||||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
||||||
@ -69,7 +69,7 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
|||||||
try {
|
try {
|
||||||
const license = await window.electronAPI?.activateLicense(key);
|
const license = await window.electronAPI?.activateLicense(key);
|
||||||
if (!license) return false;
|
if (!license) return false;
|
||||||
set({ status: { tag: 'Licensed', license }, showDialog: false, canEdit: true, canUseAI: true });
|
set({ status: { tag: 'Licensed', license }, showDialog: false, canEdit: true, canUseAI: license.tier === 'business' });
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
@ -81,7 +81,7 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
|||||||
await window.electronAPI?.deactivateLicense();
|
await window.electronAPI?.deactivateLicense();
|
||||||
const s = await window.electronAPI?.getAppStatus();
|
const s = await window.electronAPI?.getAppStatus();
|
||||||
const canEdit = s?.tag === 'Licensed' || s?.tag === 'Trial';
|
const canEdit = s?.tag === 'Licensed' || s?.tag === 'Trial';
|
||||||
const canUseAI = s?.tag === 'Licensed';
|
const canUseAI = s?.tag === 'Licensed' && s.license.tier === 'business';
|
||||||
set({ status: s || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
set({ status: s || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
||||||
} catch {
|
} catch {
|
||||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
||||||
|
|||||||
Reference in New Issue
Block a user