33using System . IO ;
44using System . Net . Http ;
55using System . Text ;
6+ using System . Threading ;
7+ using System . Threading . Tasks ;
68using System . Windows ;
79using System . Windows . Controls ;
810using System . Windows . Input ;
@@ -27,6 +29,7 @@ public partial class NewProject : Window
2729 int previousSelectedModuleIndex = - 1 ;
2830
2931 public static string targetFolder { get ; private set ; } = null ;
32+ private CancellationTokenSource _templateLoadCancellation ;
3033
3134 public NewProject ( string unityVersion , string suggestedName , string targetFolder , bool nameIsLocked = false )
3235 {
@@ -35,9 +38,6 @@ public NewProject(string unityVersion, string suggestedName, string targetFolder
3538
3639 NewProject . targetFolder = targetFolder ;
3740
38- // TODO could optionally disable templates in settings
39- _ = LoadOnlineTemplatesAsync ( ) ;
40-
4141 LoadSettings ( ) ;
4242
4343 // get version
@@ -65,6 +65,9 @@ public NewProject(string unityVersion, string suggestedName, string targetFolder
6565 {
6666 gridAvailableVersions . SelectedIndex = i ;
6767 gridAvailableVersions . ScrollIntoView ( gridAvailableVersions . SelectedItem ) ;
68+
69+ string baseVersion = GetBaseVersion ( newVersion ) ;
70+ _ = LoadOnlineTemplatesAsync ( baseVersion ) ;
6871 break ;
6972 }
7073 }
@@ -309,6 +312,23 @@ private void GridAvailableVersions_SelectionChanged(object sender, SelectionChan
309312 lblOverride . Visibility = chkForceDX11 . Visibility = is6000 ? Visibility . Visible : Visibility . Collapsed ;
310313 //chkForceDX11.IsChecked = chkForceDX11.Visibility == Visibility.Visible ? forceDX11 : false;
311314 forceDX11 = Settings . Default . forceDX11 && is6000 ;
315+
316+ string baseVersion = GetBaseVersion ( k . Version ) ;
317+ // Cancel previous request
318+ _templateLoadCancellation ? . Cancel ( ) ;
319+ _templateLoadCancellation = new CancellationTokenSource ( ) ;
320+ _ = LoadOnlineTemplatesAsync ( baseVersion , _templateLoadCancellation . Token ) ;
321+ }
322+
323+ string GetBaseVersion ( string version )
324+ {
325+ // e.g. 2020.3.15f1 -> 2020.3
326+ var parts = version . Split ( '.' ) ;
327+ if ( parts . Length >= 2 )
328+ {
329+ return parts [ 0 ] + "." + parts [ 1 ] ;
330+ }
331+ return version ;
312332 }
313333
314334 private void GridAvailableVersions_Loaded ( object sender , RoutedEventArgs e )
@@ -418,47 +438,91 @@ private void btnCreateMissingFolder_Click(object sender, RoutedEventArgs e)
418438 }
419439 }
420440
421- private async System . Threading . Tasks . Task LoadOnlineTemplatesAsync ( )
441+ private async Task LoadOnlineTemplatesAsync ( string baseVersion , CancellationToken cancellationToken = default )
422442 {
423443 try
424444 {
425445 using ( var client = new HttpClient ( ) )
426446 {
427447 client . DefaultRequestHeaders . Add ( "Accept" , "application/json" ) ;
428448
429- // Build JSON manually
430- var graphqlJson = "{\" query\" :\" fragment TemplateEntity on Template { __typename name packageName description type buildPlatforms renderPipeline previewImage { url } versions { name tarball { url } } } query HUB__getTemplates($limit: Int! $skip: Int! $orderBy: TemplateOrder! $supportedUnityEditorVersions: [String!]!) { getTemplates(limit: $limit skip: $skip orderBy: $orderBy supportedUnityEditorVersions: $supportedUnityEditorVersions) { edges { node { ...TemplateEntity } } } }\" ,\" variables\" :{\" limit\" :50,\" skip\" :0,\" orderBy\" :\" WEIGHTED_DESC\" ,\" supportedUnityEditorVersions\" :[\" 6000.0\" ]}}" ;
449+ var graphqlJson = "{\" query\" :\" fragment TemplateEntity on Template { __typename name packageName description type buildPlatforms renderPipeline previewImage { url } versions { name tarball { url } } } query HUB__getTemplates($limit: Int! $skip: Int! $orderBy: TemplateOrder! $supportedUnityEditorVersions: [String!]!) { getTemplates(limit: $limit skip: $skip orderBy: $orderBy supportedUnityEditorVersions: $supportedUnityEditorVersions) { edges { node { ...TemplateEntity } } } }\" ,\" variables\" :{\" limit\" :40,\" skip\" :0,\" orderBy\" :\" WEIGHTED_DESC\" ,\" supportedUnityEditorVersions\" :[\" " + baseVersion + "\" ]}}" ;
431450
432451 var content = new StringContent ( graphqlJson , Encoding . UTF8 , "application/json" ) ;
433452
434- var response = await client . PostAsync ( "https://live-platform-api.prd.ld.unity3d.com/graphql" , content ) ;
453+ // Check for cancellation before making request
454+ if ( cancellationToken . IsCancellationRequested ) return ;
455+
456+ var response = await client . PostAsync ( "https://live-platform-api.prd.ld.unity3d.com/graphql" , content , cancellationToken ) ;
457+
458+ // Check for cancellation after request
459+ if ( cancellationToken . IsCancellationRequested ) return ;
435460
436461 if ( response . IsSuccessStatusCode )
437462 {
438463 var responseString = await response . Content . ReadAsStringAsync ( ) ;
464+
465+ // Check for cancellation before parsing
466+ if ( cancellationToken . IsCancellationRequested ) return ;
467+
439468 var templates = ParseTemplatesFromJson ( responseString ) ;
440469
441- // Update UI on dispatcher thread
442- Dispatcher . Invoke ( ( ) =>
470+ // Update UI on dispatcher thread only if not cancelled
471+ if ( ! cancellationToken . IsCancellationRequested )
443472 {
444- listOnlineTemplates . Items . Clear ( ) ;
445- listOnlineTemplates . ItemsSource = templates ;
446- } ) ;
473+ Dispatcher . Invoke ( ( ) =>
474+ {
475+ // Only set ItemsSource, don't touch Items
476+ listOnlineTemplates . ItemsSource = templates ;
477+ } ) ;
478+ }
447479 }
448480 else
449481 {
450482 Console . WriteLine ( $ "GraphQL request failed: { response . StatusCode } ") ;
451- LoadFallbackTemplates ( ) ;
483+ if ( ! cancellationToken . IsCancellationRequested )
484+ {
485+ LoadFallbackTemplates ( ) ;
486+ }
452487 }
453488 }
454489 }
490+ catch ( OperationCanceledException )
491+ {
492+ // Request was cancelled, this is expected
493+ Console . WriteLine ( "Template loading cancelled" ) ;
494+ }
455495 catch ( Exception ex )
456496 {
457- Console . WriteLine ( $ "Error loading online templates: { ex . Message } ") ;
458- LoadFallbackTemplates ( ) ;
497+ if ( ! cancellationToken . IsCancellationRequested )
498+ {
499+ Console . WriteLine ( $ "Error loading online templates: { ex . Message } ") ;
500+ LoadFallbackTemplates ( ) ;
501+ }
459502 }
460503 }
461504
505+ private void LoadFallbackTemplates ( )
506+ {
507+ var templates = new List < OnlineTemplateItem >
508+ {
509+ new OnlineTemplateItem
510+ {
511+ Name = "3D Template" ,
512+ Description = "A great starting point for 3D projects using the Universal Render Pipeline (URP)." ,
513+ PreviewImageURL = "pack://application:,,,/Images/icon.png" ,
514+ Type = "CORE" ,
515+ RenderPipeline = "URP"
516+ }
517+ } ;
518+
519+ Dispatcher . Invoke ( ( ) =>
520+ {
521+ // Only set ItemsSource, don't use Items.Clear()
522+ listOnlineTemplates . ItemsSource = templates ;
523+ } ) ;
524+ }
525+
462526 private List < OnlineTemplateItem > ParseTemplatesFromJson ( string json )
463527 {
464528 var templates = new List < OnlineTemplateItem > ( ) ;
@@ -576,26 +640,5 @@ private int FindMatchingBrace(string json, int openBraceIndex)
576640
577641 return - 1 ;
578642 }
579-
580- private void LoadFallbackTemplates ( )
581- {
582- var templates = new List < OnlineTemplateItem >
583- {
584- new OnlineTemplateItem
585- {
586- Name = "3D Template" ,
587- Description = "A great starting point for 3D projects using the Universal Render Pipeline (URP)." ,
588- PreviewImageURL = "pack://application:,,,/Images/icon.png" ,
589- Type = "CORE" ,
590- RenderPipeline = "URP"
591- }
592- } ;
593-
594- Dispatcher . Invoke ( ( ) =>
595- {
596- listOnlineTemplates . Items . Clear ( ) ;
597- listOnlineTemplates . ItemsSource = templates ;
598- } ) ;
599- }
600- }
601- }
643+ } // class NewProject
644+ } // namespace UnityLauncherPro
0 commit comments