Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -82,17 +81,22 @@ export default class EntryAbility extends RustAbility {
}
});
RustModule.registerArktsFunction('check_app_update_ohos', async (err: Error, _arg: string): Promise<string> => {
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<string> => {
try {
Expand Down
72 changes: 57 additions & 15 deletions src/apps/ohos/entry/src/main/ets/utils/AppUpdater.ets
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<UpdateCheckResult> {
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;
}
}
48 changes: 35 additions & 13 deletions src/apps/ohos/entry/src/main/ets/utils/UpdateStrategy.ets
Original file line number Diff line number Diff line change
@@ -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<UpdateStrategyResult>;
}

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<UpdateStrategyResult> {
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<UpdateStrategyResult> {
hilog.info(DOMAIN, TAG, 'SilentUpdateStrategy: download in background');
let result: UpdateStrategyResult = { success: true, errorMessage: '' };
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,6 +74,10 @@ export const NewProjectDialog: React.FC<NewProjectDialogProps> = ({
setError(t('newProject.errorEnterName'));
return;
}
if (INVALID_NAME_CHARS.test(projectName.trim())) {
notificationService.warning(t('newProject.errorInvalidName'), { duration: 4500 });
return;
}

setIsCreating(true);
setError('');
Expand All @@ -82,7 +89,9 @@ export const NewProjectDialog: React.FC<NewProjectDialogProps> = ({
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);
}
Expand Down
3 changes: 2 additions & 1 deletion src/web-ui/src/locales/en-US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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…",
Expand Down
3 changes: 2 additions & 1 deletion src/web-ui/src/locales/zh-CN/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,8 @@
"creating": "创建中...",
"errorSelectParent": "请选择父目录",
"errorEnterName": "请输入工作区名称",
"errorCreateFailed": "创建工作区失败"
"errorCreateFailed": "创建工作区失败",
"errorInvalidName": "工作区名称不能包含以下字符:/ \\ : * ? \" < > |"
},
"peerDirectoryPicker": {
"loading": "正在加载远程目录…",
Expand Down
3 changes: 2 additions & 1 deletion src/web-ui/src/locales/zh-TW/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,8 @@
"creating": "建立中...",
"errorSelectParent": "請選擇父目錄",
"errorEnterName": "請輸入工作區名稱",
"errorCreateFailed": "建立工作區失敗"
"errorCreateFailed": "建立工作區失敗",
"errorInvalidName": "工作區名稱不能包含以下字符:/ \\ : * ? \" < > |"
},
"peerDirectoryPicker": {
"loading": "正在載入遠端目錄…",
Expand Down
Loading