Better trial activation flows

This commit is contained in:
Gregory Schier
2025-02-25 22:16:55 -08:00
parent 80de232bec
commit eb8153f409
6 changed files with 26 additions and 22 deletions

View File

@@ -26,6 +26,7 @@ export function useLicense() {
const CHECK_QUERY_KEY = ['license.check'];
const check = useQuery<void, string, LicenseCheckStatus>({
refetchInterval: 1000 * 60 * 60 * 12, // Refetch every 12 hours
queryKey: CHECK_QUERY_KEY,
queryFn: () => invoke('plugin:yaak-license|check'),
});

View File

@@ -65,7 +65,7 @@ export function LicenseBadge() {
if (check.data.type === 'trialing') {
await setLicenseDetails((v) => ({
...v,
dismissedTrial: true,
hasDismissedTrial: true,
}));
}
openSettings.mutate(SettingsTab.License);

View File

@@ -1,9 +1,10 @@
import { openUrl } from '@tauri-apps/plugin-opener';
import { useLicense } from '@yaakapp-internal/license';
import { formatDistanceToNowStrict } from 'date-fns';
import { differenceInDays } from 'date-fns';
import React, { useState } from 'react';
import { useLicenseConfirmation } from '../../hooks/useLicenseConfirmation';
import { useToggle } from '../../hooks/useToggle';
import { pluralizeCount } from '../../lib/pluralize';
import { Banner } from '../core/Banner';
import { Button } from '../core/Button';
import { Checkbox } from '../core/Checkbox';
@@ -32,8 +33,12 @@ export function SettingsLicense() {
) : check.data?.type == 'trialing' ? (
<Banner color="success" className="flex flex-col gap-3 max-w-lg">
<p className="select-text">
<strong>{formatDistanceToNowStrict(check.data.end)} days remaining</strong> on your
commercial use trial
You have{' '}
<strong>
{pluralizeCount('day', differenceInDays(check.data.end, new Date()))} remaining
</strong>{' '}
on your commercial use trial. Once the trial ends, Yaak will be limited to personal use
until a license is activated.
</p>
</Banner>
) : check.data?.type == 'personal_use' && !licenseDetails?.confirmedPersonalUse ? (
@@ -76,12 +81,7 @@ export function SettingsLicense() {
{check.data?.type === 'commercial_use' ? (
<HStack space={2}>
<Button
variant="border"
color="secondary"
size="sm"
onClick={toggleActivateFormVisible}
>
<Button variant="border" color="secondary" size="sm" onClick={toggleActivateFormVisible}>
Activate Another License
</Button>
<Button
@@ -95,11 +95,7 @@ export function SettingsLicense() {
</HStack>
) : (
<HStack space={2}>
<Button
color="primary"
size="sm"
onClick={toggleActivateFormVisible}
>
<Button color="primary" size="sm" onClick={toggleActivateFormVisible}>
Activate
</Button>
<Button
@@ -131,12 +127,7 @@ export function SettingsLicense() {
onChange={setKey}
placeholder="YK1-XXXXX-XXXXX-XXXXX-XXXXX"
/>
<Button
type="submit"
color="primary"
size="sm"
isLoading={activate.isPending}
>
<Button type="submit" color="primary" size="sm" isLoading={activate.isPending}>
Submit
</Button>
</VStack>

View File

@@ -1,4 +1,5 @@
import { openUrl } from '@tauri-apps/plugin-opener';
import { useLicense } from '@yaakapp-internal/license';
import { useRef } from 'react';
import { openSettings } from '../commands/openSettings';
import { useAppInfo } from '../hooks/useAppInfo';
@@ -12,6 +13,7 @@ import { Dropdown } from './core/Dropdown';
import { Icon } from './core/Icon';
import { IconButton } from './core/IconButton';
import { KeyboardShortcutsDialog } from './KeyboardShortcutsDialog';
import { SettingsTab } from './Settings/SettingsTab';
export function SettingsDropdown() {
const importData = useImportData();
@@ -19,6 +21,7 @@ export function SettingsDropdown() {
const appInfo = useAppInfo();
const dropdownRef = useRef<DropdownRef>(null);
const checkForUpdates = useCheckForUpdates();
const { check } = useLicense();
useListenToTauriEvent('settings', () => openSettings.mutate(null));
@@ -56,6 +59,13 @@ export function SettingsDropdown() {
onSelect: () => exportData.mutate(),
},
{ type: 'separator', label: `Yaak v${appInfo.version}` },
{
label: 'Purchase License',
color: 'success',
hidden: check.data == null || check.data.type === 'commercial_use',
leftSlot: <Icon icon="circle_dollar_sign" />,
onSelect: () => openSettings.mutate(SettingsTab.License),
},
{
label: 'Check for Updates',
leftSlot: <Icon icon="update" />,

View File

@@ -55,7 +55,7 @@ export type DropdownItemDefault = {
label: ReactNode;
hotKeyAction?: HotkeyAction;
hotKeyLabelOnly?: boolean;
color?: 'default' | 'danger' | 'info' | 'warning' | 'notice' | 'success';
color?: 'default' | 'primary' | 'danger' | 'info' | 'warning' | 'notice' | 'success';
disabled?: boolean;
hidden?: boolean;
leftSlot?: ReactNode;
@@ -645,6 +645,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
'min-w-[8rem] outline-none px-2 mx-1.5 flex whitespace-nowrap',
'focus:bg-surface-highlight focus:text rounded',
item.color === 'danger' && '!text-danger',
item.color === 'primary' && '!text-primary',
item.color === 'success' && '!text-success',
item.color === 'warning' && '!text-warning',
item.color === 'notice' && '!text-notice',

View File

@@ -8,6 +8,7 @@ const icons = {
alert_triangle: lucide.AlertTriangleIcon,
archive: lucide.ArchiveIcon,
arrow_big_down_dash: lucide.ArrowBigDownDashIcon,
circle_dollar_sign: lucide.CircleDollarSignIcon,
arrow_right_circle: lucide.ArrowRightCircleIcon,
arrow_big_left_dash: lucide.ArrowBigLeftDashIcon,
arrow_big_right: lucide.ArrowBigRightIcon,