11prpr_l10n:: tl_file!( "message" ) ;
22
3- use std:: borrow:: Cow ;
3+ use std:: { borrow:: Cow , sync :: Arc } ;
44
55use super :: { Page , SharedState } ;
66use crate :: {
7- client:: { recv_raw, Client , Message } ,
8- get_data, get_data_mut, save_data,
7+ client:: { recv_raw, Chart , Client , Message , Ptr } ,
8+ get_data, get_data_mut,
9+ icons:: Icons ,
10+ page:: { ChartItem , SFader } ,
11+ save_data,
12+ scene:: { ProfileScene , SongScene } ,
913} ;
1014use anyhow:: Result ;
1115use chrono:: Local ;
1216use macroquad:: prelude:: * ;
1317use prpr:: {
14- ext:: { semi_black, semi_white, RectExt } ,
18+ ext:: { open_url , semi_black, semi_white, RectExt , SafeTexture } ,
1519 scene:: show_error,
1620 task:: Task ,
1721 ui:: { DRectButton , Scroll , Ui } ,
@@ -25,10 +29,17 @@ pub struct MessagePage {
2529
2630 btns_scroll : Scroll ,
2731 scroll : Scroll ,
32+
33+ action_btns : Vec < DRectButton > ,
34+
35+ sf : SFader ,
36+ chart_task : Option < Task < Result < Arc < Chart > > > > ,
37+ icons : Arc < Icons > ,
38+ rank_icons : [ SafeTexture ; 8 ] ,
2839}
2940
3041impl MessagePage {
31- pub fn new ( ) -> Self {
42+ pub fn new ( icons : Arc < Icons > , rank_icons : [ SafeTexture ; 8 ] ) -> Self {
3243 Self {
3344 msgs : None ,
3445 load_task : None ,
@@ -37,6 +48,13 @@ impl MessagePage {
3748
3849 btns_scroll : Scroll :: new ( ) ,
3950 scroll : Scroll :: new ( ) ,
51+
52+ action_btns : Vec :: new ( ) ,
53+
54+ sf : SFader :: new ( ) ,
55+ chart_task : None ,
56+ icons,
57+ rank_icons,
4058 }
4159 }
4260
@@ -53,6 +71,46 @@ impl MessagePage {
5371 Ok ( recv_raw ( req) . await ?. json ( ) . await ?)
5472 } ) ) ;
5573 }
74+
75+ fn execute_action ( & mut self , t : f32 , action : String ) -> Result < ( ) > {
76+ let ( ty, param) = match action. split_once ( ':' ) {
77+ Some ( it) => it,
78+ None => {
79+ warn ! ( "invalid action: {action}" ) ;
80+ return Ok ( ( ) ) ;
81+ }
82+ } ;
83+ match ty {
84+ "url" => {
85+ open_url ( param) ?;
86+ }
87+ "chart" => {
88+ let id = match param. parse :: < i32 > ( ) {
89+ Ok ( it) => it,
90+ Err ( _) => {
91+ warn ! ( "invalid chart id: {param}" ) ;
92+ return Ok ( ( ) ) ;
93+ }
94+ } ;
95+ self . chart_task = Some ( Task :: new ( async move { Ptr :: < Chart > :: new ( id) . fetch ( ) . await } ) ) ;
96+ }
97+ "user" => {
98+ let id = match param. parse :: < i32 > ( ) {
99+ Ok ( it) => it,
100+ Err ( _) => {
101+ warn ! ( "invalid user id: {param}" ) ;
102+ return Ok ( ( ) ) ;
103+ }
104+ } ;
105+ self . sf . goto ( t, ProfileScene :: new ( id, self . icons . user . clone ( ) , self . rank_icons . clone ( ) ) ) ;
106+ }
107+ _ => {
108+ warn ! ( "unknown action type: {ty}" ) ;
109+ }
110+ }
111+
112+ Ok ( ( ) )
113+ }
56114}
57115
58116impl Page for MessagePage {
@@ -67,6 +125,9 @@ impl Page for MessagePage {
67125
68126 fn touch ( & mut self , touch : & Touch , s : & mut SharedState ) -> Result < bool > {
69127 let t = s. t ;
128+ if self . chart_task . is_some ( ) {
129+ return Ok ( false ) ;
130+ }
70131 if self . load_task . is_none ( ) {
71132 if self . btns_scroll . touch ( touch, t) {
72133 return Ok ( true ) ;
@@ -86,6 +147,15 @@ impl Page for MessagePage {
86147 return Ok ( true ) ;
87148 }
88149 }
150+ if let Some ( index) = self . index {
151+ for ( btn, action) in self . action_btns . iter_mut ( ) . zip ( & msgs[ index] . 0 . actions ) {
152+ if btn. touch ( touch, t) {
153+ let action = action. action . clone ( ) ;
154+ self . execute_action ( t, action) ?;
155+ return Ok ( true ) ;
156+ }
157+ }
158+ }
89159 }
90160 }
91161 if self . scroll . touch ( touch, t) {
@@ -118,6 +188,29 @@ impl Page for MessagePage {
118188 self . load_task = None ;
119189 }
120190 }
191+ if self . chart_task . is_some ( ) {
192+ if let Some ( res) = self . chart_task . as_mut ( ) . unwrap ( ) . take ( ) {
193+ match res {
194+ Err ( err) => {
195+ show_error ( err) ;
196+ }
197+ Ok ( chart) => {
198+ let data = get_data ( ) ;
199+ let ( local_path, mods) = data
200+ . charts
201+ . iter ( )
202+ . find ( |it| it. info . id == Some ( chart. id ) )
203+ . map ( |it| ( Some ( it. local_path . clone ( ) ) , it. mods ) )
204+ . unwrap_or_default ( ) ;
205+ self . sf . goto (
206+ t,
207+ SongScene :: new ( ChartItem :: from_remote ( chart. as_ref ( ) ) , local_path, self . icons . clone ( ) , self . rank_icons . clone ( ) , mods) ,
208+ ) ;
209+ }
210+ }
211+ self . chart_task = None ;
212+ }
213+ }
121214 Ok ( ( ) )
122215 }
123216
@@ -162,10 +255,24 @@ impl Page for MessagePage {
162255 } ) ;
163256 s. render_fader ( ui, |ui| {
164257 ui. fill_path ( & cr. rounded ( 0.005 ) , semi_black ( 0.4 ) ) ;
165- let pad = 0.03 ;
166- ui. dx ( cr. x + pad + 0.01 ) ;
167- ui. dy ( cr. y + pad) ;
258+
168259 if let Some ( msg) = self . index . and_then ( |it| self . msgs . as_ref ( ) . map ( |msgs| & msgs[ it] . 0 ) ) {
260+ let pad = 0.03 ;
261+ ui. scope ( |ui| {
262+ ui. dx ( cr. right ( ) - pad) ;
263+ ui. dy ( cr. bottom ( ) - pad) ;
264+ self . action_btns . resize_with ( msg. actions . len ( ) , DRectButton :: new) ;
265+ let mut r = Rect :: new ( 0. , 0. , 0.28 , 0.1 ) ;
266+ r. x -= r. w ;
267+ r. y -= r. h ;
268+ for ( btn, action) in self . action_btns . iter_mut ( ) . zip ( & msg. actions ) {
269+ btn. render_text ( ui, r, t, & action. name , 0.5 , false ) ;
270+ ui. dy ( -r. h - 0.01 ) ;
271+ }
272+ } ) ;
273+
274+ ui. dx ( cr. x + pad + 0.01 ) ;
275+ ui. dy ( cr. y + pad) ;
169276 let mw = cr. w - pad * 2. - 0.01 ;
170277 let mut h = 0. ;
171278 macro_rules! dy {
@@ -193,6 +300,14 @@ impl Page for MessagePage {
193300 } ) ;
194301 }
195302 } ) ;
303+ self . sf . render ( ui, t) ;
304+ if self . chart_task . is_some ( ) {
305+ ui. full_loading_simple ( t) ;
306+ }
196307 Ok ( ( ) )
197308 }
309+
310+ fn next_scene ( & mut self , s : & mut SharedState ) -> prpr:: scene:: NextScene {
311+ self . sf . next_scene ( s. t ) . unwrap_or_default ( )
312+ }
198313}
0 commit comments