8484 {{ t('guests', 'Limit guest access to an app\'s allowlist') }}
8585 </NcCheckboxRadioSwitch >
8686
87- <p v-if =" config.useWhitelist" class =" allowlist" >
88- <NcSelect
89- v-model =" config .whitelist "
90- :options =" config .whiteListableApps "
91- :multiple =" true "
92- :closeOnSelect =" false "
93- :clearSearchOnSelect =" false "
94- @input =" saveConfig " />
87+ <div v-if =" config.useWhitelist" class =" allowlist" >
88+ <!-- Read-only display with an edit (pencil) button -->
89+ <div v-if =" !editingAllowlist" class =" allowlist__row allowlist__row--display" >
90+ <ul v-if =" allowlistApps.length" class =" allowlist__apps" >
91+ <li v-for =" app in allowlistApps" :key =" app" class =" allowlist__app" >
92+ {{ app }}
93+ </li >
94+ </ul >
95+ <span v-else class =" allowlist__empty" >{{ t('guests', 'No apps selected') }}</span >
96+ <NcButton
97+ variant="tertiary"
98+ :aria-label =" t (' guests' , ' Edit allowlist' )"
99+ :title =" t (' guests' , ' Edit allowlist' )"
100+ @click =" startEditAllowlist " >
101+ <template #icon >
102+ <Pencil :size =" 20 " />
103+ </template >
104+ </NcButton >
105+ </div >
106+ <!-- Edit mode: changes are only persisted when confirmed -->
107+ <div v-else class =" allowlist__row" >
108+ <NcSelect
109+ v-model =" whitelistDraft "
110+ class="allowlist__select"
111+ :options =" config .whiteListableApps "
112+ :multiple =" true "
113+ keepOpen />
114+ <NcButton
115+ variant="tertiary"
116+ :aria-label =" t (' guests' , ' Confirm allowlist' )"
117+ :title =" t (' guests' , ' Confirm allowlist' )"
118+ @click =" confirmAllowlist " >
119+ <template #icon >
120+ <Check :size =" 20 " />
121+ </template >
122+ </NcButton >
123+ </div >
95124 <NcButton variant="secondary" class="reset-button" @click =" reset " >
96125 <template #icon >
97126 <History :size =" 16 " />
98127 </template >
99128 {{ t('guests', 'Reset allowlist') }}
100129 </NcButton >
101- </p >
130+ </div >
102131 </div >
103132 <div v-if =" !loaded" >
104133 <div class =" loading" />
@@ -121,13 +150,16 @@ import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
121150import NcSelect from ' @nextcloud/vue/components/NcSelect'
122151import NcSettingsSection from ' @nextcloud/vue/components/NcSettingsSection'
123152import NcSettingsSelectGroup from ' @nextcloud/vue/components/NcSettingsSelectGroup'
153+ import Check from ' vue-material-design-icons/Check.vue'
124154import History from ' vue-material-design-icons/History.vue'
155+ import Pencil from ' vue-material-design-icons/Pencil.vue'
125156import GuestList from ' ../components/GuestList.vue'
126157import { logger } from ' ../services/logger.ts'
127158
128159export default {
129160 name: ' GuestSettings' ,
130161 components: {
162+ Check,
131163 GuestList,
132164 History ,
133165 NcButton,
@@ -136,6 +168,7 @@ export default {
136168 NcSelect,
137169 NcSettingsSection,
138170 NcSettingsSelectGroup,
171+ Pencil,
139172 },
140173
141174 data () {
@@ -145,6 +178,8 @@ export default {
145178 saved: false ,
146179 saving: false ,
147180 savingTimeout: null ,
181+ editingAllowlist: false ,
182+ whitelistDraft: [],
148183 config: {
149184 useWhitelist: false ,
150185 allowExternalStorage: false ,
@@ -159,6 +194,15 @@ export default {
159194 },
160195
161196 computed: {
197+ allowlistApps () {
198+ // getAppWhitelist() returns [''] for an empty allowlist (explode of an
199+ // empty string), so drop empty entries to show the placeholder instead
200+ // of a blank chip.
201+ return (this .config .whitelist ?? [])
202+ .map ((app ) => (typeof app === ' string' ? app : (app .label ?? app .id ?? app .name ?? String (app))))
203+ .filter (Boolean )
204+ },
205+
162206 statusText () {
163207 if (this .error ) {
164208 return t (' guests' , ' Error' )
@@ -180,6 +224,19 @@ export default {
180224 methods: {
181225 t,
182226
227+ startEditAllowlist () {
228+ // Drop the empty entry from an empty allowlist so the selector does
229+ // not start with a blank, removable tag.
230+ this .whitelistDraft = (this .config .whitelist ?? []).filter (Boolean )
231+ this .editingAllowlist = true
232+ },
233+
234+ confirmAllowlist () {
235+ this .config .whitelist = [... this .whitelistDraft ]
236+ this .editingAllowlist = false
237+ this .saveConfig ()
238+ },
239+
183240 async loadConfig () {
184241 const { data } = await axios .get (generateUrl (' apps/guests/config' ))
185242 this .config = data
@@ -208,6 +265,9 @@ export default {
208265 try {
209266 const { data } = await axios .post (generateUrl (' apps/guests/whitelist/reset' ))
210267 this .config .whitelist = data .whitelist
268+ if (this .editingAllowlist ) {
269+ this .whitelistDraft = [... data .whitelist ]
270+ }
211271 this .saved = true
212272 } catch (error) {
213273 this .error = true
@@ -266,12 +326,46 @@ export default {
266326}
267327
268328.allowlist {
269- max-width : 500px ;
329+ max- width: 690px ;
330+ margin- inline- start: var (-- default- clickable- area);
270331
271- .multiselect {
272- width : calc (100% - 48px );
273- margin-right : 0 ;
332+ & __row {
333+ display: flex;
334+ align- items: center;
335+ gap: 8px ;
274336 margin- top: 1rem ;
337+
338+ & -- display {
339+ align- items: flex- start;
340+ }
341+ }
342+
343+ & __apps {
344+ flex: 1 1 auto;
345+ display: flex;
346+ flex- wrap: wrap;
347+ gap: 4px ;
348+ min- width: 0 ;
349+ padding- top: 4px ;
350+ }
351+
352+ & __app {
353+ padding: 2px 10px ;
354+ background- color: var (-- color- background- dark);
355+ border- radius: var (-- border- radius- pill, 1rem );
356+ color: var (-- color- text- maxcontrast);
357+ font- size: 0 .9em ;
358+ }
359+
360+ & __empty {
361+ flex: 1 1 auto;
362+ padding- top: 6px ;
363+ color: var (-- color- text- maxcontrast);
364+ }
365+
366+ & __select {
367+ flex: 1 1 auto;
368+ min- width: 0 ;
275369 }
276370
277371 .reset - button {
0 commit comments