WIP - remove upper() calls from layout

obrusvit 1 month ago
parent bde7a99a82
commit 11af0ac2d3

@ -253,6 +253,10 @@ impl<'a> OpTextLayout<'a> {
self.font(Font::BOLD).text(text.into())
}
pub fn text_bold_upper(self, text: impl Into<TString<'a>>) -> Self {
self.font(Font::BOLD_UPPER).text(text.into())
}
pub fn text_demibold(self, text: impl Into<TString<'a>>) -> Self {
self.font(Font::DEMIBOLD).text(text.into())
}

@ -44,7 +44,7 @@ where
}
pub fn center_bold(text: T) -> Self {
Self::new(text, Font::BOLD, Alignment::Center)
Self::new(text, Font::BOLD_UPPER, Alignment::Center)
}
/// Not showing ellipsis at the beginning of longer texts.

@ -18,8 +18,8 @@ pub const FG: Color = WHITE; // Default foreground (text & icon) color.
pub const BG: Color = BLACK; // Default background color.
// Font constants.
pub const FONT_BUTTON: Font = Font::NORMAL;
pub const FONT_HEADER: Font = Font::BOLD;
pub const FONT_BUTTON: Font = Font::NORMAL_UPPER;
pub const FONT_HEADER: Font = Font::BOLD_UPPER;
pub const FONT_CHOICE_ITEMS: Font = Font::BIG;
// Text constants.

@ -158,7 +158,7 @@ pub const fn label_progress() -> TextStyle {
}
pub const fn label_title() -> TextStyle {
TextStyle::new(Font::BOLD, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT)
TextStyle::new(Font::BOLD_UPPER, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT)
}
pub const fn label_subtitle() -> TextStyle {
@ -166,13 +166,13 @@ pub const fn label_subtitle() -> TextStyle {
}
pub const fn label_coinjoin_progress() -> TextStyle {
TextStyle::new(Font::BOLD, FG, YELLOW, FG, FG)
TextStyle::new(Font::BOLD_UPPER, FG, YELLOW, FG, FG)
}
pub const fn button_default() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: GREY_DARK,
background_color: BG,
@ -181,7 +181,7 @@ pub const fn button_default() -> ButtonStyleSheet {
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: GREY_MEDIUM,
background_color: BG,
@ -190,7 +190,7 @@ pub const fn button_default() -> ButtonStyleSheet {
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: GREY_LIGHT,
button_color: GREY_DARK,
background_color: BG,
@ -204,7 +204,7 @@ pub const fn button_default() -> ButtonStyleSheet {
pub const fn button_confirm() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: GREEN,
background_color: BG,
@ -213,7 +213,7 @@ pub const fn button_confirm() -> ButtonStyleSheet {
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: GREEN_DARK,
background_color: BG,
@ -222,7 +222,7 @@ pub const fn button_confirm() -> ButtonStyleSheet {
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: GREY_LIGHT,
button_color: GREEN_DARK,
background_color: BG,
@ -236,7 +236,7 @@ pub const fn button_confirm() -> ButtonStyleSheet {
pub const fn button_cancel() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: RED,
background_color: BG,
@ -245,7 +245,7 @@ pub const fn button_cancel() -> ButtonStyleSheet {
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: RED_DARK,
background_color: BG,
@ -254,7 +254,7 @@ pub const fn button_cancel() -> ButtonStyleSheet {
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: GREY_LIGHT,
button_color: RED,
background_color: BG,
@ -272,7 +272,7 @@ pub const fn button_danger() -> ButtonStyleSheet {
pub const fn button_reset() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: YELLOW,
background_color: BG,
@ -281,7 +281,7 @@ pub const fn button_reset() -> ButtonStyleSheet {
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: YELLOW_DARK,
background_color: BG,
@ -290,7 +290,7 @@ pub const fn button_reset() -> ButtonStyleSheet {
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: YELLOW,
background_color: BG,
@ -304,7 +304,7 @@ pub const fn button_reset() -> ButtonStyleSheet {
pub const fn button_moreinfo() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: BG,
background_color: BG,
@ -313,7 +313,7 @@ pub const fn button_moreinfo() -> ButtonStyleSheet {
border_width: 2,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: BG,
background_color: BG,
@ -322,7 +322,7 @@ pub const fn button_moreinfo() -> ButtonStyleSheet {
border_width: 2,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: GREY_LIGHT,
button_color: BG,
background_color: BG,
@ -336,7 +336,7 @@ pub const fn button_moreinfo() -> ButtonStyleSheet {
pub const fn button_info() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: BLUE,
background_color: BG,
@ -345,7 +345,7 @@ pub const fn button_info() -> ButtonStyleSheet {
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: FG,
button_color: BLUE_DARK,
background_color: BG,
@ -354,7 +354,7 @@ pub const fn button_info() -> ButtonStyleSheet {
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
font: Font::BOLD_UPPER,
text_color: GREY_LIGHT,
button_color: BLUE,
background_color: BG,
@ -593,7 +593,7 @@ pub const fn loader_lock_icon() -> LoaderStyleSheet {
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD_UPPER, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, GREY_LIGHT, GREY_LIGHT)
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
.with_page_breaking(PageBreaking::CutAndInsertEllipsisBoth)
@ -629,7 +629,7 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle {
let font = Font::from_i32(-num);
match font {
Some(Font::DEMIBOLD) => &TEXT_DEMIBOLD,
Some(Font::BOLD) => &TEXT_BOLD,
Some(Font::BOLD_UPPER) => &TEXT_BOLD,
Some(Font::MONO) => &TEXT_MONO,
_ => &TEXT_NORMAL,
}

@ -58,13 +58,6 @@ def progress(
if description is None:
description = TR.progress__please_wait # def_arg
if title is not None:
title = title.upper()
elif not utils.MODEL_IS_T2B1:
# on TT, uppercase the description which ends up on top of the screen
# when no title is set
description = description.upper()
return RustProgress(
layout=trezorui2.show_progress(
description=description,

@ -277,7 +277,7 @@ def _placeholder_confirm(
verb = verb or TR.buttons__confirm # def_arg
return confirm_action(
br_type,
title.upper(),
title,
data,
description,
verb=verb,
@ -302,7 +302,7 @@ async def get_bool(
result = await interact(
RustLayout(
trezorui2.confirm_action(
title=title.upper(),
title=title,
action=data,
description=description,
verb=verb,
@ -340,9 +340,6 @@ def confirm_action(
br_code: ButtonRequestType = BR_TYPE_OTHER,
) -> Awaitable[None]:
verb = verb or TR.buttons__confirm # def_arg
if verb_cancel is not None:
verb_cancel = verb_cancel.upper()
if description is not None and description_param is not None:
description = description.format(description_param)
@ -350,10 +347,10 @@ def confirm_action(
interact(
RustLayout(
trezorui2.confirm_action(
title=title.upper(),
title=title,
action=action,
description=description,
verb=verb.upper(),
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
reverse=reverse,
@ -403,7 +400,7 @@ def confirm_reset_device(
interact(
RustLayout(
trezorui2.confirm_reset_device(
title=title.upper(),
title=title,
button=button,
)
),
@ -446,7 +443,7 @@ def confirm_path_warning(
title = f"{TR.words__unknown} {path_type if path_type else 'path'}"
return _placeholder_confirm(
"path_warning",
title.upper(),
title,
description=path,
br_code=ButtonRequestType.UnknownDerivationPath,
)
@ -546,7 +543,7 @@ async def show_address(
# User pressed left cancel button, show mismatch dialogue.
else:
result = await ctx_wait(
RustLayout(trezorui2.show_mismatch(title=mismatch_title.upper()))
RustLayout(trezorui2.show_mismatch(title=mismatch_title))
)
assert result in (CONFIRMED, CANCELLED)
# Right button aborts action, left goes back to showing address.
@ -567,7 +564,7 @@ def show_pubkey(
mismatch_title = mismatch_title or TR.addr_mismatch__key_mismatch # def_arg
return show_address(
address=pubkey,
title=title.upper(),
title=title,
account=account,
path=path,
br_type=br_type,
@ -589,7 +586,7 @@ def _show_modal(
) -> Awaitable[None]:
return confirm_action(
br_type,
header.upper(),
header,
subheader,
content,
verb=button_confirm or "",
@ -638,7 +635,7 @@ def show_warning(
return interact(
RustLayout(
trezorui2.show_warning( # type: ignore [Argument missing for parameter "title"]
button=button.upper(),
button=button,
warning=content, # type: ignore [No parameter named "warning"]
description=subheader or "",
)
@ -705,7 +702,7 @@ async def confirm_output(
trezorui2.confirm_output_address(
address=address,
address_label=address_label or "",
address_title=address_title.upper(),
address_title=address_title,
chunkify=chunkify,
)
),
@ -718,7 +715,7 @@ async def confirm_output(
result = await interact(
RustLayout(
trezorui2.confirm_output_amount(
amount_title=amount_title.upper(),
amount_title=amount_title,
amount=amount,
)
),
@ -776,11 +773,11 @@ async def should_show_more(
result = await interact(
RustLayout(
trezorui2.confirm_with_info(
title=title.upper(),
title=title,
items=para,
button=confirm.upper(),
button=confirm,
verb_cancel=verb_cancel, # type: ignore [No parameter named "verb_cancel"]
info_button=button_text.upper(), # unused on TR
info_button=button_text, # unused on TR
)
),
br_type,
@ -809,7 +806,6 @@ def confirm_blob(
chunkify: bool = False,
) -> Awaitable[None]:
verb = verb or TR.buttons__confirm # def_arg
title = title.upper()
layout = RustLayout(
trezorui2.confirm_blob(
title=title,
@ -889,7 +885,7 @@ def confirm_address(
) -> Awaitable[None]:
return confirm_blob(
br_type,
title.upper(),
title,
address,
description,
br_code=br_code,
@ -922,7 +918,7 @@ def confirm_amount(
description = description or f"{TR.words__amount}:" # def_arg
return confirm_blob(
br_type,
title.upper(),
title,
amount,
description,
br_code=br_code,
@ -951,7 +947,7 @@ def confirm_properties(
interact(
RustLayout(
trezorui2.confirm_properties(
title=title.upper(),
title=title,
items=map(handle_bytes, props), # type: ignore [cannot be assigned to parameter "items"]
hold=hold,
)
@ -979,15 +975,12 @@ async def confirm_value(
if not verb and not hold:
raise ValueError("Either verb or hold=True must be set")
if verb:
verb = verb.upper()
if info_items is None:
return await raise_if_not_confirmed(
interact(
RustLayout(
trezorui2.confirm_value( # type: ignore [Argument missing for parameter "subtitle"]
title=title.upper(),
title=title,
description=description,
value=value,
verb=verb or TR.buttons__hold_to_confirm,
@ -1007,7 +1000,7 @@ async def confirm_value(
while True:
should_show_more_layout = RustLayout(
trezorui2.confirm_with_info(
title=title.upper(),
title=title,
items=((ui.NORMAL, value),),
button=verb or TR.buttons__confirm,
info_button=TR.buttons__info,
@ -1031,7 +1024,7 @@ async def confirm_value(
await ctx_wait(
RustLayout(
trezorui2.confirm_blob(
title=info_title.upper(),
title=info_title,
data=info_value,
description=description,
extra=None,
@ -1228,7 +1221,7 @@ def confirm_metadata(
) -> Awaitable[None]:
return _placeholder_confirm(
br_type,
title.upper(),
title,
description=content.format(param),
hold=hold,
br_code=br_code,
@ -1237,7 +1230,7 @@ def confirm_metadata(
def confirm_replacement(description: str, txid: str) -> Awaitable[None]:
return confirm_value(
description.upper(),
description,
txid,
TR.send__transaction_id,
"confirm_replacement",
@ -1345,7 +1338,7 @@ def confirm_sign_identity(
return _placeholder_confirm(
"confirm_sign_identity",
f"{TR.words__sign} {proto}".upper(),
f"{TR.words__sign} {proto}",
text,
br_code=BR_TYPE_OTHER,
)
@ -1456,7 +1449,7 @@ async def request_pin_on_device(
result = await interact(
RustLayout(
trezorui2.request_pin(
prompt=prompt.upper(),
prompt=prompt,
subprompt=subprompt,
allow_cancel=allow_cancel,
wrong_pin=wrong_pin,
@ -1542,7 +1535,7 @@ async def confirm_set_new_pin(
) -> None:
await _confirm_multiple_pages_texts(
br_type,
title.upper(),
title,
[description, information],
TR.buttons__turn_on,
br_code,
@ -1559,7 +1552,7 @@ async def confirm_set_new_pin(
]
await _confirm_multiple_pages_texts(
br_type,
title.upper(),
title,
next_info,
TR.buttons__continue,
br_code,

@ -14,7 +14,7 @@ async def confirm_fido(
"""Webauthn confirmation for one or more credentials."""
confirm = RustLayout(
trezorui2.confirm_fido( # type: ignore [Argument missing for parameter "icon_name"]
title=header.upper(),
title=header,
app_name=app_name,
accounts=accounts,
)

@ -94,7 +94,7 @@ async def continue_recovery(
trezorui2.confirm_recovery(
title="",
description=text,
button=button_label.upper(),
button=button_label,
info_button=False,
dry_run=dry_run,
show_info=show_info, # type: ignore [No parameter named "show_info"]

@ -88,7 +88,7 @@ async def select_word(
while len(words) < 3:
words.append(words[-1])
word_ordinal = format_ordinal(checked_index + 1).upper()
word_ordinal = format_ordinal(checked_index + 1)
result = await wait(
RustLayout(
trezorui2.select_word(
@ -148,7 +148,7 @@ async def _prompt_number(
) -> int:
num_input = RustLayout(
trezorui2.request_number(
title=title.upper(),
title=title,
count=count,
min_count=min_count,
max_count=max_count,
@ -292,6 +292,6 @@ async def show_reset_warning(
br_type,
subheader or "",
content,
button.upper(),
button,
br_code=br_code,
)

@ -256,11 +256,6 @@ def confirm_action(
exc: ExceptionType = ActionCancelled,
br_code: ButtonRequestType = BR_TYPE_OTHER,
) -> Awaitable[None]:
if verb is not None:
verb = verb.upper()
if verb_cancel is not None:
verb_cancel = verb_cancel.upper()
if description is not None and description_param is not None:
description = description.format(description_param)
@ -268,7 +263,7 @@ def confirm_action(
interact(
RustLayout(
trezorui2.confirm_action(
title=title.upper(),
title=title,
action=action,
description=description,
verb=verb,
@ -292,8 +287,6 @@ def confirm_single(
description_param: str | None = None,
verb: str | None = None,
) -> Awaitable[None]:
if verb is not None:
verb = verb.upper()
description_param = description_param or ""
# Placeholders are coming from translations in form of {0}
@ -306,7 +299,7 @@ def confirm_single(
interact(
RustLayout(
trezorui2.confirm_emphasized(
title=title.upper(),
title=title,
items=(begin, (True, description_param), end),
verb=verb,
)
@ -327,7 +320,7 @@ def confirm_reset_device(title: str, recovery: bool = False) -> Awaitable[None]:
interact(
RustLayout(
trezorui2.confirm_reset_device(
title=title.upper(),
title=title,
button=button,
)
),
@ -362,7 +355,7 @@ async def prompt_backup() -> bool:
result = await interact(
RustLayout(
trezorui2.confirm_action(
title=TR.words__warning.upper(),
title=TR.words__warning,
action=TR.backup__want_to_skip,
description=TR.backup__can_back_up_anytime,
verb=TR.buttons__back_up,
@ -515,7 +508,7 @@ def show_pubkey(
mismatch_title = mismatch_title or TR.addr_mismatch__key_mismatch # def_arg
return show_address(
address=pubkey,
title=title.upper(),
title=title,
account=account,
path=path,
br_type=br_type,
@ -538,7 +531,7 @@ async def show_error_and_raise(
trezorui2.show_error(
title=subheader or "",
description=content,
button=button.upper(),
button=button,
allow_cancel=False,
)
),
@ -562,7 +555,7 @@ def show_warning(
trezorui2.show_warning(
title=content,
description=subheader or "",
button=button.upper(),
button=button,
)
),
br_type,
@ -584,7 +577,7 @@ def show_success(
trezorui2.show_success(
title=content,
description=subheader or "",
button=button.upper(),
button=button,
allow_cancel=False,
)
),
@ -605,7 +598,7 @@ async def confirm_output(
chunkify: bool = False,
) -> None:
if title is not None:
# TODO: handle translation
# TODO: handle translation:
if title.upper().startswith("CONFIRM "):
title = title[len("CONFIRM ") :]
amount_title = title
@ -621,7 +614,7 @@ async def confirm_output(
result = await interact(
RustLayout(
trezorui2.confirm_value(
title=recipient_title.upper(),
title=recipient_title,
subtitle=address_label,
description=None,
value=address,
@ -640,7 +633,7 @@ async def confirm_output(
result = await interact(
RustLayout(
trezorui2.confirm_value(
title=amount_title.upper(),
title=amount_title,
subtitle=None,
description=None,
value=amount,
@ -709,10 +702,10 @@ async def should_show_more(
result = await interact(
RustLayout(
trezorui2.confirm_with_info(
title=title.upper(),
title=title,
items=para,
button=confirm.upper(),
info_button=button_text.upper(),
button=confirm,
info_button=button_text,
)
),
br_type,
@ -780,7 +773,6 @@ def confirm_blob(
chunkify: bool = False,
) -> Awaitable[None]:
verb = verb or TR.buttons__confirm # def_arg
title = title.upper()
layout = RustLayout(
trezorui2.confirm_blob(
title=title,
@ -880,9 +872,6 @@ def confirm_value(
if not verb and not hold:
raise ValueError("Either verb or hold=True must be set")
if verb:
verb = verb.upper()
info_items = info_items or []
info_layout = RustLayout(
trezorui2.show_info_with_cancel(
@ -896,7 +885,7 @@ def confirm_value(
with_info(
RustLayout(
trezorui2.confirm_value(
title=title.upper(),
title=title,
subtitle=subtitle,
description=description,
value=value,
@ -927,7 +916,7 @@ def confirm_properties(
interact(
RustLayout(
trezorui2.confirm_properties(
title=title.upper(),
title=title,
items=items,
hold=hold,
)
@ -984,7 +973,7 @@ def confirm_summary(
total_layout = RustLayout(
trezorui2.confirm_total(
title=title.upper(),
title=title,
items=items,
info_button=bool(info_items),
)
@ -992,7 +981,7 @@ def confirm_summary(
info_items = info_items or []
info_layout = RustLayout(
trezorui2.show_info_with_cancel(
title=info_title.upper() if info_title else TR.words__title_information,
title=info_title if info_title else TR.words__title_information,
items=info_items,
)
)
@ -1034,7 +1023,7 @@ if not utils.BITCOIN_ONLY:
# Allowing going back and forth between recipient and summary/details
await confirm_blob(
br_type,
TR.words__recipient.upper(),
TR.words__recipient,
recipient,
verb=TR.buttons__continue,
chunkify=chunkify,
@ -1144,11 +1133,11 @@ def confirm_metadata(
verb = verb or TR.buttons__continue # def_arg
return confirm_action(
br_type,
title=title.upper(),
title=title,
action="",
description=content,
description_param=param,
verb=verb.upper(),
verb=verb,
hold=hold,
br_code=br_code,
)
@ -1157,7 +1146,7 @@ def confirm_metadata(
def confirm_replacement(title: str, txid: str) -> Awaitable[None]:
return confirm_blob(
"confirm_replacement",
title.upper(),
title,
txid,
TR.send__transaction_id,
TR.buttons__continue,
@ -1244,7 +1233,7 @@ def confirm_modify_fee(
) -> Awaitable[None]:
fee_layout = RustLayout(
trezorui2.confirm_modify_fee(
title=title.upper(),
title=title,
sign=sign,
user_fee_change=user_fee_change,
total_fee_new=total_fee_new,
@ -1499,7 +1488,7 @@ def confirm_set_new_pin(
interact(
RustLayout(
trezorui2.confirm_emphasized(
title=title.upper(),
title=title,
items=(
(True, description + "\n\n"),
information,

@ -56,7 +56,7 @@ async def confirm_fido(
"""Webauthn confirmation for one or more credentials."""
confirm = _RustFidoLayout(
trezorui2.confirm_fido(
title=header.upper(),
title=header,
app_name=app_name,
icon_name=icon_name,
accounts=accounts,

@ -132,7 +132,7 @@ async def continue_recovery(
trezorui2.confirm_recovery(
title=text,
description=description,
button=button_label.upper(),
button=button_label,
info_button=info_func is not None,
dry_run=dry_run,
)
@ -161,7 +161,7 @@ async def show_recovery_warning(
trezorui2.show_warning(
title=content,
description=subheader or "",
button=button.upper(),
button=button,
allow_cancel=False,
)
),

@ -158,7 +158,7 @@ async def _prompt_number(
) -> int:
num_input = RustLayout(
trezorui2.request_number(
title=title.upper(),
title=title,
description=description,
count=count,
min_count=min_count,
@ -359,7 +359,7 @@ async def show_reset_warning(
trezorui2.show_warning(
title=subheader or "",
description=content,
button=button.upper(),
button=button,
allow_cancel=False,
)
),

Loading…
Cancel
Save