Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/app-hub-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ cargo run --release -p octosense-app-hub-app --example preview
The preview logs launch requests instead of launching; only a shell opens
apps.

On an app's detail page, the primary button stays in the same position:
**Get → Install → Installing… → Open**. Get reveals the permissions and
privacy disclosures below the button; Install confirms that reviewed
catalog entry. Cancel returns the button to Get (or Update for an available
update). Installation results appear below the app header, so they do not
move the primary action.

This crate's default feature, `text-input-state-query`, turns on the same
feature in `octosense-appstore`, and that feature needs OctoSense's runtime
patches on `../makepad`. Against plain sibling checkouts, add
Expand Down
10 changes: 8 additions & 2 deletions crates/app-hub-app/examples/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ impl AppMain for App {
if let Event::Actions(actions) = event {
for action in actions {
if let Some(action) = action.as_widget_action() {
if let octosense_app_hub_app::AppHubAction::Launch(id) = action.cast() {
log!("apphub-preview: launch {id}");
match action.cast() {
octosense_app_hub_app::AppHubAction::Launch(id) => {
log!("apphub-preview: launch {id}");
}
octosense_app_hub_app::AppHubAction::OpenInstalled(id) => {
log!("apphub-preview: open installed {id}");
}
octosense_app_hub_app::AppHubAction::None => {}
}
}
}
Expand Down
47 changes: 24 additions & 23 deletions crates/app-hub-app/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ script_mod! {
title := Heading{}
permissions := Text{}
privacy := Meta{}
confirm := Primary{text: "Install"}
cancel := Plain{text: "Cancel" width: Fill}
}
}
Expand Down Expand Up @@ -493,16 +492,21 @@ impl AppHubView {
script_apply_eval!(cx,tab,{draw_icon.color: #(color) draw_text +: {color: #(color) color_hover: #(color) color_down: #(color) color_focus: #(color)} draw_bg +: {color: #(face) color_hover: #(face) color_focus: #(face) border_radius: 12}});
}
self.rows.clear();
if !self.notice.is_empty() {
if !self.notice.is_empty() && self.selected.is_none() {
self.rows.push(Row::Notice(self.notice.clone()));
}
if let Some(entry) = self.selected.clone() {
// Keep the primary action at the same position when consent,
// installation results, or catalog warnings change below it.
self.rows.push(Row::Detail(entry.clone()));
if !self.notice.is_empty() {
self.rows.push(Row::Notice(self.notice.clone()));
}
if entry.kind == CatalogKind::Live {
if let Some(warning) = self.snapshot.as_ref().and_then(|s| s.warning.clone()) {
self.rows.push(Row::Notice(warning));
}
}
self.rows.push(Row::Detail(entry.clone()));
if self.confirming {
self.rows.push(Row::Consent(entry));
} else {
Expand Down Expand Up @@ -651,10 +655,18 @@ impl AppHubView {
}
EntryStatus::Installed => self.send(Command::Open(entry.id)),
EntryStatus::Available | EntryStatus::UpdateAvailable => {
if entry.consent.is_some() {
if self.confirming && self.selected.as_ref().is_some_and(|e| e.id == entry.id) {
if let Some(consent) = entry.consent {
self.send(Command::Install(consent));
self.installing = self.busy;
}
} else if entry.consent.is_some() {
let already_selected = self.selected.as_ref().is_some_and(|e| e.id == entry.id);
self.selected = Some(entry);
self.confirming = true;
self.reset_scroll(cx);
if !already_selected {
self.reset_scroll(cx);
}
} else {
self.notice = "Refresh the catalog before installing this app.".into();
}
Expand Down Expand Up @@ -767,6 +779,12 @@ impl AppHubView {
cx,
if self.installing && self.selected.as_ref().is_some_and(|e| e.id == entry.id) {
"Installing…"
} else if self.confirming
&& self.selected.as_ref().is_some_and(|e| e.id == entry.id)
&& entry.consent.is_some()
&& matches!(entry.status, EntryStatus::Available | EntryStatus::UpdateAvailable)
{
"Install"
} else {
button_text(entry)
},
Expand Down Expand Up @@ -860,18 +878,8 @@ impl AppHubView {
);
item.label(cx, ids!(privacy))
.set_text(cx, &lines_or(&entry.privacy, ""));
item.button(cx, ids!(confirm))
.set_enabled(cx, !self.busy && entry.consent.is_some());
item.button(cx, ids!(cancel))
.set_enabled(cx, !self.installing);
item.button(cx, ids!(confirm)).set_text(
cx,
if self.installing {
"Installing…"
} else {
"Install"
},
);
}
Row::Categories => {
for (id, category) in [
Expand Down Expand Up @@ -985,14 +993,7 @@ impl AppHubView {
}
}
}
Row::Consent(entry) => {
if item.button(cx, ids!(confirm)).clicked(actions) && !self.busy {
if let Some(consent) = entry.consent {
self.installing = true;
self.send(Command::Install(consent));
self.render(cx);
}
}
Row::Consent(_) => {
if item.button(cx, ids!(cancel)).clicked(actions) && !self.installing {
self.confirming = false;
self.render(cx);
Expand Down
Loading