44using CollapseLauncher . Helper . Animation ;
55using CollapseLauncher . Helper . Metadata ;
66using CollapseLauncher . InstallManager . Base ;
7+ using CollapseLauncher . XAMLs . Theme . CustomControls . UserFeedbackDialog ;
78using CommunityToolkit . WinUI ;
89using Hi3Helper ;
910using Hi3Helper . SentryHelper ;
@@ -1182,7 +1183,7 @@ public static Task<ContentDialogResult> Dialog_GenericWarning()
11821183 ContentDialogTheme . Warning ) ;
11831184 }
11841185
1185- public static async Task < ContentDialogResult > Dialog_ShowUnhandledExceptionMenu ( )
1186+ public static async Task < ContentDialogResult > Dialog_ShowUnhandledExceptionMenu ( bool isUserFeedbackSent = false )
11861187 {
11871188 Button ? copyButton = null ;
11881189
@@ -1196,22 +1197,23 @@ public static async Task<ContentDialogResult> Dialog_ShowUnhandledExceptionMenu(
11961197 . WithHorizontalAlignment ( HorizontalAlignment . Stretch )
11971198 . WithVerticalAlignment ( VerticalAlignment . Stretch )
11981199 . WithRows ( GridLength . Auto , new GridLength ( 1 , GridUnitType . Star ) ,
1199- GridLength . Auto ) ;
1200+ GridLength . Auto )
1201+ . WithColumns ( GridLength . Auto , new GridLength ( 1 , GridUnitType . Star ) ) ;
12001202
1201- _ = rootGrid . AddElementToGridRow ( new TextBlock
1203+ _ = rootGrid . AddElementToGridRowColumn ( new TextBlock
12021204 {
12031205 Text = subtitle ,
12041206 TextWrapping = TextWrapping . Wrap ,
12051207 FontWeight = FontWeights . Medium
1206- } , 0 ) ;
1207- _ = rootGrid . AddElementToGridRow ( new TextBox
1208+ } , 0 , 0 , 0 , 2 ) ;
1209+ _ = rootGrid . AddElementToGridRowColumn ( new TextBox
12081210 {
12091211 IsReadOnly = true ,
12101212 TextWrapping = TextWrapping . Wrap ,
12111213 MaxHeight = 300 ,
12121214 AcceptsReturn = true ,
12131215 Text = exceptionContent
1214- } , 1 ) . WithMargin ( 0d , 8d )
1216+ } , 1 , 0 , 0 , 2 ) . WithMargin ( 0d , 8d )
12151217 . WithHorizontalAlignment ( HorizontalAlignment . Stretch )
12161218 . WithVerticalAlignment ( VerticalAlignment . Stretch ) ;
12171219
@@ -1220,18 +1222,47 @@ public static async Task<ContentDialogResult> Dialog_ShowUnhandledExceptionMenu(
12201222 "" ,
12211223 "FontAwesomeSolid" ,
12221224 "AccentButtonStyle"
1223- ) , 2 )
1224- . WithHorizontalAlignment ( HorizontalAlignment . Center ) ;
1225+ ) . WithHorizontalAlignment (
1226+ HorizontalAlignment . Left
1227+ ) , 2 ) ;
12251228 copyButton . Click += CopyTextToClipboard ;
12261229
1230+ var btnText = isUserFeedbackSent ? Lang . _Misc . ExceptionFeedbackBtn_FeedbackSent :
1231+ ErrorSender . SentryErrorId == Guid . Empty
1232+ ? Lang . _Misc . ExceptionFeedbackBtn_Unavailable
1233+ : Lang . _Misc . ExceptionFeedbackBtn ;
1234+
1235+ Button submitFeedbackButton = rootGrid . AddElementToGridRowColumn ( CollapseUIExt . CreateButtonWithIcon < Button > (
1236+ btnText ,
1237+ "\ue594 " ,
1238+ "FontAwesomeSolid" ,
1239+ "TransparentDefaultButtonStyle" ,
1240+ 14 ,
1241+ 10
1242+ ) . WithMargin ( 8 , 0 , 0 , 0 ) . WithHorizontalAlignment ( HorizontalAlignment . Right ) ,
1243+ 2 , 1 ) ;
1244+
1245+ if ( ErrorSender . SentryErrorId == Guid . Empty || isUserFeedbackSent )
1246+ {
1247+ submitFeedbackButton . IsEnabled = false ;
1248+ }
1249+
1250+ submitFeedbackButton . Click += SubmitFeedbackButton_Click ;
1251+ // TODO: Change button content after feedback is submitted
1252+
12271253 ContentDialogResult result = await SpawnDialog ( title , rootGrid , null ,
12281254 Lang . _UnhandledExceptionPage . GoBackPageBtn1 ,
12291255 null ,
12301256 null ,
12311257 ContentDialogButton . Close ,
1232- ContentDialogTheme . Error ) ;
1258+ ContentDialogTheme . Error ,
1259+ OnLoadedDialog
1260+ ) ;
12331261
12341262 return result ;
1263+
1264+ void OnLoadedDialog ( object ? sender , RoutedEventArgs e )
1265+ => submitFeedbackButton . SetTag ( sender ) ;
12351266 }
12361267 catch ( Exception ex )
12371268 {
@@ -1247,6 +1278,77 @@ public static async Task<ContentDialogResult> Dialog_ShowUnhandledExceptionMenu(
12471278 }
12481279 }
12491280
1281+ // ReSharper disable once AsyncVoidMethod
1282+ private static async void SubmitFeedbackButton_Click ( object sender , RoutedEventArgs e )
1283+ {
1284+ bool isFeedbackSent = false ;
1285+ if ( sender is not Button { Tag : ContentDialog contentDialog } )
1286+ {
1287+ return ;
1288+ }
1289+
1290+ try
1291+ {
1292+ contentDialog . Hide ( ) ;
1293+
1294+ var userTemplate = Lang . _Misc . ExceptionFeedbackTemplate_User ;
1295+ var emailTemplate = Lang . _Misc . ExceptionFeedbackTemplate_Email ;
1296+
1297+ string exceptionContent = $ """
1298+ { userTemplate }
1299+ { emailTemplate }
1300+ { Lang . _Misc . ExceptionFeedbackTemplate_Message }
1301+ ------------------------------------
1302+ """ ;
1303+ string exceptionTitle = $ "{ Lang . _Misc . ExceptionFeedbackTitle } { ErrorSender . ExceptionTitle } ";
1304+
1305+ UserFeedbackDialog feedbackDialog = new UserFeedbackDialog ( contentDialog . XamlRoot )
1306+ {
1307+ Title = exceptionTitle ,
1308+ IsTitleReadOnly = true ,
1309+ Message = exceptionContent
1310+ } ;
1311+ UserFeedbackResult ? feedbackResult = await feedbackDialog . ShowAsync ( ) ;
1312+ // TODO: (Optional) Implement generic user feedback pathway (preferably when SentryErrorId is null
1313+ // Using https://paste.mozilla.org/
1314+ // API Documentation: https://docs.dpaste.org/api/
1315+ // Though im not sure since user will still need to paste the link to us 🤷
1316+
1317+ if ( feedbackResult is null )
1318+ {
1319+ return ;
1320+ }
1321+
1322+ // Parse username and email
1323+ var msg = feedbackResult . Message . Split ( [ '\r ' , '\n ' ] , StringSplitOptions . RemoveEmptyEntries ) ;
1324+ if ( msg . Length <= 4 ) return ; // Do not send feedback if format is not correct
1325+ var user = msg [ 0 ] . Replace ( userTemplate , "" , StringComparison . InvariantCulture ) . Trim ( ) ;
1326+ var email = msg [ 1 ] . Replace ( userTemplate , "" , StringComparison . InvariantCulture ) . Trim ( ) ;
1327+ var feedback = msg . Length > 4 ? string . Join ( "\n " , msg . Skip ( 4 ) ) . Trim ( ) : null ;
1328+
1329+ if ( string . IsNullOrEmpty ( user ) ) user = "none" ;
1330+
1331+ // Validate email
1332+ var addr = System . Net . Mail . MailAddress . TryCreate ( email , out var address ) ;
1333+ email = addr ? address ! . Address : "[email protected] " ; 1334+
1335+ if ( string . IsNullOrEmpty ( feedback ) ) return ;
1336+
1337+ var feedbackContent = $ "{ feedback } \n \n Rating: { feedbackResult . Rating } /5";
1338+
1339+ SentryHelper . SendExceptionFeedback ( ErrorSender . SentryErrorId , email , user , feedbackContent ) ;
1340+ isFeedbackSent = true ;
1341+ }
1342+ catch ( Exception ex )
1343+ {
1344+ await SentryHelper . ExceptionHandlerAsync ( ex , SentryHelper . ExceptionType . UnhandledOther ) ;
1345+ }
1346+ finally
1347+ {
1348+ await Dialog_ShowUnhandledExceptionMenu ( isFeedbackSent ) ;
1349+ }
1350+ }
1351+
12501352 private static async void CopyTextToClipboard ( object sender , RoutedEventArgs e )
12511353 {
12521354 try
@@ -1495,7 +1597,8 @@ public static Task<ContentDialogResult> SpawnDialog(string? title,
14951597 ContentDialogButton defaultButton =
14961598 ContentDialogButton . Primary ,
14971599 ContentDialogTheme dialogTheme =
1498- ContentDialogTheme . Informational )
1600+ ContentDialogTheme . Informational ,
1601+ RoutedEventHandler ? onLoaded = null )
14991602 {
15001603 _sharedDispatcherQueue ??=
15011604 parentUI ? . DispatcherQueue ??
@@ -1524,8 +1627,19 @@ WindowUtility.CurrentWindow is MainWindow
15241627 : parentUI ? . XamlRoot
15251628 } ;
15261629
1527- // Queue and spawn the dialog instance
1528- return await dialog . QueueAndSpawnDialog ( ) ;
1630+ try
1631+ {
1632+ if ( onLoaded is not null )
1633+ dialog . Loaded += onLoaded ;
1634+
1635+ // Queue and spawn the dialog instance
1636+ return await dialog . QueueAndSpawnDialog ( ) ;
1637+ }
1638+ finally
1639+ {
1640+ if ( onLoaded is not null )
1641+ dialog . Loaded -= onLoaded ;
1642+ }
15291643 } ) ?? Task . FromResult ( ContentDialogResult . None ) ;
15301644 }
15311645
@@ -1548,18 +1662,41 @@ public static async Task<ContentDialogResult> QueueAndSpawnDialog(this ContentDi
15481662 dialog . RequestedTheme = InnerLauncherConfig . IsAppThemeLight ? ElementTheme . Light : ElementTheme . Dark ;
15491663 }
15501664
1551- dialog . XamlRoot ??= SharedXamlRoot ;
1665+ try
1666+ {
1667+ dialog . XamlRoot ??= SharedXamlRoot ;
1668+ dialog . Loaded += RecursivelySetDialogCursor ;
15521669
1553- // Assign the dialog to the global task
1554- _currentSpawnedDialogTask = dialog switch
1555- {
1556- ContentDialogCollapse dialogCollapse => dialogCollapse . ShowAsync ( ) ,
1557- ContentDialogOverlay overlapCollapse => overlapCollapse . ShowAsync ( ) ,
1558- _ => dialog . ShowAsync ( )
1559- } ;
1560- // Spawn and await for the result
1561- ContentDialogResult dialogResult = await _currentSpawnedDialogTask ;
1562- return dialogResult ; // Return the result
1670+ // Assign the dialog to the global task
1671+ _currentSpawnedDialogTask = dialog switch
1672+ {
1673+ ContentDialogCollapse dialogCollapse => dialogCollapse . ShowAsync ( ) ,
1674+ ContentDialogOverlay overlapCollapse => overlapCollapse . ShowAsync ( ) ,
1675+ _ => dialog . ShowAsync ( )
1676+ } ;
1677+ // Spawn and await for the result
1678+ ContentDialogResult dialogResult = await _currentSpawnedDialogTask ;
1679+ return dialogResult ; // Return the result
1680+ }
1681+ finally
1682+ {
1683+ dialog . Loaded -= RecursivelySetDialogCursor ;
1684+ }
1685+ }
1686+
1687+ private static void RecursivelySetDialogCursor ( object sender , RoutedEventArgs args )
1688+ {
1689+ if ( sender is not ContentDialog contentDialog )
1690+ {
1691+ return ;
1692+ }
1693+
1694+ InputSystemCursor cursor = InputSystemCursor . Create ( InputSystemCursorShape . Hand ) ;
1695+ contentDialog . SetAllControlsCursorRecursive ( cursor ) ;
1696+
1697+ Grid ? parent = ( contentDialog . Content as UIElement ) ? . FindAscendant ( "LayoutRoot" , StringComparison . OrdinalIgnoreCase ) as Grid ;
1698+ Grid ? commandButtonGrid = parent ? . FindDescendant ( "CommandSpace" , StringComparison . OrdinalIgnoreCase ) as Grid ;
1699+ commandButtonGrid ? . SetAllControlsCursorRecursive ( cursor ) ;
15631700 }
15641701 }
15651702}
0 commit comments