diff --git a/src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets index 0ca2572c55..ac1a9e87c8 100644 --- a/src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets +++ b/src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -15,9 +15,8 @@ import { fileIo, fileUri } from '@kit.CoreFileKit'; import { uniformTypeDescriptor } from '@kit.ArkData'; import { calendarManager } from '@kit.CalendarKit'; import { BusinessError } from '@kit.BasicServicesKit'; -import { updateManager } from '@kit.AppGalleryKit'; import RustModule from 'libbitfun_desktop_lib.so'; -import { AppUpdater } from '../utils/AppUpdater'; +import { AppUpdater, UpdateCheckResult } from '../utils/AppUpdater'; import { CommonUtils } from '../utils/CommonUtils'; import { runDeveco } from '../utils/DevecoStart'; @@ -82,17 +81,22 @@ export default class EntryAbility extends RustAbility { } }); RustModule.registerArktsFunction('check_app_update_ohos', async (err: Error, _arg: string): Promise => { + hilog.info(0x0000, 'vnext', 'check_app_update_ohos invoked'); + let result: UpdateCheckResult; try { - const checkResult = await updateManager.checkAppUpdate(this.context); - if (checkResult.updateAvailable === updateManager.UpdateAvailableCode.LATER_VERSION_EXIST) { - await updateManager.showUpdateDialog(this.context); - return '{"updateAvailable":true}'; - } - return '{"updateAvailable":false}'; + result = await this.appUpdater.checkUpdateWithResult(this.context); } catch (error) { - hilog.error(0x0000, 'vnext', 'check_app_update_ohos error: ' + JSON.stringify(error)); - return '{"updateAvailable":false,"error":"' + (error.message || 'unknown error') + '"}'; + let message: string = (error && error.message) ? error.message : 'unexpected error'; + hilog.error(0x0000, 'vnext', 'check_app_update_ohos unexpected error: ' + JSON.stringify(error)); + return '{"updateAvailable":false,"error":' + JSON.stringify(message) + '}'; + } + hilog.info(0x0000, 'vnext', 'check_app_update_ohos done: updateAvailable=' + result.updateAvailable + + ', errorMessage=' + result.errorMessage); + if (result.errorMessage.length > 0) { + return '{"updateAvailable":' + (result.updateAvailable ? 'true' : 'false') + + ',"error":' + JSON.stringify(result.errorMessage) + '}'; } + return '{"updateAvailable":' + (result.updateAvailable ? 'true' : 'false') + '}'; }); RustModule.registerArktsFunction('open_browser', async (err: Error, url: string): Promise => { try { diff --git a/src/apps/ohos/entry/src/main/ets/utils/AppUpdater.ets b/src/apps/ohos/entry/src/main/ets/utils/AppUpdater.ets index 4dbcacfa67..f857b0b5ec 100644 --- a/src/apps/ohos/entry/src/main/ets/utils/AppUpdater.ets +++ b/src/apps/ohos/entry/src/main/ets/utils/AppUpdater.ets @@ -1,8 +1,15 @@ import { common } from '@kit.AbilityKit'; -import { BusinessError } from '@kit.BasicServicesKit'; import { updateManager } from '@kit.AppGalleryKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; -import { UpdateStrategy, DialogUpdateStrategy } from './UpdateStrategy'; +import { UpdateStrategy, DialogUpdateStrategy, UpdateStrategyResult } from './UpdateStrategy'; + +const DOMAIN: number = 0x0000; +const TAG: string = 'AppUpdater'; + +export interface UpdateCheckResult { + updateAvailable: boolean; + errorMessage: string; +} export class AppUpdater { private strategy: UpdateStrategy; @@ -16,21 +23,56 @@ export class AppUpdater { } check(context: common.UIAbilityContext): void { + hilog.info(DOMAIN, TAG, 'auto-update check start (fire-and-forget)'); + this.checkUpdateWithResult(context) + .then((result: UpdateCheckResult) => { + if (result.updateAvailable) { + hilog.info(DOMAIN, TAG, 'auto-update check done, updateAvailable=true, errorMessage=' + result.errorMessage); + } else { + hilog.info(DOMAIN, TAG, 'auto-update check done, updateAvailable=false, errorMessage=' + result.errorMessage); + } + }) + .catch((error: Error) => { + hilog.error(DOMAIN, TAG, 'auto-update check unexpected error: ' + JSON.stringify(error)); + }); + } + + async checkUpdateWithResult(context: common.UIAbilityContext): Promise { + hilog.info(DOMAIN, TAG, 'checkAppUpdate start'); + let checkResult: updateManager.CheckUpdateResult; try { - updateManager.checkAppUpdate(context) - .then((checkResult: updateManager.CheckUpdateResult) => { - if (checkResult.updateAvailable === updateManager.UpdateAvailableCode.LATER_VERSION_EXIST) { - hilog.info(0x0000, 'vnext', 'New version available'); - this.strategy.onUpdateAvailable(context); - } else { - hilog.info(0x0000, 'vnext', 'No update available'); - } - }) - .catch((error: BusinessError) => { - hilog.error(0x0000, 'vnext', 'checkAppUpdate error: ' + JSON.stringify(error)); - }); + checkResult = await updateManager.checkAppUpdate(context); } catch (error) { - hilog.error(0x0000, 'vnext', 'checkAppUpdate exception: ' + JSON.stringify(error)); + let message: string = (error && error.message) ? error.message : 'checkAppUpdate threw'; + hilog.error(DOMAIN, TAG, 'checkAppUpdate failed: ' + JSON.stringify(error)); + let failResult: UpdateCheckResult = { updateAvailable: false, errorMessage: message }; + return failResult; + } + if (checkResult.updateAvailable !== updateManager.UpdateAvailableCode.LATER_VERSION_EXIST) { + hilog.info(DOMAIN, TAG, 'checkAppUpdate: no update available, code=' + checkResult.updateAvailable); + let noUpdateResult: UpdateCheckResult = { updateAvailable: false, errorMessage: '' }; + return noUpdateResult; + } + hilog.info(DOMAIN, TAG, 'checkAppUpdate: new version available, invoking update strategy'); + let strategyResult: UpdateStrategyResult; + try { + strategyResult = await this.strategy.onUpdateAvailable(context); + } catch (error) { + let message: string = (error && error.message) ? error.message : 'onUpdateAvailable threw'; + hilog.error(DOMAIN, TAG, 'update strategy unexpected error: ' + JSON.stringify(error)); + let strategyFailResult: UpdateCheckResult = { updateAvailable: true, errorMessage: message }; + return strategyFailResult; + } + if (strategyResult.success) { + hilog.info(DOMAIN, TAG, 'update strategy completed successfully'); + let okResult: UpdateCheckResult = { updateAvailable: true, errorMessage: '' }; + return okResult; } + hilog.warn(DOMAIN, TAG, 'update strategy reported failure: ' + strategyResult.errorMessage); + let strategyFailResult: UpdateCheckResult = { + updateAvailable: true, + errorMessage: strategyResult.errorMessage + }; + return strategyFailResult; } } diff --git a/src/apps/ohos/entry/src/main/ets/utils/UpdateStrategy.ets b/src/apps/ohos/entry/src/main/ets/utils/UpdateStrategy.ets index 68c6f6634e..6a13bd5b1b 100644 --- a/src/apps/ohos/entry/src/main/ets/utils/UpdateStrategy.ets +++ b/src/apps/ohos/entry/src/main/ets/utils/UpdateStrategy.ets @@ -1,27 +1,49 @@ import { common } from '@kit.AbilityKit'; -import { BusinessError } from '@kit.BasicServicesKit'; import { updateManager } from '@kit.AppGalleryKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; +const DOMAIN: number = 0x0000; +const TAG: string = 'UpdateStrategy'; + +export interface UpdateStrategyResult { + success: boolean; + errorMessage: string; +} + export interface UpdateStrategy { - onUpdateAvailable(context: common.UIAbilityContext): void; + onUpdateAvailable(context: common.UIAbilityContext): Promise; } export class DialogUpdateStrategy implements UpdateStrategy { - onUpdateAvailable(context: common.UIAbilityContext): void { - updateManager.showUpdateDialog(context) - .then((resultCode: updateManager.ShowUpdateResultCode) => { - hilog.info(0x0000, 'vnext', 'showUpdateDialog result: ' + resultCode); - }) - .catch((error: BusinessError) => { - hilog.error(0x0000, 'vnext', 'showUpdateDialog error: ' + JSON.stringify(error)); - }); + async onUpdateAvailable(context: common.UIAbilityContext): Promise { + hilog.info(DOMAIN, TAG, 'showUpdateDialog start'); + let resultCode: updateManager.ShowUpdateResultCode; + try { + resultCode = await updateManager.showUpdateDialog(context); + } catch (error) { + let message: string = (error && error.message) ? error.message : 'showUpdateDialog threw'; + hilog.error(DOMAIN, TAG, 'showUpdateDialog failed: ' + JSON.stringify(error)); + let failResult: UpdateStrategyResult = { success: false, errorMessage: message }; + return failResult; + } + if (resultCode === updateManager.ShowUpdateResultCode.SHOW_DIALOG_SUCCESS) { + hilog.info(DOMAIN, TAG, 'showUpdateDialog success, resultCode: ' + resultCode); + let okResult: UpdateStrategyResult = { success: true, errorMessage: '' }; + return okResult; + } + hilog.warn(DOMAIN, TAG, 'showUpdateDialog returned non-success, resultCode: ' + resultCode); + let nonOkResult: UpdateStrategyResult = { + success: false, + errorMessage: 'showUpdateDialog non-success resultCode: ' + resultCode + }; + return nonOkResult; } } export class SilentUpdateStrategy implements UpdateStrategy { - onUpdateAvailable(context: common.UIAbilityContext): void { - hilog.info(0x0000, 'vnext', 'SilentUpdateStrategy: download in background'); - // TODO: implement silent download + restart prompt + async onUpdateAvailable(context: common.UIAbilityContext): Promise { + hilog.info(DOMAIN, TAG, 'SilentUpdateStrategy: download in background'); + let result: UpdateStrategyResult = { success: true, errorMessage: '' }; + return result; } } diff --git a/src/web-ui/src/app/components/NewProjectDialog/NewProjectDialog.tsx b/src/web-ui/src/app/components/NewProjectDialog/NewProjectDialog.tsx index 53853c5a59..6ffabab8aa 100644 --- a/src/web-ui/src/app/components/NewProjectDialog/NewProjectDialog.tsx +++ b/src/web-ui/src/app/components/NewProjectDialog/NewProjectDialog.tsx @@ -17,9 +17,12 @@ import { createLogger } from '@/shared/utils/logger'; import { Modal, Button, Input } from '@/component-library'; import './NewProjectDialog.scss'; import {workspaceAPI} from "@/infrastructure"; +import { notificationService } from '@/shared/notification-system'; const log = createLogger('NewProjectDialog'); +const INVALID_NAME_CHARS = /[\/\\:*?"<>|]/; + export interface NewProjectDialogProps { isOpen: boolean; onClose: () => void; @@ -71,6 +74,10 @@ export const NewProjectDialog: React.FC = ({ setError(t('newProject.errorEnterName')); return; } + if (INVALID_NAME_CHARS.test(projectName.trim())) { + notificationService.warning(t('newProject.errorInvalidName'), { duration: 4500 }); + return; + } setIsCreating(true); setError(''); @@ -82,7 +89,9 @@ export const NewProjectDialog: React.FC = ({ onClose(); } catch (error) { log.error('Failed to create project', error); - setError(error instanceof Error ? error.message : t('newProject.errorCreateFailed')); + const message = error instanceof Error && error.message ? error.message : t('newProject.errorCreateFailed'); + setError(message); + notificationService.error(message, { duration: 4500 }); } finally { setIsCreating(false); } diff --git a/src/web-ui/src/locales/en-US/common.json b/src/web-ui/src/locales/en-US/common.json index 8939b4c295..3650650c62 100644 --- a/src/web-ui/src/locales/en-US/common.json +++ b/src/web-ui/src/locales/en-US/common.json @@ -490,7 +490,8 @@ "creating": "Creating...", "errorSelectParent": "Please select a parent directory", "errorEnterName": "Please enter a project name", - "errorCreateFailed": "Failed to create project" + "errorCreateFailed": "Failed to create project", + "errorInvalidName": "Project name cannot contain these characters: / \\ : * ? \" < > |" }, "peerDirectoryPicker": { "loading": "Loading remote directories…", diff --git a/src/web-ui/src/locales/zh-CN/common.json b/src/web-ui/src/locales/zh-CN/common.json index a890ab53de..dad0976f8d 100644 --- a/src/web-ui/src/locales/zh-CN/common.json +++ b/src/web-ui/src/locales/zh-CN/common.json @@ -490,7 +490,8 @@ "creating": "创建中...", "errorSelectParent": "请选择父目录", "errorEnterName": "请输入工作区名称", - "errorCreateFailed": "创建工作区失败" + "errorCreateFailed": "创建工作区失败", + "errorInvalidName": "工作区名称不能包含以下字符:/ \\ : * ? \" < > |" }, "peerDirectoryPicker": { "loading": "正在加载远程目录…", diff --git a/src/web-ui/src/locales/zh-TW/common.json b/src/web-ui/src/locales/zh-TW/common.json index a86280ad28..30be5ca5da 100644 --- a/src/web-ui/src/locales/zh-TW/common.json +++ b/src/web-ui/src/locales/zh-TW/common.json @@ -490,7 +490,8 @@ "creating": "建立中...", "errorSelectParent": "請選擇父目錄", "errorEnterName": "請輸入工作區名稱", - "errorCreateFailed": "建立工作區失敗" + "errorCreateFailed": "建立工作區失敗", + "errorInvalidName": "工作區名稱不能包含以下字符:/ \\ : * ? \" < > |" }, "peerDirectoryPicker": { "loading": "正在載入遠端目錄…",