mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
License and updater Cargo features (#258)
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -114,4 +114,4 @@ jobs:
|
||||
releaseBody: '[Changelog __VERSION__](https://yaak.app/blog/__VERSION__)'
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }}
|
||||
args: '${{ matrix.args }} --features "updater license"'
|
||||
|
||||
@@ -32,6 +32,9 @@ strip = true # Automatically strip symbols from the binary.
|
||||
|
||||
[features]
|
||||
cargo-clippy = []
|
||||
default = []
|
||||
updater = []
|
||||
license = ["yaak-license"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.4.1", features = [] }
|
||||
@@ -74,7 +77,7 @@ yaak-fonts = { workspace = true }
|
||||
yaak-git = { path = "yaak-git" }
|
||||
yaak-grpc = { path = "yaak-grpc" }
|
||||
yaak-http = { workspace = true }
|
||||
yaak-license = { path = "yaak-license" }
|
||||
yaak-license = { path = "yaak-license", optional = true }
|
||||
yaak-mac-window = { path = "yaak-mac-window" }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
|
||||
@@ -73,6 +73,8 @@ struct AppMetaData {
|
||||
name: String,
|
||||
app_data_dir: String,
|
||||
app_log_dir: String,
|
||||
feature_updater: bool,
|
||||
feature_license: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -85,6 +87,8 @@ async fn cmd_metadata(app_handle: AppHandle) -> YaakResult<AppMetaData> {
|
||||
name: app_handle.package_info().name.to_string(),
|
||||
app_data_dir: app_data_dir.to_string_lossy().to_string(),
|
||||
app_log_dir: app_log_dir.to_string_lossy().to_string(),
|
||||
feature_license: cfg!(feature = "license"),
|
||||
feature_updater: cfg!(feature = "updater"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1254,7 +1258,6 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(yaak_license::init())
|
||||
.plugin(yaak_mac_window::init())
|
||||
.plugin(yaak_models::init())
|
||||
.plugin(yaak_plugins::init())
|
||||
@@ -1264,6 +1267,11 @@ pub fn run() {
|
||||
.plugin(yaak_ws::init())
|
||||
.plugin(yaak_sync::init());
|
||||
|
||||
#[cfg(feature = "license")]
|
||||
{
|
||||
builder = builder.plugin(yaak_license::init());
|
||||
}
|
||||
|
||||
builder
|
||||
.setup(|app| {
|
||||
{
|
||||
@@ -1380,19 +1388,21 @@ pub fn run() {
|
||||
label,
|
||||
..
|
||||
} => {
|
||||
let w = app_handle.get_webview_window(&label).unwrap();
|
||||
let h = app_handle.clone();
|
||||
|
||||
// Run update check whenever the window is focused
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if w.db().get_settings().autoupdate {
|
||||
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
||||
let update_mode = get_update_mode(&w).await.unwrap();
|
||||
if let Err(e) = val.lock().await.maybe_check(&w, update_mode).await {
|
||||
warn!("Failed to check for updates {e:?}");
|
||||
if cfg!(feature = "updater") {
|
||||
// Run update check whenever the window is focused
|
||||
let w = app_handle.get_webview_window(&label).unwrap();
|
||||
let h = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if w.db().get_settings().autoupdate {
|
||||
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
||||
let update_mode = get_update_mode(&w).await.unwrap();
|
||||
if let Err(e) = val.lock().await.maybe_check(&w, update_mode).await
|
||||
{
|
||||
warn!("Failed to check for updates {e:?}");
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let h = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
|
||||
20
src-web/components/CargoFeature.tsx
Normal file
20
src-web/components/CargoFeature.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { appInfo } from '../lib/appInfo';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
feature: 'updater' | 'license';
|
||||
}
|
||||
|
||||
const featureMap: Record<Props['feature'], boolean> = {
|
||||
updater: appInfo.featureUpdater,
|
||||
license: appInfo.featureLicense,
|
||||
};
|
||||
|
||||
export function CargoFeature({ children, feature }: Props) {
|
||||
if (featureMap[feature]) {
|
||||
return <>{children}</>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { useAtomValue } from 'jotai';
|
||||
import type { ReactNode } from 'react';
|
||||
import { openSettings } from '../commands/openSettings';
|
||||
import { appInfo } from '../lib/appInfo';
|
||||
import { CargoFeature } from './CargoFeature';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
|
||||
@@ -19,6 +20,14 @@ const details: Record<
|
||||
};
|
||||
|
||||
export function LicenseBadge() {
|
||||
return (
|
||||
<CargoFeature feature="license">
|
||||
<LicenseBadgeCmp />
|
||||
</CargoFeature>
|
||||
);
|
||||
}
|
||||
|
||||
function LicenseBadgeCmp() {
|
||||
const { check } = useLicense();
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import { type } from '@tauri-apps/plugin-os';
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { useKeyPressEvent } from 'react-use';
|
||||
import { appInfo } from '../../lib/appInfo';
|
||||
import { capitalize } from '../../lib/capitalize';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import type { TabItem } from '../core/Tabs/Tabs';
|
||||
import { TabContent, Tabs } from '../core/Tabs/Tabs';
|
||||
import { HeaderSize } from '../HeaderSize';
|
||||
import { SettingsInterface } from './SettingsInterface';
|
||||
@@ -72,21 +74,27 @@ export default function Settings({ hide }: Props) {
|
||||
tabListClassName="min-w-[10rem] bg-surface x-theme-sidebar border-r border-border pl-3"
|
||||
label="Settings"
|
||||
onChangeValue={setTab}
|
||||
tabs={tabs.map((value) => ({ value, label: capitalize(value) }))}
|
||||
tabs={tabs.map(
|
||||
(value): TabItem => ({
|
||||
value,
|
||||
label: capitalize(value),
|
||||
hidden: !appInfo.featureLicense && value === TAB_LICENSE,
|
||||
}),
|
||||
)}
|
||||
>
|
||||
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full p-8">
|
||||
<TabContent value={TAB_GENERAL} className="overflow-y-auto h-full px-8 !py-4">
|
||||
<SettingsGeneral />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_INTERFACE} className="overflow-y-auto h-full p-8">
|
||||
<TabContent value={TAB_INTERFACE} className="overflow-y-auto h-full px-8 !py-4">
|
||||
<SettingsInterface />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_THEME} className="overflow-y-auto h-full p-8">
|
||||
<TabContent value={TAB_THEME} className="overflow-y-auto h-full px-8 !py-4">
|
||||
<SettingsTheme />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_PLUGINS} className="h-full grid grid-rows-1 p-8">
|
||||
<TabContent value={TAB_PLUGINS} className="h-full grid grid-rows-1 px-8 !py-4">
|
||||
<SettingsPlugins />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_PROXY} className="overflow-y-auto h-full p-8!">
|
||||
<TabContent value={TAB_PROXY} className="overflow-y-auto h-full px-8 !py-4">
|
||||
<SettingsProxy />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_LICENSE} className="overflow-y-auto h-full px-8 !py-4">
|
||||
|
||||
@@ -6,6 +6,7 @@ import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { appInfo } from '../../lib/appInfo';
|
||||
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
|
||||
import { revealInFinderText } from '../../lib/reveal';
|
||||
import { CargoFeature } from '../CargoFeature';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { Heading } from '../core/Heading';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
@@ -26,43 +27,45 @@ export function SettingsGeneral() {
|
||||
|
||||
return (
|
||||
<VStack space={1.5} className="mb-4">
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_auto] gap-1">
|
||||
<CargoFeature feature="updater">
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_auto] gap-1">
|
||||
<Select
|
||||
name="updateChannel"
|
||||
label="Update Channel"
|
||||
labelPosition="left"
|
||||
labelClassName="w-[14rem]"
|
||||
size="sm"
|
||||
value={settings.updateChannel}
|
||||
onChange={(updateChannel) => patchModel(settings, { updateChannel })}
|
||||
options={[
|
||||
{ label: 'Stable', value: 'stable' },
|
||||
{ label: 'Beta (more frequent)', value: 'beta' },
|
||||
]}
|
||||
/>
|
||||
<IconButton
|
||||
variant="border"
|
||||
size="sm"
|
||||
title="Check for updates"
|
||||
icon="refresh"
|
||||
spin={checkForUpdates.isPending}
|
||||
onClick={() => checkForUpdates.mutateAsync()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
name="updateChannel"
|
||||
label="Update Channel"
|
||||
name="autoupdate"
|
||||
value={settings.autoupdate ? 'auto' : 'manual'}
|
||||
label="Update Behavior"
|
||||
labelPosition="left"
|
||||
labelClassName="w-[14rem]"
|
||||
size="sm"
|
||||
value={settings.updateChannel}
|
||||
onChange={(updateChannel) => patchModel(settings, { updateChannel })}
|
||||
labelClassName="w-[14rem]"
|
||||
onChange={(v) => patchModel(settings, { autoupdate: v === 'auto' })}
|
||||
options={[
|
||||
{ label: 'Stable', value: 'stable' },
|
||||
{ label: 'Beta (more frequent)', value: 'beta' },
|
||||
{ label: 'Automatic', value: 'auto' },
|
||||
{ label: 'Manual', value: 'manual' },
|
||||
]}
|
||||
/>
|
||||
<IconButton
|
||||
variant="border"
|
||||
size="sm"
|
||||
title="Check for updates"
|
||||
icon="refresh"
|
||||
spin={checkForUpdates.isPending}
|
||||
onClick={() => checkForUpdates.mutateAsync()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
name="autoupdate"
|
||||
value={settings.autoupdate ? 'auto' : 'manual'}
|
||||
label="Update Behavior"
|
||||
labelPosition="left"
|
||||
size="sm"
|
||||
labelClassName="w-[14rem]"
|
||||
onChange={(v) => patchModel(settings, { autoupdate: v === 'auto' })}
|
||||
options={[
|
||||
{ label: 'Automatic', value: 'auto' },
|
||||
{ label: 'Manual', value: 'manual' },
|
||||
]}
|
||||
/>
|
||||
</CargoFeature>
|
||||
|
||||
<Select
|
||||
name="switchWorkspaceBehavior"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { differenceInDays } from 'date-fns';
|
||||
import React, { useState } from 'react';
|
||||
import { useToggle } from '../../hooks/useToggle';
|
||||
import { pluralizeCount } from '../../lib/pluralize';
|
||||
import { CargoFeature } from '../CargoFeature';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import { Icon } from '../core/Icon';
|
||||
@@ -13,6 +14,14 @@ import { HStack, VStack } from '../core/Stacks';
|
||||
import { LocalImage } from '../LocalImage';
|
||||
|
||||
export function SettingsLicense() {
|
||||
return (
|
||||
<CargoFeature feature="license">
|
||||
<SettingsLicenseCmp />
|
||||
</CargoFeature>
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsLicenseCmp() {
|
||||
const { check, activate, deactivate } = useLicense();
|
||||
const [key, setKey] = useState<string>('');
|
||||
const [activateFormVisible, toggleActivateFormVisible] = useToggle(false);
|
||||
|
||||
@@ -74,6 +74,7 @@ export function SettingsDropdown() {
|
||||
{
|
||||
label: 'Check for Updates',
|
||||
leftSlot: <Icon icon="update" />,
|
||||
hidden: !appInfo.featureUpdater,
|
||||
onSelect: () => checkForUpdates.mutate(),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ export type TabItem =
|
||||
| {
|
||||
value: string;
|
||||
label: string;
|
||||
hidden?: boolean;
|
||||
rightSlot?: ReactNode;
|
||||
}
|
||||
| {
|
||||
@@ -97,6 +98,10 @@ export function Tabs({
|
||||
)}
|
||||
>
|
||||
{tabs.map((t) => {
|
||||
if ('hidden' in t && t.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isActive = t.value === value;
|
||||
const btnClassName = classNames(
|
||||
'h-sm flex items-center rounded whitespace-nowrap',
|
||||
|
||||
@@ -8,9 +8,13 @@ export interface AppInfo {
|
||||
appDataDir: string;
|
||||
appLogDir: string;
|
||||
identifier: string;
|
||||
featureLicense: boolean;
|
||||
featureUpdater: boolean;
|
||||
}
|
||||
|
||||
export const appInfo = {
|
||||
...(await invokeCmd('cmd_metadata')),
|
||||
identifier: await getIdentifier(),
|
||||
} as AppInfo;
|
||||
|
||||
console.log('App info', appInfo);
|
||||
|
||||
Reference in New Issue
Block a user