Skip to content

Commit 7fc4206

Browse files
committed
feat: add choose cover
1 parent 71c421c commit 7fc4206

8 files changed

Lines changed: 138 additions & 21 deletions

File tree

phira/locales/en-US/favorites.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ invalid-import = Invalid import code
1313
1414
rename = Rename
1515
set-description = Set description
16+
set-cover = Set cover
17+
select-cover = Please select a chart as cover
1618
1719
delete = Delete Folder
1820
deleted = Folder deleted

phira/locales/zh-CN/favorites.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ invalid-import = 无效的导入代码
1313
1414
rename = 重命名
1515
set-description = 设置描述
16+
set-cover = 设置封面
17+
select-cover = 请选择一个谱面作为封面
1618
1719
delete = 删除收藏夹
1820
deleted = 已删除收藏夹

phira/src/charts_view.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
2-
client::Chart,
2+
client::{Chart, CollectionCover},
33
dir, get_data, get_data_mut,
44
icons::Icons,
5-
page::{ChartItem, ChartType, Fader, Illustration},
5+
page::{ChartItem, ChartType, Fader, Illustration, CHOOSE_COVER, CHOSEN_COVER},
66
save_data,
77
scene::{render_release_to_refresh, SongScene, MP_PANEL},
88
};
@@ -182,6 +182,16 @@ impl ChartsView {
182182
if handled_by_mp {
183183
continue;
184184
}
185+
if CHOOSE_COVER.load(Ordering::Relaxed) {
186+
CHOSEN_COVER.with(|it| {
187+
*it.borrow_mut() = Some(if let Some(id) = chart.info.id {
188+
Ok(id)
189+
} else {
190+
Err(chart.local_path.clone().unwrap())
191+
});
192+
});
193+
continue;
194+
}
185195
let download_path = chart.info.id.map(|it| format!("download/{it}"));
186196
let scene = SongScene::new(
187197
chart.clone(),

phira/src/client/model/collection.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ impl LocalCollection {
125125
pub enum CollectionPatch {
126126
Toggle(i32),
127127
Public(bool),
128+
Cover(i32),
128129
}
129130

130131
#[derive(Serialize)]

phira/src/page.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod home;
1111
pub use home::HomePage;
1212

1313
mod library;
14-
pub use library::{LibraryPage, FAV_UPDATED};
14+
pub use library::{LibraryPage, CHOOSE_COVER, CHOSEN_COVER, FAV_UPDATED};
1515

1616
mod message;
1717
pub use message::MessagePage;

phira/src/page/favorites.rs

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ prpr_l10n::tl_file!("favorites");
22

33
use super::{Illustration, NextPage, Page, SharedState};
44
use crate::{
5-
client::{recv_raw, Chart, ChartRef, Client, Collection, CollectionContent, CollectionPatch, LocalCollection, UserManager},
5+
client::{Chart, ChartRef, Client, Collection, CollectionContent, CollectionCover, CollectionPatch, File, LocalCollection, Ptr, UserManager, recv_raw},
66
get_data, get_data_mut,
77
icons::Icons,
8-
page::SFader,
8+
page::{CHOOSE_COVER, SFader},
99
popup::Popup,
1010
save_data,
11-
scene::{confirm_dialog, ProfileScene, TEX_BACKGROUND},
11+
scene::{ProfileScene, TEX_BACKGROUND, confirm_dialog},
1212
};
1313
use anyhow::Result;
1414
use chrono::{DateTime, Utc};
@@ -96,16 +96,19 @@ pub struct FavoritesPage {
9696
sf: SFader,
9797
next_page: Option<NextPage>,
9898

99+
chosen_cover: Option<Result<i32, String>>,
100+
99101
upload_task: Option<Task<Result<Collection>>>,
100102
delete_from_cloud_task: Option<Task<Result<()>>>,
101103
set_public_task: Option<Task<Result<Collection>>>,
102104
sync_task: Option<Task<Result<Option<Collection>>>>,
103105
import_task: Option<Task<Result<Collection>>>,
104106
batch_import_task: Option<Task<Result<Vec<Chart>>>>,
107+
set_cover_task: Option<Task<Result<Result<Collection, File>>>>,
105108
}
106109

107110
impl FavoritesPage {
108-
pub fn new(icons: Arc<Icons>, rank_icons: [SafeTexture; 8], active_folder: Option<usize>) -> Self {
111+
pub fn new(icons: Arc<Icons>, rank_icons: [SafeTexture; 8], active_folder: Option<usize>, chosen_cover: Option<Result<i32, String>>) -> Self {
109112
let mut page = Self {
110113
icons,
111114
rank_icons,
@@ -142,12 +145,15 @@ impl FavoritesPage {
142145
sf: SFader::new(),
143146
next_page: None,
144147

148+
chosen_cover,
149+
145150
upload_task: None,
146151
delete_from_cloud_task: None,
147152
set_public_task: None,
148153
sync_task: None,
149154
import_task: None,
150155
batch_import_task: None,
156+
set_cover_task: None,
151157
};
152158
page.rebuild_folders();
153159
page
@@ -239,6 +245,7 @@ impl FavoritesPage {
239245
|| self.sync_task.is_some()
240246
|| self.import_task.is_some()
241247
|| self.batch_import_task.is_some()
248+
|| self.set_cover_task.is_some()
242249
}
243250

244251
fn collect_chart_ids(&self, col: &LocalCollection, allow_local: bool) -> Option<Vec<i32>> {
@@ -400,6 +407,7 @@ impl Page for FavoritesPage {
400407
if col.is_owned() {
401408
options.push("rename");
402409
options.push("set-description");
410+
options.push("set-cover");
403411
}
404412
if !is_default && col.is_owned() {
405413
options.push("set-as-default");
@@ -477,6 +485,42 @@ impl Page for FavoritesPage {
477485
folder.cover.settle(t);
478486
}
479487

488+
if let Some(chosen_cover) = self.chosen_cover.take() {
489+
let data = get_data_mut();
490+
let col = &mut data.collections[self.active_folder.unwrap()];
491+
match chosen_cover {
492+
Ok(chart_id) => {
493+
let col_id = col.id;
494+
self.set_cover_task = Some(Task::new(async move {
495+
if let Some(col_id) = col_id {
496+
// Collection is synced, update cloud directly
497+
let resp: Collection =
498+
recv_raw(Client::request(Method::PATCH, format!("/collection/{col_id}")).json(&CollectionPatch::Cover(chart_id)))
499+
.await?
500+
.json()
501+
.await?;
502+
Ok(Ok(resp))
503+
} else {
504+
// Collection is not synced, fetch chart
505+
// illustration and set as cover locally
506+
let chart = Ptr::<Chart>::new(chart_id).fetch().await?;
507+
Ok(Err(chart.illustration.clone()))
508+
}
509+
}));
510+
}
511+
Err(local_path) => {
512+
if col.id.is_some() {
513+
Dialog::simple(ttl!("favorites-online-only")).show();
514+
} else {
515+
col.cover = CollectionCover::LocalChart(local_path);
516+
let _ = save_data();
517+
show_message(tl!("updated")).ok();
518+
self.rebuild_folders();
519+
}
520+
}
521+
}
522+
}
523+
480524
// 处理输入事件 || Handle input events
481525
if let Some((id, text)) = take_input() {
482526
match id.as_str() {
@@ -578,6 +622,12 @@ impl Page for FavoritesPage {
578622
"set-description" => {
579623
request_input("fav_description", &data.collections[index].description);
580624
}
625+
"set-cover" => {
626+
FAV_PAGE_RESULT.with(|it| *it.borrow_mut() = Some(Some(index)));
627+
CHOOSE_COVER.store(true, Ordering::Relaxed);
628+
show_message(tl!("select-cover"));
629+
self.next_page = Some(NextPage::Pop);
630+
}
581631
"duplicate" => {
582632
let col = &data.collections[index];
583633
data.collections.push(LocalCollection {
@@ -822,6 +872,33 @@ impl Page for FavoritesPage {
822872
self.batch_import_task = None;
823873
}
824874
}
875+
if let Some(task) = &mut self.set_cover_task {
876+
if let Some(result) = task.take() {
877+
match result {
878+
Ok(Ok(col)) => {
879+
let data = get_data_mut();
880+
let local = &mut data.collections[self.active_folder.unwrap()];
881+
local.id = Some(col.id);
882+
local.assign_from(&col);
883+
let _ = save_data();
884+
show_message(tl!("updated")).ok();
885+
self.rebuild_folders();
886+
}
887+
Ok(Err(cover)) => {
888+
let data = get_data_mut();
889+
let col = &mut data.collections[self.active_folder.unwrap()];
890+
col.cover = CollectionCover::Online(cover);
891+
let _ = save_data();
892+
show_message(tl!("updated")).ok();
893+
self.rebuild_folders();
894+
}
895+
Err(err) => {
896+
show_error(err);
897+
}
898+
}
899+
self.set_cover_task = None;
900+
}
901+
}
825902

826903
Ok(())
827904
}

phira/src/page/library.rs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use prpr::{
2424
use std::{
2525
any::Any,
2626
borrow::Cow,
27+
cell::RefCell,
2728
ops::Deref,
2829
sync::{
2930
atomic::{AtomicBool, Ordering},
@@ -33,6 +34,11 @@ use std::{
3334
use tap::Tap;
3435

3536
pub static FAV_UPDATED: AtomicBool = AtomicBool::new(false);
37+
pub static CHOOSE_COVER: AtomicBool = AtomicBool::new(false);
38+
39+
thread_local! {
40+
pub static CHOSEN_COVER: RefCell<Option<Result<i32, String>>> = const { RefCell::new(None) };
41+
}
3642

3743
const PAGE_NUM: u64 = 28;
3844

@@ -306,18 +312,21 @@ impl Page for LibraryPage {
306312

307313
fn touch(&mut self, touch: &Touch, s: &mut SharedState) -> Result<bool> {
308314
let t = s.t;
309-
if self.order_menu.showing() {
310-
self.order_menu.touch(touch, t);
311-
return Ok(true);
312-
}
313-
if self.tabs.touch(touch, s.rt) {
314-
return Ok(true);
315-
}
316-
if self.tags.touch(touch, t) {
317-
return Ok(true);
318-
}
319-
if self.rating.touch(touch, t) {
320-
return Ok(true);
315+
let choose_cover = CHOOSE_COVER.load(Ordering::Relaxed);
316+
if !choose_cover {
317+
if self.order_menu.showing() {
318+
self.order_menu.touch(touch, t);
319+
return Ok(true);
320+
}
321+
if self.tabs.touch(touch, s.rt) {
322+
return Ok(true);
323+
}
324+
if self.tags.touch(touch, t) {
325+
return Ok(true);
326+
}
327+
if self.rating.touch(touch, t) {
328+
return Ok(true);
329+
}
321330
}
322331
let charts_view = &mut self.tabs.selected_mut().view;
323332
if charts_view.transiting() {
@@ -326,6 +335,9 @@ impl Page for LibraryPage {
326335
if charts_view.touch(touch, t, s.rt)? {
327336
return Ok(true);
328337
}
338+
if choose_cover {
339+
return Ok(true);
340+
}
329341
if !matches!(self.tabs.selected().ty, ChartListType::Local) {
330342
if self.prev_page_btn.touch(touch, t) {
331343
if self.current_page != 0 {
@@ -351,7 +363,7 @@ impl Page for LibraryPage {
351363
}
352364
if self.fav_btn.touch(touch, t) {
353365
self.next_page =
354-
Some(NextPage::Overlay(Box::new(FavoritesPage::new(self.icons.clone(), self.rank_icons.clone(), self.current_fav_index))));
366+
Some(NextPage::Overlay(Box::new(FavoritesPage::new(self.icons.clone(), self.rank_icons.clone(), self.current_fav_index, None))));
355367
return Ok(true);
356368
}
357369
if !self.search_str.is_empty() && self.search_clr_btn.touch(touch) {
@@ -398,6 +410,16 @@ impl Page for LibraryPage {
398410
fn update(&mut self, s: &mut SharedState) -> Result<()> {
399411
let t = s.t;
400412

413+
if let Some(chosen_cover) = CHOSEN_COVER.with(|it| it.borrow_mut().take()) {
414+
CHOOSE_COVER.store(false, Ordering::Relaxed);
415+
self.next_page = Some(NextPage::Overlay(Box::new(FavoritesPage::new(
416+
self.icons.clone(),
417+
self.rank_icons.clone(),
418+
self.current_fav_index,
419+
Some(chosen_cover),
420+
))));
421+
}
422+
401423
// 在 update 中处理收藏夹选择结果,绕开fader那个的0.7秒延迟 || Handle the favorites folder selection result in update, bypassing the 0.7-second delay of the fader
402424
if let Some(result) = FAV_PAGE_RESULT.with(|it| it.borrow_mut().take()) {
403425
self.current_fav_index = result;

phira/src/scene/song.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,7 +1312,7 @@ impl SongScene {
13121312

13131313
fn toggle_in(&mut self, col: &mut LocalCollection) {
13141314
if col.id.is_some() && self.info.id.is_none() {
1315-
show_message(tl!("favorites-local-only")).ok();
1315+
Dialog::simple(ttl!("favorites-online-only")).show();
13161316
return;
13171317
}
13181318

@@ -1892,8 +1892,11 @@ impl Scene for SongScene {
18921892
}
18931893
if self.fav_menu.changed() {
18941894
let selected = self.fav_menu.selected();
1895+
self.fav_menu.set_selected(usize::MAX);
18951896
self.toggle_in(&mut get_data_mut().collections[self.fav_menu_options[selected]]);
18961897
let _ = save_data();
1898+
let options = self.get_fav_menu_options();
1899+
self.fav_menu.set_options(options);
18971900
}
18981901
if self.chart_should_delete.fetch_and(false, Ordering::Relaxed) {
18991902
let id = self.info.id.unwrap();

0 commit comments

Comments
 (0)