Skip to content

Commit 4c82e6e

Browse files
feat: update all (close #2)
1 parent 2414f92 commit 4c82e6e

File tree

2 files changed

+119
-34
lines changed

2 files changed

+119
-34
lines changed

src/celemod-ui/src/routes/Manage.tsx

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,20 @@ const ModMissing = ({ name, version, optional }: MissingModDepInfo) => {
116116
onClick={
117117
url !== null
118118
? async () => {
119-
setState(_i18n.t('下载中'));
120-
download.downloadMod(name, url, {
121-
onProgress: (task, progress) => {
122-
setState(`${progress}% (${task.subtasks.length})`);
123-
},
124-
onFinished: () => {
125-
setState(_i18n.t('下载完成'));
126-
ctx?.reloadMods();
127-
},
128-
onFailed: () => {
129-
setState(_i18n.t('下载失败'));
130-
},
131-
});
132-
}
119+
setState(_i18n.t('下载中'));
120+
download.downloadMod(name, url, {
121+
onProgress: (task, progress) => {
122+
setState(`${progress}% (${task.subtasks.length})`);
123+
},
124+
onFinished: () => {
125+
setState(_i18n.t('下载完成'));
126+
ctx?.reloadMods();
127+
},
128+
onFailed: () => {
129+
setState(_i18n.t('下载失败'));
130+
},
131+
});
132+
}
133133
: undefined
134134
}
135135
>
@@ -201,9 +201,8 @@ const ModLocal = ({
201201
return (
202202
<div className={`m-mod ${enabled && 'enabled'}`}>
203203
<span
204-
className={`expandBtn ${expanded && 'expanded'} ${
205-
hasDeps && 'clickable'
206-
}`}
204+
className={`expandBtn ${expanded && 'expanded'} ${hasDeps && 'clickable'
205+
}`}
207206
onClick={() => setExpanded(!expanded)}
208207
>
209208
{hasDeps && (!optional || ctx?.fullTree) ? (
@@ -459,6 +458,35 @@ export const Manage = () => {
459458

460459
return modMap;
461460
}, [installedMods, currentProfile, profiles, checkOptionalDep]);
461+
462+
const [latestModInfos, setLatestModInfos] = useState<[
463+
string, string, string // name, version, url
464+
][]>([]);
465+
466+
useEffect(() => {
467+
callRemote('get_mod_latest_info', v => {
468+
setLatestModInfos(JSON.parse(v))
469+
})
470+
}, [])
471+
472+
const hasUpdateMods = useMemo(() => {
473+
const mods = [];
474+
for (const mod of installedMods) {
475+
const latest = latestModInfos.find(v => v[0] === mod.name);
476+
if (latest && compareVersion(latest[1], mod.version) > 0) {
477+
mods.push({
478+
name: mod.name,
479+
version: latest[1],
480+
url: latest[2]
481+
});
482+
}
483+
}
484+
485+
return mods;
486+
}, [latestModInfos, installedModMap]);
487+
488+
const [hasUpdateBtnState, setHasUpdateBtnState] = useState('更新全部');
489+
462490
const modsTreeRef = useRef(null);
463491
const [filter, setFilter] = useState('');
464492

@@ -625,6 +653,8 @@ export const Manage = () => {
625653
[currentProfile, installedMods, gamePath, modPath, fullTree, showUpdate]
626654
);
627655

656+
const { download } = useGlobalContext()
657+
628658
return (
629659
<div className="manage">
630660
<modListContext.Provider value={manageCtx}>
@@ -729,6 +759,38 @@ export const Manage = () => {
729759
{_i18n.t('显示更新')}
730760
</label>
731761
</div>
762+
<div className="opers" style={{
763+
marginTop: "5px"
764+
}}>
765+
{showUpdate && hasUpdateMods.length !== 0 && (
766+
<button onClick={() => {
767+
if (hasUpdateBtnState !== '更新全部') return;
768+
setHasUpdateBtnState('更新中');
769+
const updateUnfinishedSet = new Set(hasUpdateMods.map(v => v.name));
770+
for (const mod of hasUpdateMods) {
771+
download.downloadMod(mod.name, mod.url, {
772+
onProgress: (task, progress) => {
773+
console.log(task, progress);
774+
},
775+
onFinished: () => {
776+
updateUnfinishedSet.delete(mod.name);
777+
if (updateUnfinishedSet.size === 0) {
778+
setHasUpdateBtnState('更新完成');
779+
manageCtx.reloadMods();
780+
}
781+
},
782+
onFailed: () => {
783+
console.log('failed');
784+
setHasUpdateBtnState('更新失败,请查看左下角');
785+
},
786+
force: true,
787+
});
788+
}
789+
}}>
790+
{hasUpdateBtnState}
791+
</button>
792+
)}
793+
</div>
732794
<div className="list" ref={modsTreeRef}>
733795
{installedModsTree.map((v) => (
734796
<Mod {...(v as any)} />

src/main.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
use serde::{de::VariantAccess, Deserialize, Serialize};
66

7+
use anyhow::{bail, Context};
8+
use aria2c::DownloadCallbackInfo;
9+
use everest::get_mod_cached_new;
10+
use game_scanner::prelude::Game;
711
use std::{
812
borrow::BorrowMut,
913
cell::RefCell,
@@ -12,11 +16,7 @@ use std::{
1216
path::{Path, PathBuf},
1317
rc::Rc,
1418
sync::RwLock,
15-
};
16-
use anyhow::{bail, Context};
17-
use aria2c::DownloadCallbackInfo;
18-
use everest::get_mod_cached_new;
19-
use game_scanner::prelude::Game;
19+
};
2020

2121
use sciter::{dispatch_script_call, make_args, Value, GFX_LAYER};
2222

@@ -50,7 +50,7 @@ fn compare_version(a: &str, b: &str) -> i32 {
5050
}
5151

5252
struct Handler;
53-
53+
5454
fn extract_mod_for_yaml(path: &PathBuf) -> anyhow::Result<serde_yaml::Value> {
5555
let zipfile = std::fs::File::open(path)?;
5656
let mut archive = zip::ZipArchive::new(zipfile)?;
@@ -144,22 +144,23 @@ fn get_installed_mods_sync(mods_folder_path: String) -> Vec<LocalMod> {
144144
read_to_string_bom(&cache_path)?
145145
} else if entry.file_type().unwrap().is_dir() {
146146
let cache_path = entry.path().read_dir()?.find(|v| {
147-
v.as_ref().map(|v| {
148-
let name = v.file_name()
149-
.to_string_lossy()
150-
.to_string()
151-
.to_lowercase();
152-
name == "everest.yaml" || name == "everest.yml"
153-
})
154-
.unwrap_or(false)
147+
v.as_ref()
148+
.map(|v| {
149+
let name = v.file_name().to_string_lossy().to_string().to_lowercase();
150+
name == "everest.yaml" || name == "everest.yml"
151+
})
152+
.unwrap_or(false)
155153
});
156154
match cache_path {
157155
Some(cache_path) => {
158156
let cache_path = cache_path.unwrap().path();
159157
read_to_string_bom(&cache_path)?
160158
}
161159
None => {
162-
println!("[ WARNING ] Failed to find yaml, skipping {:?}", entry.file_name());
160+
println!(
161+
"[ WARNING ] Failed to find yaml, skipping {:?}",
162+
entry.file_name()
163+
);
163164
continue;
164165
}
165166
}
@@ -481,7 +482,7 @@ impl Handler {
481482
}
482483

483484
fn start_game_directly(&self, path: String, origin: bool) {
484-
let game =Path::new(&path).join("Celeste.exe");
485+
let game = Path::new(&path).join("Celeste.exe");
485486
let game_origin = Path::new(&path).join("orig").join("Celeste.exe");
486487

487488
if origin {
@@ -628,6 +629,27 @@ impl Handler {
628629
});
629630
}
630631

632+
fn get_mod_latest_info(&self, callback: sciter::Value) {
633+
std::thread::spawn(move || {
634+
let res: anyhow::Result<Vec<(String, String, String)>> = try {
635+
let mods = get_mod_cached_new()?;
636+
mods.iter()
637+
.map(|(k, v)|
638+
(k.clone(), v.version.clone(), v.download_url.clone())
639+
)
640+
.collect()
641+
};
642+
643+
let data = if let Ok(data) = res {
644+
serde_json::to_string(&data).unwrap()
645+
} else {
646+
"[]".to_string()
647+
};
648+
649+
callback.call(None, &make_args!(data), None).unwrap();
650+
});
651+
}
652+
631653
fn rm_mod(&self, mods_folder_path: String, mod_name: String) {
632654
std::thread::spawn(move || {
633655
if let Err(e) = rm_mod(&mods_folder_path, &mod_name) {
@@ -751,6 +773,7 @@ impl sciter::EventHandler for Handler {
751773
fn do_self_update(String, Value);
752774
fn start_game_directly(String, bool);
753775
fn verify_celeste_install(String);
776+
fn get_mod_latest_info(Value);
754777
}
755778
}
756779

@@ -775,7 +798,7 @@ fn main() {
775798
{
776799
use winapi::um::wincon::{AttachConsole, ATTACH_PARENT_PROCESS};
777800
use winapi::um::winuser::SetProcessDPIAware;
778-
unsafe {
801+
unsafe {
779802
AttachConsole(ATTACH_PARENT_PROCESS);
780803
SetProcessDPIAware();
781804
}

0 commit comments

Comments
 (0)