-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add confirmation dialog for file extension changes #49308
base: master
Are you sure you want to change the base?
Conversation
- Introduced a dialog to confirm if users want to proceed with changing the file extension. - Added handling for dialog visibility to prevent recursion. (Since it looks like use must press escape to stop rename???) Signed-off-by: nfebe <[email protected]>
04da216
to
9502ffd
Compare
|
||
return new Promise((resolve) => { | ||
const dialog = new DialogBuilder() | ||
.setName(t('files', 'Extension Change Warning')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too technical, this should be more people friendly :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.setName(t('files', 'Extension Change Warning')) | |
.setName(t('files', 'Change file extension')) |
A heading describing the content of the dialog?
.setName(t('files', 'Extension Change Warning')) | ||
.setText(t( | ||
'files', | ||
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | ||
{ old: oldExtension || t('files', 'none'), new: newExtension || t('files', 'none') }, | ||
)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nextcloud/designers, please suggest nice messages which you're ok with :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would then maybe make the keep action primary instead of the renaming one
.setName(t('files', 'Extension Change Warning')) | ||
.setText(t( | ||
'files', | ||
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | |
'Changing the file extension from from "{old}" to "{new}" may result in data loss or formatting issues.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why data loss
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meaning I would stick close to what macos is doing "may open in another app" as this it what will happen, it will be opened with another app which might not be able to read the file, but the data is still there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But at least I would make the text condition as "from 'none' to '...'" sounds weird.
E.g.:
- If oldExtension is unset: 'Changing the file extension to "{new}" may result in ...'
- If newExtension is unset: 'Removing the file extension "{old}" may result in ...'
- otherwise: like @marcoambrosini suggests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @susnux here, it not true that changing the file extension can result to data loss. (At least not directly).
It can lead to truncation if the file is opened with another program AND the program makes a damaging update to the binary data in the file.
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | |
'Changing the file extension from extension from "{old}" to "{new}" may affect how the file is handled.', |
or maybe just the same "open in another application" in the ticket.
cc: @marcoambrosini
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense maybe extend it to how and with which app the file is handled.
}, | ||
}, | ||
{ | ||
label: t('files', 'Proceed'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Text of button: Use {newextension}
)) | ||
.setButtons([ | ||
{ | ||
label: t('files', 'Cancel'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Text of button Keep {oldextension}
It is not destructive (you can revert it) and initialized by the user so as a user I would expect my initiated action to be the primary one. |
const showWarningDialog = (oldExtension: string, newExtension: string): Promise<boolean> => { | ||
if (isDialogVisible) { | ||
return Promise.resolve(false) | ||
} | ||
|
||
isDialogVisible = true | ||
|
||
return new Promise((resolve) => { | ||
const dialog = new DialogBuilder() | ||
.setName(t('files', 'Extension Change Warning')) | ||
.setText(t( | ||
'files', | ||
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | ||
{ old: oldExtension || t('files', 'none'), new: newExtension || t('files', 'none') }, | ||
)) | ||
.setButtons([ | ||
{ | ||
label: t('files', 'Cancel'), | ||
icon: IconCancel, | ||
type: 'secondary', | ||
callback: () => { | ||
isDialogVisible = false | ||
resolve(false) | ||
}, | ||
}, | ||
{ | ||
label: t('files', 'Proceed'), | ||
icon: IconCheck, | ||
type: 'primary', | ||
callback: () => { | ||
isDialogVisible = false | ||
resolve(true) | ||
}, | ||
}, | ||
]) | ||
.build() | ||
|
||
dialog.show().then(() => { | ||
dialog.hide() | ||
}) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regardless of wording I think that this is a bit too nested, just make it async instead and you can reduce the nesting:
const showWarningDialog = (oldExtension: string, newExtension: string): Promise<boolean> => { | |
if (isDialogVisible) { | |
return Promise.resolve(false) | |
} | |
isDialogVisible = true | |
return new Promise((resolve) => { | |
const dialog = new DialogBuilder() | |
.setName(t('files', 'Extension Change Warning')) | |
.setText(t( | |
'files', | |
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | |
{ old: oldExtension || t('files', 'none'), new: newExtension || t('files', 'none') }, | |
)) | |
.setButtons([ | |
{ | |
label: t('files', 'Cancel'), | |
icon: IconCancel, | |
type: 'secondary', | |
callback: () => { | |
isDialogVisible = false | |
resolve(false) | |
}, | |
}, | |
{ | |
label: t('files', 'Proceed'), | |
icon: IconCheck, | |
type: 'primary', | |
callback: () => { | |
isDialogVisible = false | |
resolve(true) | |
}, | |
}, | |
]) | |
.build() | |
dialog.show().then(() => { | |
dialog.hide() | |
}) | |
}) | |
} | |
async function showWarningDialog(oldExtension: string, newExtension: string): Promise<boolean> { | |
if (isDialogVisible) { | |
return false | |
} | |
let response = false | |
isDialogVisible = true | |
const dialog = new DialogBuilder() | |
.setName(t('files', 'Extension Change Warning')) | |
.setText(t( | |
'files', | |
'You are attempting to change the file extension from "{old}" to "{new}". This may affect how the file is handled. Do you want to proceed?', | |
{ old: oldExtension || t('files', 'none'), new: newExtension || t('files', 'none') }, | |
)) | |
.setButtons([ | |
{ | |
label: t('files', 'Cancel'), | |
icon: IconCancel, | |
type: 'secondary', | |
callback: () => { | |
response = false | |
}, | |
}, | |
{ | |
label: t('files', 'Proceed'), | |
icon: IconCheck, | |
type: 'primary', | |
callback: () => { | |
response = true | |
}, | |
}, | |
]) | |
.build() | |
try { | |
await dialog.show() | |
return response | |
} catch (error) { | |
// dialog closed without response | |
return false | |
} finally { | |
dialogVisible = false | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW this also fixes the issue that there is a uncaught exception when closing the dialog with your code.
(Also this allows use to easier simplify this after nc-dialogs got the latest nc-vue changes which allow to return from button callbacks and get the returned value back in await dialog.show()
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, thanks...
Resolves : #46528
Screenshot
Checklist