From 57771617ffb7ffbb137025aa95cc249198f817d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 4 Dec 2023 16:47:09 +0100 Subject: [PATCH] dtmm: Add link to open mod on Nexus Closes #157. --- CHANGELOG.adoc | 1 + Cargo.lock | 37 ++++++++++++++++++++++++++++++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/state/delegate.rs | 16 +++++++++++++ crates/dtmm/src/ui/theme/colors.rs | 1 + crates/dtmm/src/ui/window/main.rs | 31 ++++++++++++++++++++++--- 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 358bc33..db30865 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -19,6 +19,7 @@ - dtmm: fetch cover image for Nexus mods - dtmm: fetch file version for Nexus mods - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod +- dtmm: Add button to open mod on nexusmods.com === Fixed diff --git a/Cargo.lock b/Cargo.lock index 1817853..93568af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,6 +920,7 @@ dependencies = [ "minijinja", "nexusmods", "oodle", + "open", "path-slash", "sdk", "serde", @@ -1882,6 +1883,25 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itoa" version = "1.0.9" @@ -2340,6 +2360,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "open" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.59" @@ -2478,6 +2509,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c159295..05dc160 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -24,6 +24,7 @@ luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } minijinja = "1.0.10" nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } +open = "5.0.1" path-slash = "0.2.1" sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index e30b878..f3c4711 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -61,6 +61,8 @@ pub(crate) type InitialLoadResult = (Config, Vector>); pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector>> = Selector::new("dtmm.action.finish-load-initial"); +pub(crate) const ACTION_OPEN_LINK: Selector> = Selector::new("dtmm.action.open-link"); + // A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are // `Send + Sync` pub(crate) struct ActionState { @@ -438,6 +440,20 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_OPEN_LINK) => { + let url = cmd + .get(ACTION_OPEN_LINK) + .expect("command type matched but didn't contain the expected value"); + + if let Err(err) = open::that_detached(Arc::as_ref(url)) { + tracing::error!( + "{:?}", + Report::new(err).wrap_err(format!("Failed to open url '{}'", url)) + ); + } + + Handled::Yes + } _ => Handled::No, } } diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 078dab0..2c2bf78 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -17,6 +17,7 @@ macro_rules! make_color { } make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1); +make_color!(LINK_COLOR, COLOR_ACCENT); #[allow(dead_code)] pub mod gruvbox_dark { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index baa8e22..4116932 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; +use druid::text::RichTextBuilder; use druid::widget::{ Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, @@ -16,9 +17,10 @@ use druid_widget_nursery::WidgetExt as _; use lazy_static::lazy_static; use crate::state::{ - ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, - ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, - ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, + ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_OPEN_LINK, + ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, + ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, + ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; use crate::ui::widget::border::Border; @@ -345,6 +347,28 @@ fn build_mod_details_info() -> impl Widget { } }); + let nexus_link = Maybe::or_empty(|| { + let link = Label::raw().lens(NexusInfo::id.map( + |id| { + let url = format!("https://nexusmods.com/warhammer40kdarktide/mods/{}", id); + let mut builder = RichTextBuilder::new(); + builder + .push("Open on Nexusmods") + .underline(true) + .text_color(theme::LINK_COLOR) + .link(ACTION_OPEN_LINK.with(Arc::new(url))); + builder.build() + }, + |_, _| {}, + )); + Flex::column() + .cross_axis_alignment(CrossAxisAlignment::Start) + .main_axis_alignment(MainAxisAlignment::Start) + .with_child(link) + .with_spacer(4.) + }) + .lens(ModInfo::nexus.in_arc()); + let details = Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::Start) @@ -352,6 +376,7 @@ fn build_mod_details_info() -> impl Widget { .with_spacer(4.) .with_child(summary) .with_spacer(4.) + .with_child(nexus_link) .with_child(version_line) .with_spacer(4.) .with_child(categories) -- 2.45.3