parent
fa8d6a4a60
commit
b322a9b976
@ -0,0 +1,158 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::qstr::Qstr,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{
|
||||
AddressDetails, Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
use super::util::ConfirmBlobParams;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum ConfirmOutput {
|
||||
Address,
|
||||
Amount,
|
||||
// Tap,
|
||||
Menu,
|
||||
AccountInfo,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
impl FlowState for ConfirmOutput {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
match (self, direction) {
|
||||
(ConfirmOutput::Address | ConfirmOutput::Amount, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmOutput::Menu, direction)
|
||||
}
|
||||
(ConfirmOutput::Address, SwipeDirection::Up) => {
|
||||
Decision::Goto(ConfirmOutput::Amount, direction)
|
||||
}
|
||||
(ConfirmOutput::Amount, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
|
||||
(ConfirmOutput::Amount, SwipeDirection::Down) => {
|
||||
Decision::Goto(ConfirmOutput::Address, direction)
|
||||
}
|
||||
(ConfirmOutput::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmOutput::Address, direction)
|
||||
}
|
||||
(ConfirmOutput::Menu, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmOutput::AccountInfo, direction)
|
||||
}
|
||||
(ConfirmOutput::AccountInfo | ConfirmOutput::CancelTap, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmOutput::Menu, direction)
|
||||
}
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
match (self, msg) {
|
||||
(_, FlowMsg::Info) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Left),
|
||||
(ConfirmOutput::Menu, FlowMsg::Choice(0)) => {
|
||||
Decision::Goto(ConfirmOutput::AccountInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmOutput::Menu, FlowMsg::Choice(1)) => {
|
||||
Decision::Goto(ConfirmOutput::CancelTap, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmOutput::Menu, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(ConfirmOutput::Address, SwipeDirection::Right)
|
||||
}
|
||||
(ConfirmOutput::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled),
|
||||
(_, FlowMsg::Cancelled) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Right),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmOutput::new_obj) }
|
||||
}
|
||||
|
||||
impl ConfirmOutput {
|
||||
const EXTRA_PADDING: i16 = 6;
|
||||
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
|
||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||
let account_path: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
|
||||
|
||||
let address: Obj = kwargs.get(Qstr::MP_QSTR_address)?;
|
||||
let amount: Obj = kwargs.get(Qstr::MP_QSTR_amount)?;
|
||||
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
||||
|
||||
// Address
|
||||
let content_address = ConfirmBlobParams::new(TR::words__address.into(), address, None)
|
||||
.with_subtitle(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_chunkify(chunkify)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
|
||||
// Amount
|
||||
let content_amount = ConfirmBlobParams::new(TR::words__amount.into(), amount, None)
|
||||
.with_subtitle(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Account info".into())
|
||||
.danger(theme::ICON_CANCEL, "Cancel sign".into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// AccountInfo
|
||||
let ad = AddressDetails::new(TR::send__send_from.into(), account, account_path)?;
|
||||
let content_account = SwipePage::horizontal(ad).map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
// CancelTap
|
||||
let content_cancel_tap = Frame::left_aligned(
|
||||
TR::send__cancel_sign.into(),
|
||||
PromptScreen::new_tap_to_cancel(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_address)?
|
||||
.add(content_amount)?
|
||||
.add(content_menu)?
|
||||
.add(content_account)?
|
||||
.add(content_cancel_tap)?;
|
||||
let res = SwipeFlow::new(ConfirmOutput::Address, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::{iter::IterBuf, qstr::Qstr},
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
theme,
|
||||
};
|
||||
|
||||
use super::util::ShowInfoParams;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum ConfirmSummary {
|
||||
Summary,
|
||||
Hold,
|
||||
Menu,
|
||||
FeeInfo,
|
||||
AccountInfo,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
impl FlowState for ConfirmSummary {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
match (self, direction) {
|
||||
(ConfirmSummary::Summary | ConfirmSummary::Hold, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmSummary::Menu, direction)
|
||||
}
|
||||
(ConfirmSummary::Summary, SwipeDirection::Up) => {
|
||||
Decision::Goto(ConfirmSummary::Hold, direction)
|
||||
}
|
||||
(ConfirmSummary::Hold, SwipeDirection::Down) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, direction)
|
||||
}
|
||||
(ConfirmSummary::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, direction)
|
||||
}
|
||||
(ConfirmSummary::Menu, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmSummary::FeeInfo, direction)
|
||||
}
|
||||
(
|
||||
ConfirmSummary::AccountInfo | ConfirmSummary::FeeInfo | ConfirmSummary::CancelTap,
|
||||
SwipeDirection::Right,
|
||||
) => Decision::Goto(ConfirmSummary::Menu, direction),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
match (self, msg) {
|
||||
(_, FlowMsg::Info) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Left),
|
||||
(ConfirmSummary::Hold, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed),
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(0)) => {
|
||||
Decision::Goto(ConfirmSummary::FeeInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(1)) => {
|
||||
Decision::Goto(ConfirmSummary::AccountInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(2)) => {
|
||||
Decision::Goto(ConfirmSummary::CancelTap, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, SwipeDirection::Right)
|
||||
}
|
||||
(ConfirmSummary::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled),
|
||||
(_, FlowMsg::Cancelled) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Right),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmSummary::new_obj) }
|
||||
}
|
||||
|
||||
impl ConfirmSummary {
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let account_items: Obj = kwargs.get(Qstr::MP_QSTR_account_items)?;
|
||||
let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?;
|
||||
|
||||
// Summary
|
||||
let mut summary = ShowInfoParams::new(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None);
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
summary = unwrap!(summary.add(label, value));
|
||||
}
|
||||
let content_summary = summary.into_layout()?;
|
||||
|
||||
// Hold to confirm
|
||||
let content_hold = Frame::left_aligned(
|
||||
TR::eos__sign_transaction.into(),
|
||||
PromptScreen::new_hold_to_confirm(),
|
||||
)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__hold_to_sign.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||
});
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Fee info".into())
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Account info".into())
|
||||
.danger(theme::ICON_CANCEL, "Cancel sign".into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// FeeInfo
|
||||
let mut fee = ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(fee_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
fee = unwrap!(fee.add(label, value));
|
||||
}
|
||||
let content_fee = fee.into_layout()?;
|
||||
|
||||
// AccountInfo
|
||||
let mut account = ShowInfoParams::new(TR::send__send_from.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(account_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
account = unwrap!(account.add(label, value));
|
||||
}
|
||||
let content_account = account.into_layout()?;
|
||||
|
||||
// CancelTap
|
||||
let content_cancel_tap = Frame::left_aligned(
|
||||
TR::send__cancel_sign.into(),
|
||||
PromptScreen::new_tap_to_cancel(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_summary)?
|
||||
.add(content_hold)?
|
||||
.add(content_menu)?
|
||||
.add(content_fee)?
|
||||
.add(content_account)?
|
||||
.add(content_cancel_tap)?;
|
||||
let res = SwipeFlow::new(ConfirmSummary::Summary, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg},
|
||||
theme,
|
||||
};
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::obj::Obj,
|
||||
strutil::TString,
|
||||
trace::Trace,
|
||||
ui::{
|
||||
component::{
|
||||
base::ComponentExt,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, VecExt},
|
||||
Component,
|
||||
},
|
||||
flow::{FlowMsg, Swipable, SwipePage},
|
||||
layout::util::ConfirmBlob,
|
||||
},
|
||||
};
|
||||
use heapless::Vec;
|
||||
|
||||
pub struct ConfirmBlobParams {
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
footer_instruction: Option<TString<'static>>,
|
||||
footer_description: Option<TString<'static>>,
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
extra: Option<TString<'static>>,
|
||||
menu_button: bool,
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
}
|
||||
|
||||
impl ConfirmBlobParams {
|
||||
pub const fn new(
|
||||
title: TString<'static>,
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title,
|
||||
subtitle: None,
|
||||
footer_instruction: None,
|
||||
footer_description: None,
|
||||
data,
|
||||
description,
|
||||
extra: None,
|
||||
menu_button: false,
|
||||
chunkify: false,
|
||||
text_mono: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_extra(mut self, extra: Option<TString<'static>>) -> Self {
|
||||
self.extra = extra;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_subtitle(mut self, subtitle: Option<TString<'static>>) -> Self {
|
||||
self.subtitle = subtitle;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_menu_button(mut self) -> Self {
|
||||
self.menu_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_footer(
|
||||
mut self,
|
||||
instruction: TString<'static>,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
self.footer_instruction = Some(instruction);
|
||||
self.footer_description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_chunkify(mut self, chunkify: bool) -> Self {
|
||||
self.chunkify = chunkify;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_text_mono(mut self, text_mono: bool) -> Self {
|
||||
self.text_mono = text_mono;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_layout(self) -> Result<impl Component<Msg = FlowMsg> + Swipable + Trace, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or("".into()),
|
||||
extra: self.extra.unwrap_or("".into()),
|
||||
data: self.data.try_into()?,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: if self.chunkify {
|
||||
let data: TString = self.data.try_into()?;
|
||||
theme::get_chunkified_text_style(data.len())
|
||||
} else if self.text_mono {
|
||||
&theme::TEXT_MONO
|
||||
} else {
|
||||
&theme::TEXT_NORMAL
|
||||
},
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let page = SwipePage::vertical(paragraphs);
|
||||
let mut frame = Frame::left_aligned(self.title, page);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(subtitle);
|
||||
}
|
||||
if self.menu_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
if let Some(instruction) = self.footer_instruction {
|
||||
frame = frame.with_footer(instruction, self.footer_description);
|
||||
}
|
||||
Ok(frame.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShowInfoParams {
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
menu_button: bool,
|
||||
cancel_button: bool,
|
||||
footer_instruction: Option<TString<'static>>,
|
||||
footer_description: Option<TString<'static>>,
|
||||
chunkify: bool,
|
||||
items: Vec<(TString<'static>, TString<'static>), 4>,
|
||||
}
|
||||
|
||||
impl ShowInfoParams {
|
||||
pub const fn new(title: TString<'static>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
subtitle: None,
|
||||
menu_button: false,
|
||||
cancel_button: false,
|
||||
footer_instruction: None,
|
||||
footer_description: None,
|
||||
chunkify: false,
|
||||
items: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(mut self, key: TString<'static>, value: TString<'static>) -> Option<Self> {
|
||||
if self.items.push((key, value)).is_ok() {
|
||||
Some(self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_subtitle(mut self, subtitle: Option<TString<'static>>) -> Self {
|
||||
self.subtitle = subtitle;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_menu_button(mut self) -> Self {
|
||||
self.menu_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_cancel_button(mut self) -> Self {
|
||||
self.cancel_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_footer(
|
||||
mut self,
|
||||
instruction: TString<'static>,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
self.footer_instruction = Some(instruction);
|
||||
self.footer_description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_chunkify(mut self, chunkify: bool) -> Self {
|
||||
self.chunkify = chunkify;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_layout(self) -> Result<impl Component<Msg = FlowMsg> + Swipable + Trace, Error> {
|
||||
let mut paragraphs = ParagraphVecLong::new();
|
||||
let mut first: bool = true;
|
||||
for item in self.items {
|
||||
// FIXME: padding:
|
||||
if !first {
|
||||
paragraphs.add(Paragraph::new::<TString<'static>>(
|
||||
&theme::TEXT_SUB_GREY,
|
||||
" ".into(),
|
||||
));
|
||||
}
|
||||
first = false;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_SUB_GREY, item.0).no_break());
|
||||
if self.chunkify {
|
||||
paragraphs.add(Paragraph::new(
|
||||
theme::get_chunkified_text_style(item.1.len()),
|
||||
item.1,
|
||||
));
|
||||
} else {
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, item.1));
|
||||
}
|
||||
}
|
||||
|
||||
let mut frame = Frame::left_aligned(
|
||||
self.title,
|
||||
SwipePage::vertical(paragraphs.into_paragraphs()),
|
||||
);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(subtitle);
|
||||
}
|
||||
if self.cancel_button {
|
||||
frame = frame.with_cancel_button();
|
||||
} else if self.menu_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
if let Some(instruction) = self.footer_instruction {
|
||||
frame = frame.with_footer(instruction, self.footer_description);
|
||||
}
|
||||
Ok(frame.map(move |msg| {
|
||||
matches!(msg, FrameMsg::Button(_)).then_some(if self.cancel_button {
|
||||
FlowMsg::Cancelled
|
||||
} else {
|
||||
FlowMsg::Info
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
Loading…
Reference in new issue