11use crate :: DiffSource ;
2+ use crate :: github:: octokit:: RepoClient ;
3+ use crate :: state:: { AppStateRef , SystemCommand } ;
24use eframe:: egui;
3- use eframe:: egui:: { Button , Context , ScrollArea , Spinner } ;
5+ use eframe:: egui:: { Context , Popup , ScrollArea , Spinner } ;
46use egui_inbox:: UiInbox ;
57use futures:: TryStreamExt as _;
68use futures:: stream:: FuturesUnordered ;
79use graphql_client:: GraphQLQuery ;
810use octocrab:: Octocrab ;
11+ use octocrab:: models:: { RunId , workflows:: WorkflowListArtifact } ;
912use re_ui:: egui_ext:: boxed_widget:: BoxedWidgetLocalExt as _;
13+ use re_ui:: list_item:: { LabelContent , ListItemContentButtonsExt as _, list_item_scope} ;
14+ use re_ui:: { SectionCollapsingHeader , UiExt as _, icons} ;
1015use std:: collections:: hash_map:: Entry ;
1116use std:: collections:: { HashMap , HashSet } ;
1217use 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} ;
19- // use chrono::DateTime;
2018pub type GitObjectID = String ;
2119pub type DateTime = String ;
2220#[ expect( clippy:: upper_case_acronyms) ]
2321pub type URI = String ;
2422
25- // The paths are relative to the directory where your `Cargo.toml` is located.
26- // Both json and the GraphQL schema language are supported as sources for the schema
2723#[ derive( GraphQLQuery , Debug ) ]
2824#[ graphql(
2925 schema_path = "github.graphql" ,
@@ -33,6 +29,8 @@ pub type URI = String;
3329pub struct PrDetailsQuery ;
3430use crate :: github:: model:: { GithubArtifactLink , GithubPrLink , PrNumber } ;
3531use anyhow:: { Error , Result , anyhow} ;
32+ use eframe:: emath:: RectAlign ;
33+ use re_ui:: menu:: menu_style;
3634
3735pub fn parse_github_pr_url ( url : & str ) -> Result < ( String , String , u32 ) , String > {
3836 // Parse URLs like: https://github.com/rerun-io/rerun/pull/11253
@@ -162,27 +160,22 @@ impl GithubPr {
162160 Entry :: Occupied ( _) => { }
163161 Entry :: Vacant ( entry) => {
164162 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- } ) ;
184163 }
185164 }
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 = RepoClient :: new ( self . client . clone ( ) , self . link . repo . clone ( ) ) ;
174+ self . inbox . spawn ( move |tx| async move {
175+ let artifacts = fetch_commit_artifacts ( & client, workflow_run_ids) . await ;
176+ tx. send ( GithubPrCommand :: FetchedCommitArtifacts { sha, artifacts } )
177+ . ok ( ) ;
178+ } ) ;
186179 }
187180 }
188181 }
@@ -335,67 +328,72 @@ pub fn pr_ui(ui: &mut egui::Ui, state: &AppStateRef<'_>, pr: &GithubPr) {
335328 let item = ui. list_item ( ) ;
336329
337330 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 ( ) ,
331+ CommitState :: Failure => icons :: ERROR
332+ . as_image ( )
333+ . tint ( ui . tokens ( ) . alert_error . icon )
334+ . boxed_local ( ) ,
342335 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 ( ) ,
336+ CommitState :: Success => icons:: SUCCESS
337+ . as_image ( )
338+ . tint ( ui. tokens ( ) . alert_success . icon )
339+ . boxed_local ( ) ,
349340 } ;
350341
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-
394342 let content = LabelContent :: new ( & commit. message )
395343 . with_button ( button)
396344 . with_always_show_buttons ( true ) ;
397345
398- item. show_hierarchical ( ui, content) ;
346+ let response = item. show_hierarchical ( ui, content) ;
347+ if response. clicked ( ) {
348+ pr. inbox
349+ . sender ( )
350+ . send ( GithubPrCommand :: FetchCommitArtifacts {
351+ sha : commit. sha . clone ( ) ,
352+ } )
353+ . ok ( ) ;
354+ }
355+ Popup :: menu ( & response)
356+ . align ( RectAlign :: BOTTOM_END )
357+ . style ( menu_style ( ) )
358+ . show ( |ui| {
359+ ui. set_min_width ( 250.0 ) ;
360+ match data. artifacts . get ( & commit. sha ) {
361+ None => {
362+ // Loading should be triggered by the click handler above
363+ }
364+ Some ( Poll :: Pending ) => {
365+ ui. spinner ( ) ;
366+ }
367+ Some ( Poll :: Ready ( Err ( error) ) ) => {
368+ ui. colored_label (
369+ ui. visuals ( ) . error_fg_color ,
370+ format ! ( "Error: {error}" ) ,
371+ ) ;
372+ }
373+ #[ expect( clippy:: excessive_nesting) ]
374+ Some ( Poll :: Ready ( Ok ( artifacts) ) ) => {
375+ if artifacts. is_empty ( ) {
376+ ui. label ( "No artifacts found" ) ;
377+ } else {
378+ for artifact in artifacts {
379+ if ui. button ( & artifact. data . name ) . clicked ( ) {
380+ selected_source = Some ( DiffSource :: GHArtifact (
381+ GithubArtifactLink {
382+ repo : pr. link . repo . clone ( ) ,
383+ artifact_id : artifact. data . id ,
384+ name : Some ( artifact. data . name . clone ( ) ) ,
385+ branch_name : Some (
386+ data. head_branch . clone ( ) ,
387+ ) ,
388+ run_id : Some ( artifact. run_id ) ,
389+ } ,
390+ ) ) ;
391+ }
392+ }
393+ }
394+ }
395+ }
396+ } ) ;
399397 }
400398 } ) ;
401399 } ) ;
0 commit comments