diff --git a/src-tauri/yaak-license/index.ts b/src-tauri/yaak-license/index.ts index eedd5167..42d82f58 100644 --- a/src-tauri/yaak-license/index.ts +++ b/src-tauri/yaak-license/index.ts @@ -26,6 +26,7 @@ export function useLicense() { const CHECK_QUERY_KEY = ['license.check']; const check = useQuery({ + refetchInterval: 1000 * 60 * 60 * 12, // Refetch every 12 hours queryKey: CHECK_QUERY_KEY, queryFn: () => invoke('plugin:yaak-license|check'), }); diff --git a/src-web/components/LicenseBadge.tsx b/src-web/components/LicenseBadge.tsx index 13566799..bd3e250c 100644 --- a/src-web/components/LicenseBadge.tsx +++ b/src-web/components/LicenseBadge.tsx @@ -65,7 +65,7 @@ export function LicenseBadge() { if (check.data.type === 'trialing') { await setLicenseDetails((v) => ({ ...v, - dismissedTrial: true, + hasDismissedTrial: true, })); } openSettings.mutate(SettingsTab.License); diff --git a/src-web/components/Settings/SettingsLicense.tsx b/src-web/components/Settings/SettingsLicense.tsx index e88760ef..759d820b 100644 --- a/src-web/components/Settings/SettingsLicense.tsx +++ b/src-web/components/Settings/SettingsLicense.tsx @@ -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' ? (

- {formatDistanceToNowStrict(check.data.end)} days remaining on your - commercial use trial + You have{' '} + + {pluralizeCount('day', differenceInDays(check.data.end, new Date()))} remaining + {' '} + on your commercial use trial. Once the trial ends, Yaak will be limited to personal use + until a license is activated.

) : check.data?.type == 'personal_use' && !licenseDetails?.confirmedPersonalUse ? ( @@ -76,12 +81,7 @@ export function SettingsLicense() { {check.data?.type === 'commercial_use' ? ( - diff --git a/src-web/components/SettingsDropdown.tsx b/src-web/components/SettingsDropdown.tsx index ab73951c..a7d00b4e 100644 --- a/src-web/components/SettingsDropdown.tsx +++ b/src-web/components/SettingsDropdown.tsx @@ -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(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: , + onSelect: () => openSettings.mutate(SettingsTab.License), + }, { label: 'Check for Updates', leftSlot: , diff --git a/src-web/components/core/Dropdown.tsx b/src-web/components/core/Dropdown.tsx index 31bbb117..0c4f20dd 100644 --- a/src-web/components/core/Dropdown.tsx +++ b/src-web/components/core/Dropdown.tsx @@ -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', diff --git a/src-web/components/core/Icon.tsx b/src-web/components/core/Icon.tsx index 9928268e..e47cfee5 100644 --- a/src-web/components/core/Icon.tsx +++ b/src-web/components/core/Icon.tsx @@ -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,