1+ use crate :: github:: octokit:: RepoClient ;
2+ use crate :: state:: { AppStateRef , SystemCommand } ;
3+ // Import octocrab models
14use crate :: DiffSource ;
25use eframe:: egui;
3- use eframe:: egui:: { Button , Context , ScrollArea , Spinner } ;
6+ use eframe:: egui:: { Context , Popup , ScrollArea , Spinner } ;
47use egui_inbox:: UiInbox ;
58use futures:: TryStreamExt as _;
69use futures:: stream:: FuturesUnordered ;
710use graphql_client:: GraphQLQuery ;
811use octocrab:: Octocrab ;
12+ use octocrab:: models:: { RunId , workflows:: WorkflowListArtifact } ;
913use re_ui:: egui_ext:: boxed_widget:: BoxedWidgetLocalExt as _;
14+ use re_ui:: list_item:: { LabelContent , ListItemContentButtonsExt as _, list_item_scope} ;
15+ use re_ui:: { SectionCollapsingHeader , UiExt as _, icons} ;
1016use std:: collections:: hash_map:: Entry ;
1117use std:: collections:: { HashMap , HashSet } ;
1218use std:: task:: Poll ;
13- // Import octocrab models
14- use crate :: github:: octokit:: RepoClient ;
15- use crate :: state:: { AppStateRef , SystemCommand } ;
16- use octocrab:: models:: { RunId , workflows:: WorkflowListArtifact } ;
17- use re_ui:: list_item:: { LabelContent , ListItemContentButtonsExt as _, list_item_scope} ;
18- use re_ui:: { OnResponseExt as _, SectionCollapsingHeader , UiExt as _, icons} ;
1919// use chrono::DateTime;
2020pub type GitObjectID = String ;
2121pub type DateTime = String ;
@@ -33,6 +33,8 @@ pub type URI = String;
3333pub struct PrDetailsQuery ;
3434use crate :: github:: model:: { GithubArtifactLink , GithubPrLink , PrNumber } ;
3535use anyhow:: { Error , Result , anyhow} ;
36+ use eframe:: emath:: RectAlign ;
37+ use re_ui:: menu:: menu_style;
3638
3739pub fn parse_github_pr_url ( url : & str ) -> Result < ( String , String , u32 ) , String > {
3840 // Parse URLs like: https://github.com/rerun-io/rerun/pull/11253
@@ -162,27 +164,22 @@ impl GithubPr {
162164 Entry :: Occupied ( _) => { }
163165 Entry :: Vacant ( entry) => {
164166 entry. insert ( Poll :: Pending ) ;
165-
166- let workflow_run_ids = pr_data
167- . commits
168- . iter ( )
169- . find ( |c| c. sha == sha)
170- . map ( |c| c. workflow_run_ids . clone ( ) )
171- . unwrap_or_default ( ) ;
172-
173- let client =
174- RepoClient :: new ( self . client . clone ( ) , self . link . repo . clone ( ) ) ;
175- self . inbox . spawn ( move |tx| async move {
176- let artifacts =
177- fetch_commit_artifacts ( & client, workflow_run_ids) . await ;
178- tx. send ( GithubPrCommand :: FetchedCommitArtifacts {
179- sha,
180- artifacts,
181- } )
182- . ok ( ) ;
183- } ) ;
184167 }
185168 }
169+
170+ let workflow_run_ids = pr_data
171+ . commits
172+ . iter ( )
173+ . find ( |c| c. sha == sha)
174+ . map ( |c| c. workflow_run_ids . clone ( ) )
175+ . unwrap_or_default ( ) ;
176+
177+ let client = RepoClient :: new ( self . client . clone ( ) , self . link . repo . clone ( ) ) ;
178+ self . inbox . spawn ( move |tx| async move {
179+ let artifacts = fetch_commit_artifacts ( & client, workflow_run_ids) . await ;
180+ tx. send ( GithubPrCommand :: FetchedCommitArtifacts { sha, artifacts } )
181+ . ok ( ) ;
182+ } ) ;
186183 }
187184 }
188185 }
@@ -335,67 +332,72 @@ pub fn pr_ui(ui: &mut egui::Ui, state: &AppStateRef<'_>, pr: &GithubPr) {
335332 let item = ui. list_item ( ) ;
336333
337334 let button = match & commit. status {
338- CommitState :: Failure => Button :: image (
339- icons :: ERROR . as_image ( ) . tint ( ui . tokens ( ) . alert_error . icon ) ,
340- )
341- . boxed_local ( ) ,
335+ CommitState :: Failure => icons :: ERROR
336+ . as_image ( )
337+ . tint ( ui . tokens ( ) . alert_error . icon )
338+ . boxed_local ( ) ,
342339 CommitState :: Pending => Spinner :: new ( ) . boxed_local ( ) ,
343- CommitState :: Success => Button :: image (
344- icons:: SUCCESS
345- . as_image ( )
346- . tint ( ui. tokens ( ) . alert_success . icon ) ,
347- )
348- . boxed_local ( ) ,
340+ CommitState :: Success => icons:: SUCCESS
341+ . as_image ( )
342+ . tint ( ui. tokens ( ) . alert_success . icon )
343+ . boxed_local ( ) ,
349344 } ;
350345
351- let button = button. on_menu ( |ui| {
352- ui. set_min_width ( 250.0 ) ;
353- match data. artifacts . get ( & commit. sha ) {
354- None => {
355- pr. inbox
356- . sender ( )
357- . send ( GithubPrCommand :: FetchCommitArtifacts {
358- sha : commit. sha . clone ( ) ,
359- } )
360- . ok ( ) ;
361- }
362- Some ( Poll :: Pending ) => {
363- ui. spinner ( ) ;
364- }
365- Some ( Poll :: Ready ( Err ( error) ) ) => {
366- ui. colored_label (
367- ui. visuals ( ) . error_fg_color ,
368- format ! ( "Error: {error}" ) ,
369- ) ;
370- }
371- #[ expect( clippy:: excessive_nesting) ]
372- Some ( Poll :: Ready ( Ok ( artifacts) ) ) => {
373- if artifacts. is_empty ( ) {
374- ui. label ( "No artifacts found" ) ;
375- } else {
376- for artifact in artifacts {
377- if ui. button ( & artifact. data . name ) . clicked ( ) {
378- selected_source = Some ( DiffSource :: GHArtifact (
379- GithubArtifactLink {
380- repo : pr. link . repo . clone ( ) ,
381- artifact_id : artifact. data . id ,
382- name : Some ( artifact. data . name . clone ( ) ) ,
383- branch_name : Some ( data. head_branch . clone ( ) ) ,
384- run_id : Some ( artifact. run_id ) ,
385- } ,
386- ) ) ;
387- }
388- }
389- }
390- }
391- }
392- } ) ;
393-
394346 let content = LabelContent :: new ( & commit. message )
395347 . with_button ( button)
396348 . with_always_show_buttons ( true ) ;
397349
398- item. show_hierarchical ( ui, content) ;
350+ let response = item. show_hierarchical ( ui, content) ;
351+ if response. clicked ( ) {
352+ pr. inbox
353+ . sender ( )
354+ . send ( GithubPrCommand :: FetchCommitArtifacts {
355+ sha : commit. sha . clone ( ) ,
356+ } )
357+ . ok ( ) ;
358+ }
359+ Popup :: menu ( & response)
360+ . align ( RectAlign :: BOTTOM_END )
361+ . style ( menu_style ( ) )
362+ . show ( |ui| {
363+ ui. set_min_width ( 250.0 ) ;
364+ match data. artifacts . get ( & commit. sha ) {
365+ None => {
366+ // Loading should be triggered by the click handler above
367+ }
368+ Some ( Poll :: Pending ) => {
369+ ui. spinner ( ) ;
370+ }
371+ Some ( Poll :: Ready ( Err ( error) ) ) => {
372+ ui. colored_label (
373+ ui. visuals ( ) . error_fg_color ,
374+ format ! ( "Error: {error}" ) ,
375+ ) ;
376+ }
377+ #[ expect( clippy:: excessive_nesting) ]
378+ Some ( Poll :: Ready ( Ok ( artifacts) ) ) => {
379+ if artifacts. is_empty ( ) {
380+ ui. label ( "No artifacts found" ) ;
381+ } else {
382+ for artifact in artifacts {
383+ if ui. button ( & artifact. data . name ) . clicked ( ) {
384+ selected_source = Some ( DiffSource :: GHArtifact (
385+ GithubArtifactLink {
386+ repo : pr. link . repo . clone ( ) ,
387+ artifact_id : artifact. data . id ,
388+ name : Some ( artifact. data . name . clone ( ) ) ,
389+ branch_name : Some (
390+ data. head_branch . clone ( ) ,
391+ ) ,
392+ run_id : Some ( artifact. run_id ) ,
393+ } ,
394+ ) ) ;
395+ }
396+ }
397+ }
398+ }
399+ }
400+ } ) ;
399401 }
400402 } ) ;
401403 } ) ;
0 commit comments