Skip to content

Commit 22b81cd

Browse files
author
fabienSvstr
committed
[MS] Update workspace context menu to hide/unhide "Mount" and "Unmount" based on workspace hidden state
1 parent 6f31624 commit 22b81cd

File tree

8 files changed

+185
-6
lines changed

8 files changed

+185
-6
lines changed

client/src/components/workspaces/utils.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import {
1010
WorkspaceRole,
1111
getClientProfile,
1212
getSystemPath,
13+
mountWorkspace,
1314
getPathLink as parsecGetPathLink,
1415
renameWorkspace as parsecRenameWorkspace,
16+
unmountWorkspace,
1517
} from '@/parsec';
1618
import { Routes, navigateTo } from '@/router';
1719
import { EventDistributor } from '@/services/eventDistributor';
@@ -138,6 +140,7 @@ export async function openWorkspaceContextMenu(
138140
clientProfile: clientProfile,
139141
clientRole: workspace.currentSelfRole,
140142
isFavorite: workspaceAttributes.isFavorite(workspace.id),
143+
isHidden: workspaceAttributes.isHidden(workspace.id),
141144
},
142145
});
143146

@@ -156,6 +159,7 @@ export async function openWorkspaceContextMenu(
156159
clientProfile: clientProfile,
157160
clientRole: workspace.currentSelfRole,
158161
isFavorite: workspaceAttributes.isFavorite(workspace.id),
162+
isHidden: workspaceAttributes.isHidden(workspace.id),
159163
},
160164
});
161165

@@ -184,12 +188,84 @@ export async function openWorkspaceContextMenu(
184188
case WorkspaceAction.ShowHistory:
185189
await navigateTo(Routes.History, { query: { documentPath: '/', workspaceHandle: workspace.handle } });
186190
break;
191+
case WorkspaceAction.Mount:
192+
await showWorkspace(workspace, workspaceAttributes, informationManager);
193+
break;
194+
case WorkspaceAction.UnMount:
195+
await hideWorkspace(workspace, workspaceAttributes, informationManager);
196+
break;
187197
default:
188198
console.warn('No WorkspaceAction match found');
189199
}
190200
}
191201
}
192202

203+
export async function showWorkspace(
204+
workspace: WorkspaceInfo,
205+
workspaceAttributes: WorkspaceAttributes,
206+
informationManager: InformationManager,
207+
): Promise<void> {
208+
const result = await mountWorkspace(workspace.handle);
209+
210+
if (result.ok) {
211+
workspaceAttributes.removeHidden(workspace.id);
212+
informationManager.present(
213+
new Information({
214+
message: {
215+
key: 'WorkspacesPage.showHideWorkspace.successShown',
216+
data: { workspace: workspace.currentName },
217+
},
218+
level: InformationLevel.Success,
219+
}),
220+
PresentationMode.Toast,
221+
);
222+
} else {
223+
informationManager.present(
224+
new Information({
225+
message: {
226+
key: 'WorkspacesPage.showHideWorkspace.failedShown',
227+
data: { workspace: workspace.currentName },
228+
},
229+
level: InformationLevel.Error,
230+
}),
231+
PresentationMode.Toast,
232+
);
233+
}
234+
}
235+
236+
export async function hideWorkspace(
237+
workspace: WorkspaceInfo,
238+
workspaceAttributes: WorkspaceAttributes,
239+
informationManager: InformationManager,
240+
): Promise<void> {
241+
const result = await unmountWorkspace(workspace);
242+
243+
if (result.ok) {
244+
workspaceAttributes.addHidden(workspace.id);
245+
informationManager.present(
246+
new Information({
247+
message: {
248+
key: 'WorkspacesPage.showHideWorkspace.successHidden',
249+
data: { workspace: workspace.currentName },
250+
},
251+
level: InformationLevel.Success,
252+
}),
253+
PresentationMode.Toast,
254+
);
255+
} else {
256+
informationManager.present(
257+
new Information({
258+
message: {
259+
key: 'WorkspacesPage.showHideWorkspace.failedHidden',
260+
data: { workspace: workspace.currentName },
261+
},
262+
level: InformationLevel.Error,
263+
}),
264+
PresentationMode.Toast,
265+
);
266+
}
267+
}
268+
193269
async function openWorkspace(workspace: WorkspaceInfo, informationManager: InformationManager): Promise<void> {
194270
const result = await getSystemPath(workspace.handle, '/');
195271

client/src/locales/en-US.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,8 @@
646646
"actionHistory": "History",
647647
"actionShare": "Sharing and roles",
648648
"actionDetails": "Details",
649+
"actionHide": "Hide this workspace",
650+
"actionShow": "Show this workspace",
649651
"actionCopyLink": "Copy link",
650652
"actionAddFavorite": "Pin",
651653
"actionRemoveFavorite": "Unpin"
@@ -695,6 +697,13 @@
695697
"filter": {
696698
"title": "Filter",
697699
"roles": "Role"
700+
},
701+
"showHideWorkspace": {
702+
"failedStarted": "Failed to get started workspace information. Please try again.",
703+
"successHidden": "The workspace is now hidden and will no longer appear in your explorer.",
704+
"failedHidden": "Failed to hide the workspace. Please try again.",
705+
"successShown": "The workspace is now visible in Parsec and your explorer.",
706+
"failedShown": "Failed to show the workspace. Please try again."
698707
}
699708
},
700709
"FoldersPage": {

client/src/locales/fr-FR.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,8 @@
646646
"actionShare": "Partage et rôles",
647647
"actionDetails": "Détails",
648648
"actionCopyLink": "Copier le lien",
649+
"actionHide": "Masquer cet espace",
650+
"actionShow": "Afficher cet espace",
649651
"actionAddFavorite": "Épingler",
650652
"actionRemoveFavorite": "Désépingler"
651653
},
@@ -694,6 +696,13 @@
694696
"filter": {
695697
"title": "Filtrer",
696698
"roles": "Rôle"
699+
},
700+
"showHideWorkspace": {
701+
"failedStarted": "Impossible d'obtenir les informations relatives à l'espace de travail. Veuillez réessayer.",
702+
"successHidden": "L'espace de travail est maintenant masqué et n'apparaîtra plus dans votre explorateur.",
703+
"failedHidden": "Impossible de masquer l'espace de travail. Veuillez réessayer.",
704+
"successShown": "L'espace de travail est maintenant visible dans Parsec et votre explorateur.",
705+
"failedShown": "Impossible d'afficher l'espace de travail. Veuillez réessayer."
697706
}
698707
},
699708
"FoldersPage": {

client/src/parsec/workspace.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
WorkspaceRole,
3333
} from '@/parsec/types';
3434
import { generateNoHandleError } from '@/parsec/utils';
35-
import { WorkspaceStopError, libparsec } from '@/plugins/libparsec';
35+
import { MountpointUnmountError, WorkspaceStopError, libparsec } from '@/plugins/libparsec';
3636
import { getConnectionHandle } from '@/router';
3737
import { DateTime } from 'luxon';
3838

@@ -240,7 +240,7 @@ export async function mountWorkspace(
240240
const startedWorkspaceResult = await getWorkspaceInfo(workspaceHandle);
241241
if (!startedWorkspaceResult.ok) {
242242
console.error(`Failed to get started workspace info: ${startedWorkspaceResult.error}`);
243-
return { ok: false, error: { tag: WorkspaceMountErrorTag.Internal, error: '' } };
243+
return { ok: false, error: { tag: WorkspaceMountErrorTag.Internal, error: startedWorkspaceResult.error.error } };
244244
} else {
245245
if (startedWorkspaceResult.value.mountpoints.length > 0) {
246246
return { ok: true, value: startedWorkspaceResult.value.mountpoints[0] };
@@ -249,6 +249,25 @@ export async function mountWorkspace(
249249
return await libparsec.workspaceMount(workspaceHandle);
250250
}
251251

252+
export async function unmountWorkspace(workspace: WorkspaceInfo): Promise<Result<null, MountpointUnmountError>> {
253+
let error: MountpointUnmountError | null = null;
254+
255+
for (let i = workspace.mountpoints.length - 1; i >= 0; i--) {
256+
const result = await libparsec.mountpointUnmount(workspace.mountpoints[i][0]);
257+
if (result.ok) {
258+
workspace.mountpoints.splice(i, 1);
259+
} else {
260+
error = result.error;
261+
}
262+
}
263+
264+
if (error) {
265+
return generateNoHandleError<MountpointUnmountError>();
266+
}
267+
268+
return { ok: true, value: null };
269+
}
270+
252271
export async function getPathLink(
253272
workspaceHandle: WorkspaceHandle,
254273
path: string,

client/src/views/workspaces/SmallDisplayWorkspaceContextMenu.vue

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@
4848
@click="onClick(WorkspaceAction.OpenInExplorer)"
4949
class="ion-no-padding list-group-item"
5050
>
51-
<ion-icon :icon="open" />
51+
<ion-icon
52+
class="list-group-item__icon"
53+
:icon="open"
54+
/>
5255
<ion-text class="button-large list-group-item__label-small">
5356
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionOpenInExplorer') }}
5457
</ion-text>
@@ -83,6 +86,36 @@
8386
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionDetails') }}
8487
</ion-text>
8588
</ion-item>
89+
90+
<ion-item
91+
button
92+
v-show="isDesktop() && !isHidden"
93+
@click="onClick(WorkspaceAction.UnMount)"
94+
class="ion-no-padding list-group-item"
95+
>
96+
<ion-icon
97+
class="list-group-item__icon"
98+
:icon="eyeOff"
99+
/>
100+
<ion-text class="button-large list-group-item__label-small">
101+
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionHide') }}
102+
</ion-text>
103+
</ion-item>
104+
105+
<ion-item
106+
button
107+
v-show="isDesktop() && isHidden"
108+
@click="onClick(WorkspaceAction.Mount)"
109+
class="ion-no-padding list-group-item"
110+
>
111+
<ion-icon
112+
class="list-group-item__icon"
113+
:icon="eye"
114+
/>
115+
<ion-text class="button-large list-group-item__label-small">
116+
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionShow') }}
117+
</ion-text>
118+
</ion-item>
86119
</ion-item-group>
87120
<ion-item-group class="list-group">
88121
<ion-item
@@ -144,7 +177,7 @@
144177
import { UserProfile, WorkspaceName, WorkspaceRole, isDesktop } from '@/parsec';
145178
import { WorkspaceAction } from '@/views/workspaces/types';
146179
import { IonContent, IonIcon, IonItem, IonItemGroup, IonList, IonPage, IonText, modalController } from '@ionic/vue';
147-
import { cloudy, informationCircle, link, open, shareSocial, star, time } from 'ionicons/icons';
180+
import { cloudy, eye, eyeOff, informationCircle, link, open, shareSocial, star, time } from 'ionicons/icons';
148181
import { MsImage, RenameIcon } from 'megashark-lib';
149182
150183
function onClick(action: WorkspaceAction): Promise<boolean> {
@@ -156,6 +189,7 @@ defineProps<{
156189
clientProfile: UserProfile;
157190
clientRole: WorkspaceRole;
158191
isFavorite: boolean;
192+
isHidden: boolean;
159193
}>();
160194
</script>
161195

client/src/views/workspaces/WorkspaceContextMenu.vue

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,35 @@
106106
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionDetails') }}
107107
</ion-text>
108108
</ion-item>
109+
110+
<ion-item
111+
button
112+
v-show="isDesktop() && !isHidden"
113+
@click="onClick(WorkspaceAction.UnMount)"
114+
class="ion-no-padding list-group-item"
115+
>
116+
<ion-icon
117+
class="list-group-item__icon"
118+
:icon="eyeOff"
119+
/>
120+
<ion-text class="body list-group-item__label">
121+
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionHide') }}
122+
</ion-text>
123+
</ion-item>
124+
<ion-item
125+
button
126+
v-show="isDesktop() && isHidden"
127+
@click="onClick(WorkspaceAction.Mount)"
128+
class="ion-no-padding list-group-item"
129+
>
130+
<ion-icon
131+
class="list-group-item__icon"
132+
:icon="eye"
133+
/>
134+
<ion-text class="body list-group-item__label">
135+
{{ $msTranslate('WorkspacesPage.workspaceContextMenu.actionShow') }}
136+
</ion-text>
137+
</ion-item>
109138
</ion-item-group>
110139
<ion-item-group class="list-group">
111140
<ion-item class="list-group-title button-small">
@@ -176,7 +205,7 @@
176205
import { UserProfile, WorkspaceName, WorkspaceRole, isDesktop } from '@/parsec';
177206
import { WorkspaceAction } from '@/views/workspaces/types';
178207
import { IonContent, IonIcon, IonItem, IonItemGroup, IonList, IonText, popoverController } from '@ionic/vue';
179-
import { cloudy, informationCircle, link, open, shareSocial, star, time } from 'ionicons/icons';
208+
import { cloudy, eye, eyeOff, informationCircle, link, open, shareSocial, star, time } from 'ionicons/icons';
180209
import { MsImage, RenameIcon } from 'megashark-lib';
181210
182211
function onClick(action: WorkspaceAction): Promise<boolean> {
@@ -188,6 +217,7 @@ defineProps<{
188217
clientProfile: UserProfile;
189218
clientRole: WorkspaceRole;
190219
isFavorite: boolean;
220+
isHidden: boolean;
191221
}>();
192222
</script>
193223

client/src/views/workspaces/WorkspacesPage.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ async function refreshWorkspacesList(): Promise<void> {
439439
querying.value = true;
440440
window.electronAPI.log('debug', 'Starting Parsec list workspaces');
441441
const result = await parsecListWorkspaces();
442+
const hidden = workspaceAttributes.getHidden();
442443
if (result.ok) {
443444
for (const wk of result.value) {
444445
window.electronAPI.log('debug', `Processing workspace: ${wk.currentName}`);
@@ -448,7 +449,7 @@ async function refreshWorkspacesList(): Promise<void> {
448449
} else {
449450
window.electronAPI.log('warn', `Failed to get sharing for ${wk.currentName}`);
450451
}
451-
if (isDesktop() && wk.mountpoints.length === 0) {
452+
if (isDesktop() && wk.mountpoints.length === 0 && !hidden.value.includes(wk.id)) {
452453
const mountResult = await parsecMountWorkspace(wk.handle);
453454
if (mountResult.ok) {
454455
wk.mountpoints.push(mountResult.value);

client/src/views/workspaces/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum WorkspaceAction {
1010
ShowHistory = 'workspace-show-history',
1111
OpenInExplorer = 'workspace-open-in-explorer',
1212
Mount = 'workspace-mount',
13+
UnMount = 'workspace-unmount',
1314
Favorite = 'workspace-favorite',
1415
}
1516

0 commit comments

Comments
 (0)