feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
@@ -0,0 +1,9 @@
|
||||
import { ItemEvent } from '@joplin/lib/components/shared/config/plugins/types';
|
||||
import { Linking } from 'react-native';
|
||||
import getPluginHelpUrl from '@joplin/lib/services/plugins/utils/getPluginHelpUrl';
|
||||
|
||||
const openWebsiteForPlugin = ({ item }: ItemEvent) => {
|
||||
return Linking.openURL(getPluginHelpUrl(item.manifest.id));
|
||||
};
|
||||
|
||||
export default openWebsiteForPlugin;
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ItemEvent, OnPluginSettingChangeEvent } from '@joplin/lib/components/shared/config/plugins/types';
|
||||
import useOnDeleteHandler from '@joplin/lib/components/shared/config/plugins/useOnDeleteHandler';
|
||||
import useOnInstallHandler from '@joplin/lib/components/shared/config/plugins/useOnInstallHandler';
|
||||
import NavService from '@joplin/lib/services/NavService';
|
||||
import { PluginSettings, defaultPluginSetting } from '@joplin/lib/services/plugins/PluginService';
|
||||
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
updatePluginStates: (settingValue: PluginSettings)=> void;
|
||||
pluginSettings: PluginSettings;
|
||||
repoApi: RepositoryApi;
|
||||
}
|
||||
|
||||
export type PluginCallback = (event: ItemEvent)=> void;
|
||||
|
||||
export interface PluginCallbacks {
|
||||
onToggle: PluginCallback;
|
||||
onUpdate: PluginCallback;
|
||||
onInstall: PluginCallback;
|
||||
onDelete: PluginCallback;
|
||||
onShowPluginLog: PluginCallback;
|
||||
}
|
||||
|
||||
const usePluginCallbacks = (props: Props) => {
|
||||
const onPluginSettingsChange = useCallback((event: OnPluginSettingChangeEvent) => {
|
||||
props.updatePluginStates(event.value);
|
||||
}, [props.updatePluginStates]);
|
||||
|
||||
const updatePluginEnabled = useCallback((pluginId: string, enabled: boolean) => {
|
||||
const newSettings = { ...props.pluginSettings };
|
||||
newSettings[pluginId] = {
|
||||
...defaultPluginSetting(),
|
||||
...newSettings[pluginId],
|
||||
enabled,
|
||||
};
|
||||
|
||||
props.updatePluginStates(newSettings);
|
||||
}, [props.pluginSettings, props.updatePluginStates]);
|
||||
|
||||
const onToggle = useCallback((event: ItemEvent) => {
|
||||
const pluginId = event.item.manifest.id;
|
||||
const settings = props.pluginSettings[pluginId] ?? defaultPluginSetting();
|
||||
updatePluginEnabled(pluginId, !settings.enabled);
|
||||
}, [props.pluginSettings, updatePluginEnabled]);
|
||||
|
||||
const pluginSettingsRef = useRef(props.pluginSettings);
|
||||
pluginSettingsRef.current = props.pluginSettings;
|
||||
|
||||
const onDelete = useOnDeleteHandler(pluginSettingsRef, onPluginSettingsChange, true);
|
||||
|
||||
const [updatingPluginIds, setUpdatingPluginIds] = useState<Record<string, boolean>>({});
|
||||
const onUpdate = useOnInstallHandler(setUpdatingPluginIds, pluginSettingsRef, props.repoApi, onPluginSettingsChange, true);
|
||||
|
||||
const [installingPluginIds, setInstallingPluginIds] = useState<Record<string, boolean>>({});
|
||||
const onInstall = useOnInstallHandler(
|
||||
setInstallingPluginIds, pluginSettingsRef, props.repoApi, onPluginSettingsChange, false,
|
||||
);
|
||||
|
||||
const onShowPluginLog = useCallback((event: ItemEvent) => {
|
||||
const pluginId = event.item.manifest.id;
|
||||
void NavService.go('Log', { defaultFilter: pluginId });
|
||||
}, []);
|
||||
|
||||
const callbacks = useMemo((): PluginCallbacks => {
|
||||
return {
|
||||
onToggle,
|
||||
onDelete,
|
||||
onUpdate,
|
||||
onInstall,
|
||||
onShowPluginLog,
|
||||
};
|
||||
}, [onToggle, onDelete, onUpdate, onInstall, onShowPluginLog]);
|
||||
|
||||
return {
|
||||
callbacks,
|
||||
updatingPluginIds,
|
||||
installingPluginIds,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePluginCallbacks;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
|
||||
import { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import { PluginManifest } from '@joplin/lib/services/plugins/utils/types';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import usePlugin from '@joplin/lib/hooks/plugins/usePlugin';
|
||||
|
||||
// initialItem is used when the plugin is not installed. For example, if the plugin item is being
|
||||
// created from search results.
|
||||
const usePluginItem = (id: string, pluginSettings: PluginSettings, initialItem: PluginItem|null): PluginItem => {
|
||||
const plugin = usePlugin(id);
|
||||
|
||||
const lastManifest = useRef<PluginManifest|null>(null);
|
||||
if (plugin) {
|
||||
lastManifest.current = plugin.manifest;
|
||||
} else if (!lastManifest.current) {
|
||||
lastManifest.current = initialItem?.manifest;
|
||||
}
|
||||
const manifest = lastManifest.current;
|
||||
|
||||
return useMemo(() => {
|
||||
if (!manifest) return null;
|
||||
const settings = pluginSettings[id];
|
||||
|
||||
const installed = !!settings || !!plugin;
|
||||
|
||||
return {
|
||||
id,
|
||||
manifest,
|
||||
|
||||
installed,
|
||||
enabled: settings?.enabled ?? installed,
|
||||
deleted: settings?.deleted ?? false,
|
||||
hasBeenUpdated: settings?.hasBeenUpdated ?? false,
|
||||
devMode: plugin?.devMode ?? false,
|
||||
builtIn: plugin?.builtIn ?? false,
|
||||
};
|
||||
}, [plugin, id, pluginSettings, manifest]);
|
||||
};
|
||||
|
||||
export default usePluginItem;
|
||||
@@ -0,0 +1,51 @@
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import RepositoryApi, { InstallMode } from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import Setting, { AppType } from '@joplin/lib/models/Setting';
|
||||
import { useMemo } from 'react';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
|
||||
const logger = Logger.create('useRepoApi');
|
||||
|
||||
interface Props {
|
||||
reloadRepoCounter: number;
|
||||
setRepoApiError: (error: string|null)=> void;
|
||||
onRepoApiLoaded: (repoApi: RepositoryApi)=> void;
|
||||
}
|
||||
|
||||
let repoApi_: RepositoryApi|null = null;
|
||||
|
||||
export const resetRepoApi = () => {
|
||||
repoApi_ = null;
|
||||
};
|
||||
|
||||
const useRepoApi = ({ reloadRepoCounter, setRepoApiError, onRepoApiLoaded }: Props) => {
|
||||
const repoApi = useMemo(() => {
|
||||
const installMode = shim.mobilePlatform() === 'ios' ? InstallMode.Restricted : InstallMode.Default;
|
||||
const appInfo = { type: AppType.Mobile, version: PluginService.instance().appVersion };
|
||||
repoApi_ ??= RepositoryApi.ofDefaultJoplinRepo(Setting.value('tempDir'), appInfo, installMode);
|
||||
return repoApi_;
|
||||
}, []);
|
||||
|
||||
useAsyncEffect(async event => {
|
||||
if (reloadRepoCounter > 0) {
|
||||
logger.info(`Reloading the plugin repository -- try #${reloadRepoCounter + 1}`);
|
||||
}
|
||||
|
||||
setRepoApiError(null);
|
||||
try {
|
||||
await repoApi.reinitialize();
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
setRepoApiError(error);
|
||||
}
|
||||
if (!event.cancelled) {
|
||||
onRepoApiLoaded(repoApi);
|
||||
}
|
||||
}, [setRepoApiError, reloadRepoCounter, repoApi]);
|
||||
|
||||
return repoApi;
|
||||
};
|
||||
|
||||
export default useRepoApi;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export enum UpdateState {
|
||||
Idle = 1,
|
||||
CanUpdate = 2,
|
||||
Updating = 3,
|
||||
HasBeenUpdated = 4,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
pluginId: string;
|
||||
|
||||
pluginSettings: PluginSettings;
|
||||
updatingPluginIds: Record<string, boolean>;
|
||||
updatablePluginIds: Record<string, boolean>;
|
||||
}
|
||||
|
||||
const useUpdateState = ({ pluginId, pluginSettings, updatablePluginIds, updatingPluginIds }: Props) => {
|
||||
return useMemo(() => {
|
||||
const settings = pluginSettings[pluginId];
|
||||
|
||||
// Uninstalled
|
||||
if (!settings) return UpdateState.Idle;
|
||||
|
||||
if (settings.hasBeenUpdated) {
|
||||
return UpdateState.HasBeenUpdated;
|
||||
}
|
||||
if (updatingPluginIds[pluginId]) {
|
||||
return UpdateState.Updating;
|
||||
}
|
||||
if (updatablePluginIds[pluginId]) {
|
||||
return UpdateState.CanUpdate;
|
||||
}
|
||||
return UpdateState.Idle;
|
||||
}, [pluginSettings, updatingPluginIds, pluginId, updatablePluginIds]);
|
||||
};
|
||||
|
||||
export default useUpdateState;
|
||||
在新工单中引用
屏蔽一个用户