diff --git a/Packages/com.singularitygroup.hotreload/.gitignore b/Packages/com.singularitygroup.hotreload/.gitignore new file mode 100644 index 0000000..84edfd6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/.gitignore @@ -0,0 +1,7 @@ +# do not include HotReloadSettingsObject inside the upm package. +# (needs to be editable by the user) +# (only created on first Android build) +/Resources/HotReloadSettingsObject.asset +/Resources/HotReloadSettingsObject.asset.meta +/Resources.meta + diff --git a/Packages/com.singularitygroup.hotreload/Documentation.meta b/Packages/com.singularitygroup.hotreload/Documentation.meta new file mode 100644 index 0000000..3268408 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Documentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a025eec5cd1851429c24e953a58d48b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf new file mode 100644 index 0000000..2796872 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf differ diff --git a/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta new file mode 100644 index 0000000..577af0c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8999c2c2d9cadcb44a617a5df023bfa1 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta index a0d7e00..8a36764 100644 --- a/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta @@ -1,10 +1,3 @@ fileFormatVersion: 2 guid: 67658aafb8404f0eb9496812ba4bb8a4 -timeCreated: 1678721795 -AssetOrigin: - serializedVersion: 1 - productId: 254358 - packageName: Hot Reload | Edit Code Without Compiling - packageVersion: 1.13.17 - assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs - uploadId: 870414 +timeCreated: 1678721795 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta index d5b0f71..d9256b0 100644 --- a/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta @@ -1,10 +1,3 @@ fileFormatVersion: 2 guid: d7493a30e78d4ec783ead20baea2c4d2 -timeCreated: 1678721534 -AssetOrigin: - serializedVersion: 1 - productId: 254358 - packageName: Hot Reload | Edit Code Without Compiling - packageVersion: 1.13.17 - assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs - uploadId: 870414 +timeCreated: 1678721534 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/BugReport.meta b/Packages/com.singularitygroup.hotreload/Editor/BugReport.meta new file mode 100644 index 0000000..97408ac --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/BugReport.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea93d0fb53fe44419e8a20d4701918ef +timeCreated: 1773924481 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs b/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs new file mode 100644 index 0000000..f4d4f03 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + internal static class ReportWindowAPI { + public static void OpenBugReport(string title = null, string description = null) { + HotReloadBugReportWindow.Open( + ReportMode.BugReport, + discordUrl: Constants.DiscordInviteUrl, + contactUrl: Constants.ContactURL, + email: HotReloadPrefs.LicenseEmail, + details: description, + title: title + ); + } + + public static void OpenFeedback( + Func> submitHandler = null, + string discordUrl = null, + string contactUrl = null, + string title = null, + string email = null, + string feedback = null + ) { + HotReloadBugReportWindow.Open( + ReportMode.Feedback, + discordUrl: Constants.DiscordInviteUrl, + contactUrl: Constants.ContactURL, + email: HotReloadPrefs.LicenseEmail + ); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs.meta new file mode 100644 index 0000000..d2f7770 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/BugReport/BugReportWindowAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a7505b2e85b481479de6861e72e954a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/BugReport/HotReloadBugReportWindow.cs b/Packages/com.singularitygroup.hotreload/Editor/BugReport/HotReloadBugReportWindow.cs new file mode 100644 index 0000000..d4125a6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/BugReport/HotReloadBugReportWindow.cs @@ -0,0 +1,616 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using UnityEditor.UIElements; +using UnityEngine.UIElements; +using SingularityGroup.HotReload.Editor.Localization; +using Application = UnityEngine.Application; +#if UNITY_2021_3_OR_NEWER +using System.IO; +using System.IO.Compression; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.EditorDependencies; +using CompressionLevel = System.IO.Compression.CompressionLevel; +#endif + +namespace SingularityGroup.HotReload.Editor { + + internal enum ReportMode { + BugReport, + Feedback + } + + internal class HotReloadBugReportWindow : EditorWindow { + static List SeverityChoices => new List { + Translations.BugReport.SeverityNormal, + Translations.BugReport.SeverityLow, + Translations.BugReport.SeverityHigh, + }; + + static List FrequencyChoices => new List { + Translations.BugReport.FrequencyFirstTime, + Translations.BugReport.FrequencySometimes, + Translations.BugReport.FrequencyAlways, + }; + + [SerializeField] ReportMode _mode = ReportMode.BugReport; + + [SerializeField] VisualTreeAsset visualTree; + [SerializeField] StyleSheet styleSheet; + + string _discordUrl; + string _contactUrl; + + TextField _titleField; + PopupField _severityPopup; + PopupField _frequencyPopup; + TextField _emailField; + IMGUIContainer _detailsContainer; + [SerializeField] string _detailsText = ""; + [SerializeField] string _emailText = ""; + [SerializeField] string _titleText = ""; + Label _detailsLabel; + Label _validationLabel; + Button _submitButton; + VisualElement _severitySection; + VisualElement _frequencySection; + ScrollView _scrollView; + VisualElement _detailsSlot; + + VisualElement _successScreen; + VisualElement _failureScreen; + Label _failureErrorLabel; + Button _tabBugReport; + Button _tabFeedback; + + bool _isOnResultScreen; + + static Vector2 GetMinSize(ReportMode mode) { + return mode == ReportMode.Feedback + ? new Vector2(420, 500) + : new Vector2(420, 620); + } + + #region Public API + /// + /// Opens the window with the given mode and configuration. + /// Prefer using for a cleaner call site. + /// + public static HotReloadBugReportWindow Open( + ReportMode mode, + string discordUrl = null, + string contactUrl = null, + string title = null, + string email = null, + string details = null + ) { + var wnd = GetWindow(utility: true); + wnd._mode = mode; + wnd._discordUrl = discordUrl; + wnd._contactUrl = contactUrl; + wnd.titleContent = new GUIContent( + mode == ReportMode.Feedback + ? Translations.BugReport.WindowTitleFeedback + : Translations.BugReport.WindowTitleBugReport); + wnd.minSize = wnd.maxSize = GetMinSize(mode); + + wnd._titleText = title; + wnd._emailText = email; + wnd._detailsText = details; + + wnd.RebuildUI(); + return wnd; + } + + /// + /// Prefills form fields on an already-open window. Null values are ignored. + /// + public void Prefill() { + if (_titleText != null && _titleField != null) { + _titleField.SetValueWithoutNotify(_titleText); + } + if (_emailText != null && _emailField != null) { + _emailField.SetValueWithoutNotify(_emailText); + } + } + #endregion + + void OnEnable() { + RebuildUI(); + } + + bool HasUnsavedFormContent() { + if (_isOnResultScreen) { + return false; + } + if (!string.IsNullOrWhiteSpace(_titleField.value)) { + return true; + } + if (!string.IsNullOrWhiteSpace(_detailsText) && _detailsText != Translations.BugReport.PlaceholderDetails) { + return true; + } + + return false; + } + + void OnDestroy() { + if (_detailsContainer != null && _detailsSlot != null) { + _detailsSlot.Remove(_detailsContainer); + _detailsContainer = null; + } + if (!HasUnsavedFormContent()) { + return; + } + + bool discard = EditorUtility.DisplayDialog( + Translations.BugReport.DiscardDialogTitle, + Translations.BugReport.DiscardDialogMessage, + Translations.BugReport.DiscardDialogConfirm, + Translations.BugReport.DiscardDialogCancel); + + if (!discard) { + _emailText = _emailField.value; + _titleText = _titleField.value; + EditorApplication.delayCall += () => { + Open(_mode, _discordUrl, _contactUrl); + }; + } else { + _detailsText = null; + _emailText = null; + _titleText = null; + } + } + + void RebuildUI() { + DetachCallbacks(); + if (visualTree == null) { + Log.Warning($"Could not open bug report. Please reach out to our support: {_contactUrl}"); + return; + } + + rootVisualElement.Clear(); + visualTree.CloneTree(rootVisualElement); + if (styleSheet != null) { + rootVisualElement.styleSheets.Add(styleSheet); + } + + QueryElements(); + AttachIMGUIContainers(); + ApplyTranslations(); + CreatePopupFields(); + ApplyMode(); + SetupSubmit(); + SetupResultScreenButtons(); + FixSingleLineFieldAlignment(_titleField); + FixSingleLineFieldAlignment(_emailField); + Prefill(); + AttachCallbacks(); + + ShowFormView(); + } + + void AttachIMGUIContainers() { + _detailsSlot = rootVisualElement.Q("details-field-slot"); + // Build the IMGUI textarea and inject it + _detailsContainer = new IMGUIContainer(DrawDetailsField); + _detailsSlot.Add(_detailsContainer); + } + + void QueryElements() { + _titleField = rootVisualElement.Q("title-field"); + _emailField = rootVisualElement.Q("email-field"); + _detailsLabel = rootVisualElement.Q