11use tauri:: AppHandle ;
2- use tauri:: menu:: { CheckMenuItem , IconMenuItem , Menu , MenuItem , PredefinedMenuItem } ;
2+ use tauri:: menu:: { CheckMenuItem , IconMenuItem , Menu , MenuItem , PredefinedMenuItem , Submenu } ;
33use tauri_plugin_autostart:: ManagerExt ;
44
55use crate :: avatars:: AvatarCache ;
@@ -8,6 +8,7 @@ use crate::models::{CheckStatus, PrState, PullRequest};
88struct PrSection {
99 title : String ,
1010 prs : Vec < PullRequest > ,
11+ default_collapsed : bool ,
1112}
1213
1314/// Port of src/lib/stores.ts groupPrs() with tray-specific section ordering.
@@ -42,6 +43,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
4243 . filter( |pr| pr. state == PrState :: Open && pr. check_status == CheckStatus :: Success )
4344 . cloned( )
4445 . collect( ) ,
46+ default_collapsed: false ,
4547 } ,
4648 PrSection {
4749 title: "In Merge Queue" . into( ) ,
@@ -50,6 +52,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
5052 . filter( |pr| pr. merge_queue_info. is_some( ) )
5153 . cloned( )
5254 . collect( ) ,
55+ default_collapsed: false ,
5356 } ,
5457 PrSection {
5558 title: "Checks Failing" . into( ) ,
@@ -62,6 +65,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
6265 } )
6366 . cloned( )
6467 . collect( ) ,
68+ default_collapsed: false ,
6569 } ,
6670 PrSection {
6771 title: "Changes Requested" . into( ) ,
@@ -75,6 +79,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
7579 } )
7680 . cloned( )
7781 . collect( ) ,
82+ default_collapsed: false ,
7883 } ,
7984 PrSection {
8085 title: "Waiting for Review" . into( ) ,
@@ -89,6 +94,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
8994 } )
9095 . cloned( )
9196 . collect( ) ,
97+ default_collapsed: false ,
9298 } ,
9399 PrSection {
94100 title: "Approved" . into( ) ,
@@ -102,10 +108,12 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
102108 } )
103109 . cloned( )
104110 . collect( ) ,
111+ default_collapsed: false ,
105112 } ,
106113 PrSection {
107114 title: "Draft" . into( ) ,
108115 prs: drafts,
116+ default_collapsed: false ,
109117 } ,
110118 PrSection {
111119 title: "Recently Merged" . into( ) ,
@@ -114,6 +122,7 @@ fn group_prs(all_prs: &[PullRequest]) -> Vec<PrSection> {
114122 . filter( |pr| pr. state == PrState :: Merged )
115123 . cloned( )
116124 . collect( ) ,
125+ default_collapsed: true ,
117126 } ,
118127 ]
119128}
@@ -139,42 +148,80 @@ pub fn build_pr_menu(
139148 }
140149 first = false ;
141150
142- // Section header (disabled)
143- let header_text = format ! ( "{} ({})" , section. title, section. prs. len( ) ) ;
144- let header = MenuItem :: with_id (
145- app,
146- & format ! ( "header_{}" , section. title) ,
147- & header_text,
148- false ,
149- None :: < & str > ,
150- ) ?;
151- menu. append ( & header) ?;
151+ if section. default_collapsed {
152+ let sub_title = format ! ( "{} ({})" , section. title, section. prs. len( ) ) ;
153+ let submenu = Submenu :: with_id (
154+ app,
155+ & format ! ( "section_{}" , section. title) ,
156+ & sub_title,
157+ true ,
158+ ) ?;
159+
160+ let show_count = section. prs . len ( ) . min ( 5 ) ;
161+ for pr in & section. prs [ ..show_count] {
162+ let age = time_ago ( & pr. created_at ) ;
163+ let mut label = format ! (
164+ " {} #{} — {}" ,
165+ pr. repository,
166+ pr. number,
167+ truncate( & pr. title, 32 ) ,
168+ ) ;
169+ if pr. comment_count > 0 {
170+ label. push_str ( & format ! ( " 💬{}" , pr. comment_count) ) ;
171+ }
172+ label. push_str ( & format ! ( " {}" , age) ) ;
152173
153- // PR items (max 5 per section)
154- let show_count = section. prs . len ( ) . min ( 5 ) ;
155- for pr in & section. prs [ ..show_count] {
156- let age = time_ago ( & pr. created_at ) ;
157- let mut label = format ! (
158- " {} #{} — {}" ,
159- pr. repository,
160- pr. number,
161- truncate( & pr. title, 32 ) ,
162- ) ;
163- if pr. comment_count > 0 {
164- label. push_str ( & format ! ( " 💬{}" , pr. comment_count) ) ;
174+ let icon = avatar_cache. get_image ( & pr. author_login ) ;
175+ let item = IconMenuItem :: with_id (
176+ app,
177+ & format ! ( "pr_{}" , pr. id) ,
178+ & label,
179+ true ,
180+ icon,
181+ None :: < & str > ,
182+ ) ?;
183+ submenu. append ( & item) ?;
165184 }
166- label. push_str ( & format ! ( " {}" , age) ) ;
167185
168- let icon = avatar_cache. get_image ( & pr. author_login ) ;
169- let item = IconMenuItem :: with_id (
186+ menu. append ( & submenu) ?;
187+ } else {
188+ // Section header (disabled)
189+ let header_text = format ! ( "{} ({})" , section. title, section. prs. len( ) ) ;
190+ let header = MenuItem :: with_id (
170191 app,
171- & format ! ( "pr_{}" , pr. id) ,
172- & label,
173- true ,
174- icon,
192+ & format ! ( "header_{}" , section. title) ,
193+ & header_text,
194+ false ,
175195 None :: < & str > ,
176196 ) ?;
177- menu. append ( & item) ?;
197+ menu. append ( & header) ?;
198+
199+ // PR items (max 5 per section)
200+ let show_count = section. prs . len ( ) . min ( 5 ) ;
201+ for pr in & section. prs [ ..show_count] {
202+ let age = time_ago ( & pr. created_at ) ;
203+ let mut label = format ! (
204+ " {} #{} — {}" ,
205+ pr. repository,
206+ pr. number,
207+ truncate( & pr. title, 32 ) ,
208+ ) ;
209+ if pr. comment_count > 0 {
210+ label. push_str ( & format ! ( " 💬{}" , pr. comment_count) ) ;
211+ }
212+ label. push_str ( & format ! ( " {}" , age) ) ;
213+
214+ let icon = avatar_cache. get_image ( & pr. author_login ) ;
215+ let item = IconMenuItem :: with_id (
216+ app,
217+ & format ! ( "pr_{}" , pr. id) ,
218+ & label,
219+ true ,
220+ icon,
221+ None :: < & str > ,
222+ ) ?;
223+ menu. append ( & item) ?;
224+ }
178225 }
179226
180227 }
0 commit comments