Add link to open mod on Nexus #160
6 changed files with 84 additions and 3 deletions
|
@ -19,6 +19,7 @@
|
||||||
- dtmm: fetch cover image for Nexus mods
|
- dtmm: fetch cover image for Nexus mods
|
||||||
- dtmm: fetch file version for Nexus mods
|
- dtmm: fetch file version for Nexus mods
|
||||||
- dtmm: handle `nxm://` URIs via IPC and import the corresponding mod
|
- dtmm: handle `nxm://` URIs via IPC and import the corresponding mod
|
||||||
|
- dtmm: Add button to open mod on nexusmods.com
|
||||||
|
|
||||||
=== Fixed
|
=== Fixed
|
||||||
|
|
||||||
|
|
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -920,6 +920,7 @@ dependencies = [
|
||||||
"minijinja",
|
"minijinja",
|
||||||
"nexusmods",
|
"nexusmods",
|
||||||
"oodle",
|
"oodle",
|
||||||
|
"open",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
"sdk",
|
"sdk",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1882,6 +1883,25 @@ version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
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]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -2340,6 +2360,17 @@ dependencies = [
|
||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.59"
|
version = "0.10.59"
|
||||||
|
@ -2478,6 +2509,12 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathdiff"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
|
@ -24,6 +24,7 @@ luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" }
|
||||||
minijinja = "1.0.10"
|
minijinja = "1.0.10"
|
||||||
nexusmods = { path = "../../lib/nexusmods", version = "*" }
|
nexusmods = { path = "../../lib/nexusmods", version = "*" }
|
||||||
oodle = { path = "../../lib/oodle", version = "*" }
|
oodle = { path = "../../lib/oodle", version = "*" }
|
||||||
|
open = "5.0.1"
|
||||||
path-slash = "0.2.1"
|
path-slash = "0.2.1"
|
||||||
sdk = { path = "../../lib/sdk", version = "*" }
|
sdk = { path = "../../lib/sdk", version = "*" }
|
||||||
serde = { version = "1.0.152", features = ["derive", "rc"] }
|
serde = { version = "1.0.152", features = ["derive", "rc"] }
|
||||||
|
|
|
@ -61,6 +61,8 @@ pub(crate) type InitialLoadResult = (Config, Vector<Arc<ModInfo>>);
|
||||||
pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector<SingleUse<Option<InitialLoadResult>>> =
|
pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector<SingleUse<Option<InitialLoadResult>>> =
|
||||||
Selector::new("dtmm.action.finish-load-initial");
|
Selector::new("dtmm.action.finish-load-initial");
|
||||||
|
|
||||||
|
pub(crate) const ACTION_OPEN_LINK: Selector<Arc<String>> = Selector::new("dtmm.action.open-link");
|
||||||
|
|
||||||
// A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are
|
// A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are
|
||||||
// `Send + Sync`
|
// `Send + Sync`
|
||||||
pub(crate) struct ActionState {
|
pub(crate) struct ActionState {
|
||||||
|
@ -438,6 +440,20 @@ impl AppDelegate<State> for Delegate {
|
||||||
|
|
||||||
Handled::Yes
|
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,
|
_ => Handled::No,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ macro_rules! make_color {
|
||||||
}
|
}
|
||||||
|
|
||||||
make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1);
|
make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1);
|
||||||
|
make_color!(LINK_COLOR, COLOR_ACCENT);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod gruvbox_dark {
|
pub mod gruvbox_dark {
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use druid::im::Vector;
|
use druid::im::Vector;
|
||||||
|
use druid::text::RichTextBuilder;
|
||||||
use druid::widget::{
|
use druid::widget::{
|
||||||
Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List,
|
Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List,
|
||||||
MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher,
|
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 lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN,
|
ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_OPEN_LINK,
|
||||||
ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE,
|
ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE,
|
||||||
ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT,
|
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::theme::{self, ColorExt, COLOR_YELLOW_LIGHT};
|
||||||
use crate::ui::widget::border::Border;
|
use crate::ui::widget::border::Border;
|
||||||
|
@ -345,6 +347,28 @@ fn build_mod_details_info() -> impl Widget<State> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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()
|
let details = Flex::column()
|
||||||
.cross_axis_alignment(CrossAxisAlignment::Start)
|
.cross_axis_alignment(CrossAxisAlignment::Start)
|
||||||
.main_axis_alignment(MainAxisAlignment::Start)
|
.main_axis_alignment(MainAxisAlignment::Start)
|
||||||
|
@ -352,6 +376,7 @@ fn build_mod_details_info() -> impl Widget<State> {
|
||||||
.with_spacer(4.)
|
.with_spacer(4.)
|
||||||
.with_child(summary)
|
.with_child(summary)
|
||||||
.with_spacer(4.)
|
.with_spacer(4.)
|
||||||
|
.with_child(nexus_link)
|
||||||
.with_child(version_line)
|
.with_child(version_line)
|
||||||
.with_spacer(4.)
|
.with_spacer(4.)
|
||||||
.with_child(categories)
|
.with_child(categories)
|
||||||
|
|
Loading…
Add table
Reference in a new issue