Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nfebe
Copy link
Contributor

@nfebe nfebe commented Nov 15, 2024

  • 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???)

Resolves : #46528

Screenshot

Screenshot from 2024-11-15 13-14-35

Checklist

  • Tests

- 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]>
@nfebe nfebe force-pushed the feat/46528/ask-confirm-extension-change branch from 04da216 to 9502ffd Compare November 15, 2024 12:20

return new Promise((resolve) => {
const dialog = new DialogBuilder()
.setName(t('files', 'Extension Change Warning'))
Copy link
Member

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 :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.setName(t('files', 'Extension Change Warning'))
.setName(t('files', 'Change file extension'))

A heading describing the content of the dialog?

Comment on lines +31 to +36
.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') },
))
Copy link
Member

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 :)

Copy link
Member

@marcoambrosini marcoambrosini left a 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?',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'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.',

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why data loss?

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor Author

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.

Suggested change
'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

Copy link
Contributor

@susnux susnux Nov 15, 2024

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'),
Copy link
Member

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'),
Copy link
Member

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}

@susnux
Copy link
Contributor

susnux commented Nov 15, 2024

I would then maybe make the keep action primary instead of the renaming one

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.

Comment on lines +22 to +63
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()
})
})
}
Copy link
Contributor

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:

Suggested change
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
}
}

Copy link
Contributor

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())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, thanks...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

When changing file extension ask for confirmation
4 participants