diff --git a/src/frontend/src/lib/api/mission-control.api.ts b/src/frontend/src/lib/api/mission-control.api.ts index dd80b5e29..7a89440e0 100644 --- a/src/frontend/src/lib/api/mission-control.api.ts +++ b/src/frontend/src/lib/api/mission-control.api.ts @@ -1,8 +1,8 @@ -import type { Controller, SetController } from '$declarations/mission_control/mission_control.did'; +import type { Controller } from '$declarations/mission_control/mission_control.did'; import { getMissionControl } from '$lib/services/mission-control.services'; +import type { SetControllerParams } from '$lib/types/controlers'; import { getMissionControlActor } from '$lib/utils/actor.utils'; -import { toNullable } from '$lib/utils/did.utils'; -import { nonNullish } from '$lib/utils/utils'; +import { toSetController } from '$lib/utils/controllers.utils'; import type { Identity } from '@dfinity/agent'; import { Principal } from '@dfinity/principal'; @@ -43,16 +43,6 @@ export const initMissionControl = async ({ } }); -export type SetControllerParams = { - controllerId: string; - profile: string | null | undefined; -}; - -const toSetController = (profile: string | null | undefined): SetController => ({ - metadata: nonNullish(profile) && profile !== '' ? [['profile', profile]] : [], - expires_at: toNullable(undefined) -}); - export const setSatellitesController = async ({ missionControlId, satelliteIds, diff --git a/src/frontend/src/lib/components/canister/Controllers.svelte b/src/frontend/src/lib/components/canister/Controllers.svelte deleted file mode 100644 index eff6c0a86..000000000 --- a/src/frontend/src/lib/components/canister/Controllers.svelte +++ /dev/null @@ -1,171 +0,0 @@ - - -
- - - - - - - - - {#each controllers as [controllerId, controller] (controllerId.toText())} - - - - - {/each} - -
{$i18n.controllers.title} {$i18n.controllers.profile}
- { - selectedController = controllerId; - visible = true; - }} - /> - - {controllerId.toText()} - {metadataProfile(controller?.metadata ?? [])}
-
- - -
- {#if canDelete()} -

{$i18n.controllers.delete_question}

- - {#if nonNullish(selectedController)} -

- {@html i18nFormat($i18n.controllers.controller_id, [ - { - placeholder: '{0}', - value: selectedController.toText() - } - ])} -

- {/if} - - - - - {:else} -

{$i18n.controllers.no_delete}

- -

{$i18n.controllers.more_delete}

- - - {/if} -
-
- - diff --git a/src/frontend/src/lib/components/controllers/ControllerDelete.svelte b/src/frontend/src/lib/components/controllers/ControllerDelete.svelte new file mode 100644 index 000000000..af0852980 --- /dev/null +++ b/src/frontend/src/lib/components/controllers/ControllerDelete.svelte @@ -0,0 +1,89 @@ + + + +
+

{$i18n.controllers.delete_question}

+ + + {$i18n.controllers.controller_id} +

{selectedController?.[0].toText() ?? ''}

+
+ + + + +
+
+ + diff --git a/src/frontend/src/lib/components/controllers/ControllerInfo.svelte b/src/frontend/src/lib/components/controllers/ControllerInfo.svelte new file mode 100644 index 000000000..cc4355773 --- /dev/null +++ b/src/frontend/src/lib/components/controllers/ControllerInfo.svelte @@ -0,0 +1,26 @@ + + + +
+

{$i18n.controllers.no_delete}

+ +

{$i18n.controllers.more_delete}

+ + +
+
+ + diff --git a/src/frontend/src/lib/components/controllers/Controllers.svelte b/src/frontend/src/lib/components/controllers/Controllers.svelte new file mode 100644 index 000000000..1df3ce5a8 --- /dev/null +++ b/src/frontend/src/lib/components/controllers/Controllers.svelte @@ -0,0 +1,107 @@ + + +
+ + + + + + + + + {#each controllers as [controllerId, controller] (controllerId.toText())} + + + + + + {/each} + +
+ {$i18n.controllers.title} {$i18n.controllers.profile}
+ {#if canEdit(controllerId)} + { + selectedController = [controllerId, controller]; + visibleDelete = true; + }} + /> + {:else} + (visibleInfo = true)} + /> + {/if} + + {controllerId.toText()} + {metadataProfile(controller?.metadata ?? [])}
+
+ + + + + + diff --git a/src/frontend/src/lib/components/hosting/CustomDomainActions.svelte b/src/frontend/src/lib/components/hosting/CustomDomainActions.svelte index f1faa5e0d..19ca1c7fd 100644 --- a/src/frontend/src/lib/components/hosting/CustomDomainActions.svelte +++ b/src/frontend/src/lib/components/hosting/CustomDomainActions.svelte @@ -1,5 +1,5 @@
- + {#if displayState !== undefined && displayState?.toLowerCase() !== 'available'} - + })} + ariaLabel={$i18n.hosting.edit} + icon="edit" + /> {/if}
diff --git a/src/frontend/src/lib/components/hosting/Hosting.svelte b/src/frontend/src/lib/components/hosting/Hosting.svelte index aefa217f9..1c068a937 100644 --- a/src/frontend/src/lib/components/hosting/Hosting.svelte +++ b/src/frontend/src/lib/components/hosting/Hosting.svelte @@ -78,7 +78,7 @@ @use '../../styles/mixins/media'; .tools { - width: 88px; + width: 100px; } .domain { diff --git a/src/frontend/src/lib/components/icons/IconInfo.svelte b/src/frontend/src/lib/components/icons/IconInfo.svelte new file mode 100644 index 000000000..83fc803f9 --- /dev/null +++ b/src/frontend/src/lib/components/icons/IconInfo.svelte @@ -0,0 +1,15 @@ + + + + diff --git a/src/frontend/src/lib/components/mission-control/MissionControlControllers.svelte b/src/frontend/src/lib/components/mission-control/MissionControlControllers.svelte index e42e6c052..c942f74dd 100644 --- a/src/frontend/src/lib/components/mission-control/MissionControlControllers.svelte +++ b/src/frontend/src/lib/components/mission-control/MissionControlControllers.svelte @@ -4,7 +4,7 @@ deleteMissionControlController, listMissionControlControllers } from '$lib/api/mission-control.api'; - import Controllers from '$lib/components/canister/Controllers.svelte'; + import Controllers from '$lib/components/controllers/Controllers.svelte'; import { authStore } from '$lib/stores/auth.store'; import { nonNullish } from '$lib/utils/utils'; import type { Controller } from '$declarations/mission_control/mission_control.did'; diff --git a/src/frontend/src/lib/components/satellites/SatelliteControllers.svelte b/src/frontend/src/lib/components/satellites/SatelliteControllers.svelte index 781d3ca42..34b3284cf 100644 --- a/src/frontend/src/lib/components/satellites/SatelliteControllers.svelte +++ b/src/frontend/src/lib/components/satellites/SatelliteControllers.svelte @@ -3,7 +3,7 @@ import { listControllers } from '$lib/api/satellites.api'; import type { Principal } from '@dfinity/principal'; import { deleteSatellitesController } from '$lib/api/mission-control.api'; - import Controllers from '$lib/components/canister/Controllers.svelte'; + import Controllers from '$lib/components/controllers/Controllers.svelte'; import type { Controller } from '$declarations/mission_control/mission_control.did'; export let satellite: Satellite; diff --git a/src/frontend/src/lib/components/ui/ButtonDelete.svelte b/src/frontend/src/lib/components/ui/ButtonDelete.svelte deleted file mode 100644 index eadd7ba9e..000000000 --- a/src/frontend/src/lib/components/ui/ButtonDelete.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/src/frontend/src/lib/components/ui/ButtonTableAction.svelte b/src/frontend/src/lib/components/ui/ButtonTableAction.svelte new file mode 100644 index 000000000..a33c427b4 --- /dev/null +++ b/src/frontend/src/lib/components/ui/ButtonTableAction.svelte @@ -0,0 +1,24 @@ + + + + + diff --git a/src/frontend/src/lib/i18n/en.json b/src/frontend/src/lib/i18n/en.json index 162f47520..217b6ff6e 100644 --- a/src/frontend/src/lib/i18n/en.json +++ b/src/frontend/src/lib/i18n/en.json @@ -116,7 +116,7 @@ "unselect_all": "Unselect all segments", "profile": "Profile:", "segments": "Segments:", - "name_placeholder": "An optional hint about the controller" + "profile_placeholder": "An optional hint about the controller" }, "errors": { "no_mission_control": "Mission control center is not initialized.", @@ -159,10 +159,11 @@ "title": "Controllers", "profile": "Profile", "delete": "Delete a controller", + "info": "Information", "delete_question": "Delete selected controller?", - "controller_id": "ID: {0}", - "no_delete": "Juno cannot operates without your current signed-in identity and your mission control set as controllers. Therefore these cannot be deleted in the console.", - "more_delete": "You can delete additional controllers e.g. those you add for use in non-interactive environments." + "controller_id": "ID", + "no_delete": "Juno cannot operate without your current signed-in identity and your mission control set as controllers. These settings cannot be edited through the console.", + "more_delete": "However, you can edit or delete additional controllers that you may have added for non-interactive environments." }, "collections": { "title": "Collections", diff --git a/src/frontend/src/lib/types/controlers.ts b/src/frontend/src/lib/types/controlers.ts new file mode 100644 index 000000000..bcdfb0435 --- /dev/null +++ b/src/frontend/src/lib/types/controlers.ts @@ -0,0 +1,4 @@ +export type SetControllerParams = { + controllerId: string; + profile: string | null | undefined; +}; diff --git a/src/frontend/src/lib/types/i18n.d.ts b/src/frontend/src/lib/types/i18n.d.ts index 191df8f8d..cc1e82f7c 100644 --- a/src/frontend/src/lib/types/i18n.d.ts +++ b/src/frontend/src/lib/types/i18n.d.ts @@ -129,7 +129,7 @@ interface I18nCli { unselect_all: string; profile: string; segments: string; - name_placeholder: string; + profile_placeholder: string; } interface I18nErrors { @@ -177,6 +177,7 @@ interface I18nControllers { title: string; profile: string; delete: string; + info: string; delete_question: string; controller_id: string; no_delete: string; diff --git a/src/frontend/src/lib/utils/controllers.utils.ts b/src/frontend/src/lib/utils/controllers.utils.ts new file mode 100644 index 000000000..3dc7f3c4b --- /dev/null +++ b/src/frontend/src/lib/utils/controllers.utils.ts @@ -0,0 +1,10 @@ +import type { SetController as SetControllerMissionControl } from '$declarations/mission_control/mission_control.did'; +import { toNullable } from '$lib/utils/did.utils'; +import { nonNullish } from '$lib/utils/utils'; + +export const toSetController = ( + profile: string | null | undefined +): SetControllerMissionControl => ({ + metadata: nonNullish(profile) && profile !== '' ? [['profile', profile]] : [], + expires_at: toNullable(undefined) +}); diff --git a/src/frontend/src/routes/(sub)/cli/+page.svelte b/src/frontend/src/routes/(sub)/cli/+page.svelte index fa2c964bc..0dd47b66d 100644 --- a/src/frontend/src/routes/(sub)/cli/+page.svelte +++ b/src/frontend/src/routes/(sub)/cli/+page.svelte @@ -213,7 +213,7 @@ diff --git a/src/mission_control/src/controllers/mission_control.rs b/src/mission_control/src/controllers/mission_control.rs index aa7633b97..36a7a4e94 100644 --- a/src/mission_control/src/controllers/mission_control.rs +++ b/src/mission_control/src/controllers/mission_control.rs @@ -2,7 +2,7 @@ use crate::controllers::store::{delete_controllers, get_controllers, set_control use crate::store::get_user; use ic_cdk::id; use shared::constants::MAX_NUMBER_OF_MISSION_CONTROL_CONTROLLERS; -use shared::controllers::into_controller_ids; +use shared::controllers::{assert_max_number_of_controllers, into_controller_ids}; use shared::ic::update_canister_controllers; use shared::types::interface::SetController; use shared::types::state::{ControllerId, Controllers}; @@ -11,14 +11,11 @@ pub async fn set_mission_control_controllers( controllers: &[ControllerId], controller: &SetController, ) -> Result<(), String> { - let current_controllers = get_controllers(); - - if current_controllers.len() >= MAX_NUMBER_OF_MISSION_CONTROL_CONTROLLERS { - return Err(format!( - "Maximum number of controllers ({}) is already reached.", - MAX_NUMBER_OF_MISSION_CONTROL_CONTROLLERS - )); - } + assert_max_number_of_controllers( + &get_controllers(), + controllers, + MAX_NUMBER_OF_MISSION_CONTROL_CONTROLLERS, + )?; set_controllers(controllers, controller); diff --git a/src/satellite/src/lib.rs b/src/satellite/src/lib.rs index fd3df20ae..24478445f 100644 --- a/src/satellite/src/lib.rs +++ b/src/satellite/src/lib.rs @@ -55,7 +55,7 @@ use ic_cdk::storage::{stable_restore, stable_save}; use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; use rules::constants::DEFAULT_DB_COLLECTIONS; use shared::constants::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; -use shared::controllers::init_controllers; +use shared::controllers::{assert_max_number_of_controllers, init_controllers}; use shared::types::interface::{DeleteControllersArgs, SatelliteArgs, SetControllersArgs}; use shared::types::state::Controllers; use std::cell::RefCell; @@ -234,13 +234,14 @@ fn set_controllers( controller, }: SetControllersArgs, ) -> Controllers { - let current_controllers = get_controllers(); + let max_controllers = assert_max_number_of_controllers( + &get_controllers(), + &controllers, + MAX_NUMBER_OF_SATELLITE_CONTROLLERS, + ); - if current_controllers.len() >= MAX_NUMBER_OF_SATELLITE_CONTROLLERS { - trap(&format!( - "Maximum number of controllers ({}) is already reached.", - MAX_NUMBER_OF_SATELLITE_CONTROLLERS - )); + if let Err(err) = max_controllers { + trap(&err) } set_controllers_store(&controllers, &controller); diff --git a/src/shared/src/controllers.rs b/src/shared/src/controllers.rs index f2ef28a08..a2fffbcae 100644 --- a/src/shared/src/controllers.rs +++ b/src/shared/src/controllers.rs @@ -64,3 +64,26 @@ pub fn into_controller_ids(controllers: &Controllers) -> Vec { .map(|(controller_id, _)| controller_id) .collect::>() } + +pub fn assert_max_number_of_controllers( + current_controllers: &Controllers, + controllers_ids: &[ControllerId], + max_controllers: usize, +) -> Result<(), String> { + let current_controller_ids = into_controller_ids(current_controllers); + + let new_controller_ids = controllers_ids.iter().copied().filter(|id| { + !current_controller_ids + .iter() + .any(|current_id| current_id == id) + }); + + if current_controller_ids.len() + new_controller_ids.count() > max_controllers { + return Err(format!( + "Maximum number of controllers ({}) is already reached.", + max_controllers + )); + } + + Ok(()) +}