Compare commits

..

23 Commits

Author SHA1 Message Date
Achies f2daaeb64e Update README.md 2026-06-20 18:00:23 +03:00
github-actions fd3e131d33 chore(release): 1.8.5 2026-06-20 11:46:25 +00:00
Achies f7cc7dcf99 Merge pull request #26 from achiez/dev
Release 1.8.5
2026-06-20 14:45:51 +03:00
achiez 16f0cc2b2d chore: update AssemblyVersion to 1.8.5 and add changelog for new features and fixes 2026-06-20 00:49:26 +03:00
achiez cd0416705f fix: add missing SignAt scheme in Proxy Assignment 2026-06-19 23:36:04 +03:00
Achies 2d06cb81f9 Merge pull request #30 from achiez/feat/website-links
feat(links): fetch website and docs URLs from GitHub at startup
2026-06-19 22:36:21 +03:00
achiez ed5d3c2eda feat(links): fetch website and docs URLs from GitHub at startup
- Add links.json to repo (alongside update.xml) with Website and Documentation URLs.
- Add LinksManager to support link fetching on startup via raw.githubusercontent.com
- Suppress TimeAligner failure instead of blocking app launch
2026-06-19 22:34:37 +03:00
Achies 07eb9f15a9 Merge pull request #29 from achiez/feat/login-again-on-session-expired
feat(session): show LoginAgainDialog on permanent session expiry
2026-06-19 22:06:44 +03:00
achiez c39660fc55 feat(session): show LoginAgainDialog on permanent session expiry
- Replace snackbar with interactive LoginAgainDialog when session cannot be   recovered automatically on user-initiated actions
- Show proxy warning hint in dialog when no proxy assigned to account or set as default
- Pass allowInteractiveLogin: false from MAACRequestHandler to skip dialog in  automated MAAC confirmations
2026-06-19 22:05:49 +03:00
Achies 038f62b4db Merge pull request #28 from achiez/feat/export-zip-and-improvements
feat(export): add zip export, export-all, and login:password input parsing
2026-06-19 21:21:51 +03:00
achiez 5029f78247 feat(export): add zip export, export-all, and login:password input parsing 2026-06-19 21:18:34 +03:00
Achies b9452c4baf Merge pull request #27 from achiez/feat/dialog-header
feat(ui): add DialogHeader component and restyle dialog headers
2026-06-19 20:45:30 +03:00
achiez ec71b9881e feat(ui): add DialogHeader component and restyle dialog headers 2026-06-19 20:43:43 +03:00
achiez 4b727af1fa chore(resharper): perform resharper cleanup and add shared cleanup profile 2026-06-19 19:26:26 +03:00
achiez 9897ca21a6 fix: add missing MafileImportDialogVM from #24 2026-06-19 19:05:58 +03:00
Achies 631068e2f3 Merge pull request #25 from achiez/feat/proxy-signat-scheme
feat(proxy): add SignAtScheme support for improved proxy parsing
2026-06-19 19:03:56 +03:00
Achies 0cc9a3d100 Merge pull request #24 from achiez/feat/group-on-import
feat(import): add group assignment and skip-confirmation setting
2026-06-19 19:03:33 +03:00
Achies 7348bc2673 Merge branch 'dev' into feat/group-on-import 2026-06-19 19:03:16 +03:00
Achies 3a9d929c9a Merge pull request #23 from achiez/fix/mafile-mover-loc
fix(localization): update mafile mover Steam Guard error message
2026-06-19 19:01:40 +03:00
Achies 25818997af Merge pull request #22 from achiez/feat/proxy-assignment
feat(proxy): add bulk assignment and proxy usage stats
2026-06-19 19:00:47 +03:00
achiez 5836ed4508 feat(proxy): add SignAtScheme support for improved proxy parsing 2026-06-19 18:55:39 +03:00
achiez 537d5d907f feat(import): add group assignment and skip-confirmation setting; remove obsolete format setting
- Add ability to assign imported mafiles to a group directly from the import dialog
- Add setting to skip the import confirmation dialog when no conflicts are detected
- Remove the deprecated "legacy mafile format" setting as it is no longer relevant
2026-06-19 18:54:37 +03:00
achiez 0e90f1826b fix(localization): update mafile mover Steam Guard error message 2026-05-19 22:42:58 +03:00
58 changed files with 1228 additions and 521 deletions
+311
View File
@@ -1,3 +1,314 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;&#xD;
&lt;TypePattern DisplayName="Non-reorderable types" Priority="99999999"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.ComImport" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.StructLayoutAttribute" /&gt;&#xD;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;HasMember&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="Xunit.FactAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="Xunit.TheoryAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/HasMember&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Constructors"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;ImplementsInterface Name="System.IDisposable" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.FactAttribute" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.TheoryAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.TestFixtureAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.TestFixtureSourceAttribute" /&gt;&#xD;
&lt;HasMember&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/HasMember&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.SetUpAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.TearDownAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.TestFixtureSetUpAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.TestFixtureTearDownAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.OneTimeSetUpAttribute" /&gt;&#xD;
&lt;HasAttribute Inherited="True" Name="NUnit.Framework.OneTimeTearDownAttribute" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Grains"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;ImplementsInterface Name="^Orleans\.IGrain$" /&gt;&#xD;
&lt;ImplementsInterface Name="Orleans.IGrainBase" /&gt;&#xD;
&lt;ImplementsInterface Name="Achiefy.Orleans.Tenancy.Abstractions.ITenantGrain" /&gt;&#xD;
&lt;InheritsFromType Name="Orleans.Grain" /&gt;&#xD;
&lt;InheritsFromType Name="Achiefy.Tenant.Application.Stateful.Core.TenantGrain" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;Abstract /&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Public Delegates" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Delegate" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public Enums" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Nested Records"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Record" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Group DisplayName="Group"&gt;&#xD;
&lt;Entry DisplayName="Static Fields and Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constant" /&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Kind Is="0" Order="Constant Field" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Properties, Indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/Group&gt;&#xD;
&lt;Entry DisplayName="Constructors"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Lifecycle Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Name Is="^On(Activate|Deactivate)Async.*$" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Nested Types"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Type" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Default Pattern"&gt;&#xD;
&lt;Entry DisplayName="Public Enums" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public Delegates" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Delegate" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Nested Records"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Record" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Group DisplayName="Group"&gt;&#xD;
&lt;Entry DisplayName="Static Fields and Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constant" /&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Kind Is="0" Order="Constant Field" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Properties, Indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/Group&gt;&#xD;
&lt;Entry DisplayName="Constructors"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Nested Types"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Type" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MAAC/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=MAAC/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+4
View File
@@ -0,0 +1,4 @@
{
"Website": "https://achiefy.pro/?nebula=true",
"Documentation": "https://achiefy-project.gitbook.io/nebulaauth"
}
+4 -4
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<item> <item>
<version>1.8.4</version> <version>1.8.5</version>
<url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.8.4/NebulaAuth.1.8.4.zip</url> <url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.8.5/NebulaAuth.1.8.5.zip</url>
<changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.8.4.html</changelog> <changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.8.5.html</changelog>
<mandatory>false</mandatory> <mandatory>false</mandatory>
<checksum algorithm="SHA256">5ecad7a711bab7e98f5f3bfa92b7a00e21efc18aa92251bb30bd3e50a2f7d2f2</checksum> <checksum algorithm="SHA256">7b2d7768ff5497664c0223a535e35006022d7aed62ebf5400fbf9be3d2893bd5</checksum>
</item> </item>
+1 -1
View File
@@ -21,7 +21,7 @@ NebulaAuth is an application for emulating actions from the Steam Mobile App, re
<img src="misc/main_window.png" width="600"/> <img src="misc/main_window.png" width="600"/>
</p> </p>
- **Localization in six languages**: English, Russian, Ukrainian, Spanish, Turkish and Kazakh. - **Localization in eight languages**: English, Russian, Ukrainian, Simplified Chinese, French, Spanish, Turkish, and Kazakh.
- **Full functionality of Steam Desktop Authenticator** reimagining [old app](https://github.com/Jessecar96/SteamDesktopAuthenticator) - **Full functionality of Steam Desktop Authenticator** reimagining [old app](https://github.com/Jessecar96/SteamDesktopAuthenticator)
- **Proxy support** in all account work processes. - **Proxy support** in all account work processes.
- **Mafile grouping** for improved management. - **Mafile grouping** for improved management.
+18 -18
View File
@@ -63,25 +63,25 @@
</head> </head>
<body> <body>
<div class="changelog-container"> <div class="changelog-container">
<div class="change"> <div class="change">
<div class="version">Version 1.8.1</div> <div class="version">Version 1.8.1</div>
<div class="date">25.01.2026</div> <div class="date">25.01.2026</div>
<div class="description"> <div class="description">
<ul> <ul>
<li><b>NEW:</b> Added Mafile Export tool with templates, batch export and flexible field filtering.</li> <li><b>NEW:</b> Added Mafile Export tool with templates, batch export and flexible field filtering.</li>
<li><b>IMPROVEMENT:</b> Reworked auto-confirmation error handling logic (MAAC) — fewer false disables, smarter retries.</li> <li><b>IMPROVEMENT:</b> Reworked auto-confirmation error handling logic (MAAC) — fewer false disables, smarter retries.</li>
<li><b>UPDATE:</b> Added “Unattach Proxy” option to the account context menu.</li> <li><b>UPDATE:</b> Added “Unattach Proxy” option to the account context menu.</li>
<li><b>LOCALIZATION:</b> Added Chinese (Simplified) and French languages.</li> <li><b>LOCALIZATION:</b> Added Chinese (Simplified) and French languages.</li>
<li><b>FIX:</b> Fixed crashes related to duplicate mafiles, timers, search filtering and import logic.</li> <li><b>FIX:</b> Fixed crashes related to duplicate mafiles, timers, search filtering and import logic.</li>
<li> <li>
<b>DETAILS:</b> Full patch notes: <b>DETAILS:</b> Full patch notes:
<a href="https://teletype.in/@achies_raw/nebula-1-8-1-eng">ENG</a> | <a href="https://teletype.in/@achies_raw/nebula-1-8-1-eng">ENG</a> |
<a href="https://teletype.in/@achies_raw/nebula-1-8-1-rus">RUS</a> <a href="https://teletype.in/@achies_raw/nebula-1-8-1-rus">RUS</a>
</li> </li>
</ul> </ul>
</div>
</div> </div>
</div> </div>
</div>
</body> </body>
</html> </html>
+9 -9
View File
@@ -63,16 +63,16 @@
</head> </head>
<body> <body>
<div class="changelog-container"> <div class="changelog-container">
<div class="change"> <div class="change">
<div class="version">Version 1.8.2</div> <div class="version">Version 1.8.2</div>
<div class="date">10.02.2026</div> <div class="date">10.02.2026</div>
<div class="description"> <div class="description">
<ul> <ul>
<li><b>FIX:</b> AutoConfirm timer now correctly resets the status after a successful request (Warning → Ok).</li> <li><b>FIX:</b> AutoConfirm timer now correctly resets the status after a successful request (Warning → Ok).</li>
</ul> </ul>
</div>
</div> </div>
</div> </div>
</div>
</body> </body>
</html> </html>
+29
View File
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Changelog</title>
<style>
body { font-family: Segoe UI, sans-serif; background:#eeeeee; padding:20px; }
.change { background:white; padding:25px; border-radius:10px; }
li { margin-bottom:6px; }
</style>
</head>
<body>
<div class="change">
<ul>
<li><b>NEW:</b> Bulk proxy assignment: assign proxies to multiple accounts at once via flexible login:proxy, login:ID or login-only input <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22">details</a></li>
<li><b>NEW:</b> Import dialog now allows assigning imported mafiles to a group directly; added option to skip confirmation when no conflicts are detected <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/24">details</a></li>
<li><b>NEW:</b> Assign random free proxy to the account from context menu <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22">details</a></li>
<li><b>NEW:</b> Export: added ZIP archive output, export-all mode when the input field is empty, and support for login:password input format <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/28">details</a></li>
<li><b>IMPROVEMENT:</b> When a session permanently expires during a user-initiated action, a login dialog is now shown instead of a generic error notification <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/29">details</a></li>
<li><b>IMPROVEMENT:</b> Proxy manager: account count badges now show how many accounts are assigned to each proxy entry <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22">details</a></li>
<li><b>IMPROVEMENT:</b> Added login:pass@ip:port proxy format support <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/25">details</a></li>
<li><b>IMPROVEMENT:</b> Improved dialog header design <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/27">details</a></li>
<li><b>IMPROVEMENT:</b> Website and Documentation links are now available from the links window <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/30">details</a></li>
<li><b>FIX:</b> Application no longer fails to start when Steam time synchronization is unavailable (network issues, firewall)</li>
<li><b>FIX:</b> Fixed incorrect Steam Guard error message shown in the Mafile Mover dialog <a href="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/23">details</a></li>
</ul>
</div>
</body>
</html>
+60
View File
@@ -0,0 +1,60 @@
{
"version": "1.8.5",
"date": "2026-06-19",
"changes": [
{
"type": "NEW",
"text": "Bulk proxy assignment: assign proxies to multiple accounts at once via flexible login:proxy, login:ID or login-only input",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22"
},
{
"type": "NEW",
"text": "Import dialog now allows assigning imported mafiles to a group directly; added option to skip confirmation when no conflicts are detected",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/24"
},
{
"type": "NEW",
"text": "Assign random free proxy to the account from context menu",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22"
},
{
"type": "NEW",
"text": "Export: added ZIP archive output, export-all mode when the input field is empty, and support for login:password input format",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/28"
},
{
"type": "IMPROVEMENT",
"text": "When a session permanently expires during a user-initiated action, a login dialog is now shown instead of a generic error notification",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/29"
},
{
"type": "IMPROVEMENT",
"text": "Proxy manager: account count badges now show how many accounts are assigned to each proxy entry",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/22"
},
{
"type": "IMPROVEMENT",
"text": "Added login:pass@ip:port proxy format support",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/25"
},
{
"type": "IMPROVEMENT",
"text": "Improved dialog header design",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/27"
},
{
"type": "IMPROVEMENT",
"text": "Website and Documentation links are now available from the links window",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/30"
},
{
"type": "FIX",
"text": "Application no longer fails to start when Steam time synchronization is unavailable (network issues, firewall)"
},
{
"type": "FIX",
"text": "Fixed incorrect Steam Guard error message shown in the Mafile Mover dialog",
"link": "https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/pull/23"
}
]
}
@@ -0,0 +1,49 @@
<UserControl x:Class="NebulaAuth.DialogHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
x:Name="Root">
<StackPanel>
<Border Background="{DynamicResource Base300Brush}" Padding="10,6,10,6">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon
Kind="{Binding IconKind, ElementName=Root}"
Width="20" Height="20" Margin="0,0,8,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}">
<materialDesign:PackIcon.Style>
<Style TargetType="materialDesign:PackIcon">
<Style.Triggers>
<Trigger Property="Kind" Value="None">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</materialDesign:PackIcon.Style>
</materialDesign:PackIcon>
<TextBlock VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}"
FontSize="18">
<Run Text="{Binding Title, ElementName=Root}" />
<Run FontWeight="Bold" Text="{Binding TitleAccent, ElementName=Root}" />
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right"
IsEnabled="{Binding IsCloseEnabled, ElementName=Root}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{Binding CloseParameter, ElementName=Root}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
</Border>
</StackPanel>
</UserControl>
@@ -0,0 +1,63 @@
using System.Windows;
using System.Windows.Controls;
using MaterialDesignThemes.Wpf;
namespace NebulaAuth;
public partial class DialogHeader : UserControl
{
public static readonly DependencyProperty IconKindProperty =
DependencyProperty.Register(nameof(IconKind), typeof(PackIconKind), typeof(DialogHeader),
new PropertyMetadata(PackIconKind.None));
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(nameof(Title), typeof(string), typeof(DialogHeader),
new PropertyMetadata(null));
public static readonly DependencyProperty TitleAccentProperty =
DependencyProperty.Register(nameof(TitleAccent), typeof(string), typeof(DialogHeader),
new PropertyMetadata(null));
public static readonly DependencyProperty CloseParameterProperty =
DependencyProperty.Register(nameof(CloseParameter), typeof(object), typeof(DialogHeader),
new PropertyMetadata(null));
public static readonly DependencyProperty IsCloseEnabledProperty =
DependencyProperty.Register(nameof(IsCloseEnabled), typeof(bool), typeof(DialogHeader),
new PropertyMetadata(true));
public PackIconKind IconKind
{
get => (PackIconKind) GetValue(IconKindProperty);
set => SetValue(IconKindProperty, value);
}
public string? Title
{
get => (string?) GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public string? TitleAccent
{
get => (string?) GetValue(TitleAccentProperty);
set => SetValue(TitleAccentProperty, value);
}
public object? CloseParameter
{
get => GetValue(CloseParameterProperty);
set => SetValue(CloseParameterProperty, value);
}
public bool IsCloseEnabled
{
get => (bool) GetValue(IsCloseEnabledProperty);
set => SetValue(IsCloseEnabledProperty, value);
}
public DialogHeader()
{
InitializeComponent();
}
}
@@ -77,6 +77,10 @@ public partial class HintBox : UserControl
IconKind = PackIconKind.ErrorOutline; IconKind = PackIconKind.ErrorOutline;
IconBrush = (Brush) Application.Current.FindResource("ErrorBrush")!; IconBrush = (Brush) Application.Current.FindResource("ErrorBrush")!;
break; break;
case HintBoxSeverity.Warning:
IconKind = PackIconKind.WarningOutline;
IconBrush = (Brush) Application.Current.FindResource("WarningBrush")!;
break;
default: default:
IconKind = PackIconKind.InfoCircleOutline; IconKind = PackIconKind.InfoCircleOutline;
IconBrush = (Brush) Application.Current.FindResource("InfoBrush")!; IconBrush = (Brush) Application.Current.FindResource("InfoBrush")!;
@@ -94,5 +98,6 @@ public partial class HintBox : UserControl
public enum HintBoxSeverity public enum HintBoxSeverity
{ {
Info, Info,
Warning,
Error Error
} }
+20 -2
View File
@@ -15,13 +15,15 @@ namespace NebulaAuth.Core;
public static class DialogsController public static class DialogsController
{ {
public static async Task<LoginAgainVM?> ShowLoginAgainDialog(string username, string? currentPassword = null) public static async Task<LoginAgainVM?> ShowLoginAgainDialog(string username, string? currentPassword = null,
bool showNoProxyWarning = false)
{ {
var vm = new LoginAgainVM var vm = new LoginAgainVM
{ {
UserName = username, UserName = username,
Password = currentPassword ?? string.Empty, Password = currentPassword ?? string.Empty,
SavePassword = PHandler.IsPasswordSet SavePassword = PHandler.IsPasswordSet,
ShowNoProxyWarning = showNoProxyWarning
}; };
var content = new LoginAgainDialog var content = new LoginAgainDialog
{ {
@@ -73,6 +75,22 @@ public static class DialogsController
return null; return null;
} }
public static async Task<MafileImportDialogResult?> ShowMafileImportDialog(IEnumerable<string> groups,
int totalCount, int conflictCount)
{
var vm = new MafileImportDialogVM(groups, totalCount, conflictCount);
var content = new MafileImportDialog
{
DataContext = vm
};
var result = await DialogHost.Show(content);
return result is MafileImportDialogVM
? new MafileImportDialogResult(
!vm.AddToGroup || string.IsNullOrWhiteSpace(vm.Group) ? null : vm.Group.Trim(),
vm.OverwriteConflicts)
: null;
}
public static async Task<bool> ShowProxyManager() public static async Task<bool> ShowProxyManager()
{ {
var vm = new ProxyManagerVM(); var vm = new ProxyManagerVM();
+33
View File
@@ -0,0 +1,33 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using NebulaAuth.Model;
using Newtonsoft.Json;
namespace NebulaAuth.Core;
public static class LinksManager
{
private const string LINKS_URL =
"https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/links.json";
private static readonly HttpClient HttpClient = new();
public static string? WebsiteUrl { get; private set; }
public static string? DocumentationUrl { get; private set; }
public static async Task FetchAsync()
{
try
{
var json = await HttpClient.GetStringAsync(LINKS_URL).ConfigureAwait(false);
var links = JsonConvert.DeserializeObject<RemoteLinks>(json);
WebsiteUrl = links?.Website;
DocumentationUrl = links?.Documentation;
}
catch (Exception ex)
{
Shell.Logger.Debug(ex, "Failed to fetch remote links");
}
}
}
@@ -196,6 +196,16 @@
"es": "Grupo", "es": "Grupo",
"tr": "Grup", "tr": "Grup",
"kk": "Топ" "kk": "Топ"
},
"ExportToZip": {
"ru": "Экспортировать в ZIP архив",
"en": "Export to ZIP archive",
"uk": "Експортувати в ZIP архів",
"fr": "Exporter en archive ZIP",
"zh": "导出为 ZIP 压缩包",
"es": "Exportar a archivo ZIP",
"tr": "ZIP arşivine aktar",
"kk": "ZIP мұрағатқа экспорттау"
} }
}, },
"Tooltips": { "Tooltips": {
@@ -288,17 +298,27 @@
"es": "Incluir datos de grupo compatibles con Nebula.", "es": "Incluir datos de grupo compatibles con Nebula.",
"tr": "Nebula uyumlu grup verilerini dahil et.", "tr": "Nebula uyumlu grup verilerini dahil et.",
"kk": "Nebula үйлесімді топ деректерін қосу." "kk": "Nebula үйлесімді топ деректерін қосу."
},
"ExportToZip": {
"ru": "Экспортировать в ZIP архив вместо папки. Архив будет сохранён в выбранной директории с именем вида N_timestamp.zip.",
"en": "Export to a ZIP archive instead of a folder. The archive will be saved in the selected directory as N_timestamp.zip.",
"uk": "Експортувати в ZIP архів замість папки. Архів буде збережено у вибраній директорії з назвою N_timestamp.zip.",
"fr": "Exporter dans une archive ZIP plutôt que dans un dossier. L'archive sera enregistrée dans le répertoire sélectionné sous le nom N_timestamp.zip.",
"zh": "导出为 ZIP 压缩包而非文件夹。压缩包将保存在所选目录中,名称格式为 N_timestamp.zip。",
"es": "Exportar a un archivo ZIP en lugar de una carpeta. El archivo se guardará en el directorio seleccionado como N_timestamp.zip.",
"tr": "Klasör yerine ZIP arşivine aktar. Arşiv, seçilen dizine N_timestamp.zip adıyla kaydedilecektir.",
"kk": "Қалта орнына ZIP мұрағатына экспорттау. Мұрағат таңдалған каталогта N_timestamp.zip атауымен сақталады."
} }
}, },
"ExportAccountsPlaceholder": { "ExportAccountsPlaceholder": {
"ru": "Введите логины аккаунтов или SteamID (по одному на строку) для экспорта в выбранную директорию.", "ru": "Введите логины или SteamID (по одному на строку). Поддерживается формат логин:пароль — пароль будет проигнорирован. Оставьте пустым для экспорта всех аккаунтов.",
"en": "Enter account logins or SteamIDs (one per line) to export to the selected directory.", "en": "Enter logins or SteamIDs (one per line). Supports login:password format — the password will be ignored. Leave empty to export all accounts.",
"uk": "Введіть логіни акаунтів або SteamID (по одному на рядок) для експорту у вибрану директорію.", "uk": "Введіть логіни або SteamID (по одному на рядок). Підтримується формат логін:пароль — пароль буде проігноровано. Залиште порожнім для експорту всіх акаунтів.",
"fr": "Entrez les identifiants de compte ou les SteamID (un par ligne) pour exporter vers le répertoire sélectionné.", "fr": "Entrez les identifiants ou SteamID (un par ligne). Supporte le format login:mot_de_passe — le mot de passe sera ignoré. Laissez vide pour exporter tous les comptes.",
"zh": "输入要导出到所选目录的账户登录名或 SteamID(每行一个)。", "zh": "输入登录名或 SteamID(每行一个)。支持 login:password 格式——密码将被忽略。留空则导出所有账户。",
"es": "Introduce los logins de las cuentas o SteamID (uno por línea) para exportarlos al directorio seleccionado.", "es": "Introduce logins o SteamID (uno por línea). Soporta el formato login:contraseña — la contraseña será ignorada. Déjalo vacío para exportar todas las cuentas.",
"tr": "Seçilen dizine dışa aktarmak için hesap girişlerini veya SteamIDleri (her satıra bir tane) girin.", "tr": "Girişleri veya SteamIDleri girin (her satıra bir tane). login:şifre formatı desteklenir — şifre yok sayılır. Tüm hesapları dışa aktarmak için boş bırakın.",
"kk": "Таңдалған каталогқа экспорттау үшін аккаунт логиндерін немесе SteamID (әр жолға біреуден) енгізіңіз." "kk": "Логиндерді немесе SteamID (әр жолға біреуден) енгізіңіз. логин:құпиясөз форматы қолданылады — құпиясөз еленбейді. Барлық аккаунттарды экспорттау үшін бос қалдырыңыз."
} }
} }
} }
@@ -35,5 +35,67 @@
"tr": "Değer girin", "tr": "Değer girin",
"kk": "Мән енгізіңіз" "kk": "Мән енгізіңіз"
} }
},
"MafileImportDialog": {
"Title": {
"en": "Mafile import",
"ru": "Импорт мафайлов",
"zh": "导入 mafile",
"uk": "Імпорт mafile",
"fr": "Import de mafiles",
"es": "Importación de mafiles",
"tr": "Mafile içe aktarma",
"kk": "Mafile импорттау"
},
"TotalCount": {
"en": "Mafiles to import",
"ru": "Мафайлов к импорту",
"zh": "要导入的 mafile",
"uk": "Mafile до імпорту:",
"fr": "Mafiles à importer :",
"es": "Mafiles para importar:",
"tr": "İçe aktarılacak mafile:",
"kk": "Импортталатын mafile:"
},
"ConflictCount": {
"en": "Already exist",
"ru": "Уже существуют",
"zh": "已存在:",
"uk": "Уже існують:",
"fr": "Déjà existants :",
"es": "Ya existen:",
"tr": "Zaten mevcut:",
"kk": "Бұрыннан бар:"
},
"OverwriteConflicts": {
"en": "Overwrite conflicting mafiles",
"ru": "Перезаписать конфликтующие мафайлы",
"zh": "覆盖冲突的 mafile",
"uk": "Перезаписати конфліктні mafile",
"fr": "Remplacer les mafiles en conflit",
"es": "Sobrescribir mafiles en conflicto",
"tr": "Çakışan mafile'ların üzerine yaz",
"kk": "Қақтығысатын mafile файлдарын қайта жазу"
},
"AddToGroup": {
"en": "Add imported accounts to a group",
"ru": "Добавить импортированные аккаунты в группу",
"zh": "将导入的账号添加到组",
"uk": "Додати імпортовані акаунти до групи",
"fr": "Ajouter les comptes importés à un groupe",
"es": "Añadir las cuentas importadas a un grupo",
"tr": "İçe aktarılan hesapları bir gruba ekle",
"kk": "Импортталған аккаунттарды топқа қосу"
},
"GroupFieldHint": {
"en": "Existing or new group",
"ru": "Существующая или новая группа",
"zh": "现有或新组",
"uk": "Наявна або нова група",
"fr": "Groupe existant ou nouveau",
"es": "Grupo existente o nuevo",
"tr": "Mevcut veya yeni grup",
"kk": "Бар немесе жаңа топ"
}
} }
} }
@@ -79,6 +79,16 @@
"es": "Pulsa 'DEL' para eliminar", "es": "Pulsa 'DEL' para eliminar",
"tr": "Silmek için 'DEL' tuşuna bas", "tr": "Silmek için 'DEL' tuşuna bas",
"kk": "Жою үшін 'DEL' пернесін басыңыз" "kk": "Жою үшін 'DEL' пернесін басыңыз"
},
"NoProxyWarning": {
"en": "No proxy is assigned",
"ru": "Прокси не установлен",
"zh": "未分配代理",
"uk": "Проксі не призначено",
"fr": "Aucun proxy assigné",
"es": "No hay ningún proxy asignado",
"tr": "Proxy atanmamış",
"kk": "Прокси тағайындалмаған"
} }
} }
} }
@@ -42,14 +42,14 @@
"kk": "RCode: {0}\r\nФайл атауы: {1}.mafile" "kk": "RCode: {0}\r\nФайл атауы: {1}.mafile"
}, },
"GuardIsNotActive": { "GuardIsNotActive": {
"ru": "Steam Guard установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"", "ru": "Steam Guard НЕ установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"",
"en": "Steam Guard is set on this account. To link mafile to the account, use the 'Link' window", "en": "Steam Guard is NOT set on this account. To link mafile to the account, use the 'Link' window",
"zh": "此账户启用令牌。要将 mafile 绑定到此账户,请使用“绑定”窗口。", "zh": "此账户尚未启用令牌。要将 mafile 绑定到此账户,请使用“绑定”窗口。",
"uk": "Steam Guard встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'", "uk": "Steam Guard НЕ встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'",
"fr": "Steam Guard est configuré sur ce compte. Pour lier mafile au compte, utilisez la fenêtre 'Lien'", "fr": "Steam Guard n'est PAS configuré sur ce compte. Pour lier le mafile au compte, utilisez la fenêtre 'Lier'",
"es": "Steam Guard está activado en esta cuenta. Para vincular el mafile a la cuenta, usa la ventana 'Vincular'", "es": "Steam Guard NO está activado en esta cuenta. Para vincular el mafile a la cuenta, usa la ventana 'Vincular'",
"tr": "Steam Guard bu hesapta zaten etkin. Mafile'ı hesaba bağlamak için 'Bağla' penceresini kullanın", "tr": "Steam Guard bu hesapta etkin DEĞİL. Mafile'ı hesaba bağlamak için 'Bağla' penceresini kullanın",
"kk": "Бұл аккаунтта Steam Guard орнатылған. Mafile-ды аккаунтқа байланыстыру үшін 'Байланыстыру' терезесін пайдаланыңыз" "kk": "Бұл аккаунтта Steam Guard орнатылмаған. Mafile-ды аккаунтқа байланыстыру үшін 'Байланыстыру' терезесін пайдаланыңыз"
}, },
"SeemsNoPhoneNumber": { "SeemsNoPhoneNumber": {
"en": "The account has no phone number linked, returned Fail code (2)", "en": "The account has no phone number linked, returned Fail code (2)",
@@ -262,25 +262,25 @@
"kk": "Дискке сақталмайды" "kk": "Дискке сақталмайды"
} }
}, },
"LegacyMafileMode": { "ConfirmMafileImport": {
"en": "Common mafiles format", "en": "Ask confirmation when importing mafiles",
"ru": "Общепринятый формат мафайлов", "ru": "Спрашивать подтверждение при импорте мафайлов",
"zh": "常见的邮件文件格式", "zh": "导入 mafile 时请求确认",
"uk": "Загальноприйнятий формат мафайлів", "uk": "Запитувати підтвердження під час імпорту mafile",
"fr": "Format de mafiles commun", "fr": "Demander confirmation lors de l'import des mafiles",
"es": "Formato común de mafiles", "es": "Pedir confirmación al importar mafiles",
"tr": "Ortak mafile formatı", "tr": "Mafile içe aktarırken onay sor",
"kk": "Жалпы mafile форматы" "kk": "Mafile импорттау кезінде растауды сұрау"
}, },
"LegacyMafileModeHint": { "ConfirmMafileImportHint": {
"en": "Save mafiles in compatibility format with Steam Desktop Authenticator and other applications", "en": "Allows selecting a group when importing maFiles. Does not affect showing the dialog when file conflicts occur",
"ru": "Сохранять мафайлы в старом формате для совместимости со Steam Desktop Authenticator и другими приложениями", "ru": "Позволяет выбрать группу при импорте maFiles. Не влияет на показ окна при конфликте файлов",
"zh": "以与 Steam 桌面认证器及其他应用程序兼容的格式保存 mafiles", "zh": "允许在导入 maFiles 时选择分组。不影响发生文件冲突时窗口的显示",
"uk": "Зберігати мафайли у старому форматі для сумісності зі Steam Desktop Authenticator та іншими додатками", "uk": "Дозволяє вибрати групу під час імпорту maFiles. Не впливає на показ вікна при конфлікті файлів",
"fr": "Enregistrer les mafiles dans un format compatible avec Steam Desktop Authenticator et d'autres applications", "fr": "Permet de choisir un groupe lors de l'import des maFiles. N'affecte pas l'affichage de la fenêtre en cas de conflit de fichiers",
"es": "Guardar mafiles en formato compatible con Steam Desktop Authenticator y otras aplicaciones", "es": "Permite seleccionar un grupo al importar maFiles. No afecta a la aparición de la ventana cuando hay conflictos de archivos",
"tr": "Mafile'ları Steam Desktop Authenticator ve diğer uygulamalarla uyumlu formatta kaydet", "tr": "maFiles içe aktarılırken grup seçilmesine izin verir. Dosya çakışmalarında pencerenin gösterilmesini etkilemez",
"kk": "Mafile файлдарын Steam Desktop Authenticator және басқа қолданбалармен үйлесімді форматта сақтау" "kk": "maFiles импорттау кезінде топ таңдауға мүмкіндік береді. Файл қайшылықтары болғанда терезенің көрсетілуіне әсер етпейді"
}, },
"AllowAutoUpdate": { "AllowAutoUpdate": {
"en": "Allow auto update", "en": "Allow auto update",
@@ -101,16 +101,6 @@
"tr": "Onay hatası", "tr": "Onay hatası",
"kk": "Растау қатесі" "kk": "Растау қатесі"
}, },
"ConfirmMafileOverwrite": {
"ru": "Внимание, мафайл(-ы) уже присутствует в папке mafiles.\r\nПерезаписать мафайлы, которые конфликтуют?",
"en": "Attention, mafile(-s) already exists in mafiles folder.\r\nOverwrite conflicting mafiles?",
"zh": "注意,mafile 已经存在于 mafiles 文件夹中。\r\n覆盖冲突的 mafiles 吗?",
"uk": "Увага, мафайл(-и) вже присутній у папці mafiles.\r\nПерезаписати мафайли, які конфліктують?",
"fr": "Attention, mafiles déjà présents dans le dossier mafiles.\r\nRemplacer les mafiles en conflit?",
"es": "Atención, los mafiles ya existen en la carpeta mafiles.\r\n¿Sobrescribir los mafiles en conflicto?",
"tr": "Dikkat, mafile(ler) zaten mafiles klasöründe mevcut.\r\nÇakışan mafile'lar üzerine yazılsın mı?",
"kk": "Назар аударыңыз, mafile(дер) mafiles қалтасында бар.\r\nҚайшылық тудырған mafile файлдарының үстінен жазу керек пе?"
},
"MafileImportError": { "MafileImportError": {
"ru": "Ошибка при импорте мафайла", "ru": "Ошибка при импорте мафайла",
"en": "Mafile import error", "en": "Mafile import error",
+1
View File
@@ -42,6 +42,7 @@
DialogClosed="DialogHost_DialogClosed" DialogClosed="DialogHost_DialogClosed"
ApplyBlurBackground="{Binding Settings.ApplyBlurBackground}" ApplyBlurBackground="{Binding Settings.ApplyBlurBackground}"
BlurRadius="12" BlurRadius="12"
DialogContentUniformCornerRadius="12"
x:Name="DialogHostInstance"> x:Name="DialogHostInstance">
<Grid ZIndex="0"> <Grid ZIndex="0">
@@ -82,7 +82,7 @@ public static class MAACRequestHandler
{ {
return withSessionHandler return withSessionHandler
? Result<T>.Success(await SessionHandler.Handle(req, client.Mafile, client.Chp(), ? Result<T>.Success(await SessionHandler.Handle(req, client.Mafile, client.Chp(),
GetTimerPrefix(client.Mafile))) GetTimerPrefix(client.Mafile), false))
: Result<T>.Success(await req()); : Result<T>.Success(await req());
} }
catch (SessionInvalidException ex) catch (SessionInvalidException ex)
@@ -13,4 +13,5 @@ public class MafileExportTemplate
public bool IncludeNebulaPassword { get; set; } public bool IncludeNebulaPassword { get; set; }
public bool IncludeNebulaGroup { get; set; } public bool IncludeNebulaGroup { get; set; }
public string? Path { get; set; } public string? Path { get; set; }
public bool ExportToZip { get; set; }
} }
@@ -1,6 +1,9 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AchiesUtilities.Extensions; using AchiesUtilities.Extensions;
using NebulaAuth.Core; using NebulaAuth.Core;
@@ -36,48 +39,79 @@ public static class MafileExporter
return new ExportResult(LocManager.GetCodeBehindOrDefault("AccessDenied", "MafileExporter.AccessDenied")); return new ExportResult(LocManager.GetCodeBehindOrDefault("AccessDenied", "MafileExporter.AccessDenied"));
} }
var exported = new Dictionary<Mafile, string>(); var exported = new Dictionary<Mafile, string>();
var notFound = new List<string>(); var notFound = new List<string>();
var conflict = new List<string>(); var conflict = new List<string>();
var toExport = new List<(string key, Mafile mafile)>();
foreach (var key in keys) foreach (var key in keys)
{ {
SteamId? steamId = null; SteamId? steamId = null;
if (SteamId64.TryParse(key, out var id64)) if (SteamId64.TryParse(key, out var id64))
{
steamId = id64; steamId = id64;
}
var maf = Storage.MaFiles.FirstOrDefault(m => m.AccountName.EqualsIgnoreCase(key) || m.SteamId == steamId); var maf = Storage.MaFiles.FirstOrDefault(m => m.AccountName.EqualsIgnoreCase(key) || m.SteamId == steamId);
if (maf != null) if (maf != null)
toExport.Add((key, maf));
else
notFound.Add(key);
}
if (template.ExportToZip)
{
if (toExport.Count > 0)
{ {
var fileName = await ExportMafile(template.Path, template, maf); var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
if (fileName == null) var zipName = $"{toExport.Count}_{timestamp}.zip";
var zipPath = Path.Combine(template.Path, zipName);
await using var zipStream = File.Create(zipPath);
using var archive = new ZipArchive(zipStream, ZipArchiveMode.Create);
var addedEntries = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var (key, maf) in toExport)
{
var (content, fileName) = BuildMafileContent(template, maf);
if (!addedEntries.Add(fileName))
{
conflict.Add(key);
continue;
}
var entry = archive.CreateEntry(fileName);
await using var entryStream = entry.Open();
await using var writer = new StreamWriter(entryStream, Encoding.UTF8);
await writer.WriteAsync(content);
exported[maf] = fileName;
}
}
}
else
{
foreach (var (key, maf) in toExport)
{
var (content, fileName) = BuildMafileContent(template, maf);
var fullPath = Path.Combine(template.Path, fileName);
if (File.Exists(fullPath))
{ {
conflict.Add(key); conflict.Add(key);
continue; continue;
} }
exported[maf] = fileName; await File.WriteAllTextAsync(fullPath, content);
} exported[maf] = fullPath;
else
{
notFound.Add(key);
} }
} }
return new ExportResult(exported, notFound, conflict); return new ExportResult(exported, notFound, conflict);
} }
private static async Task<string?> ExportMafile(string path, MafileExportTemplate template, Mafile mafile) private static (string content, string fileName) BuildMafileContent(MafileExportTemplate template, Mafile mafile)
{ {
// We preserve SteamId even if other info is not included
var session = template.IncludeSessionData var session = template.IncludeSessionData
? mafile.SessionData ? mafile.SessionData
: new MobileSessionData(null!, mafile.SteamId, default, null, tokens: null); : new MobileSessionData(null!, mafile.SteamId, default, null, tokens: null);
var serializeMaf = new Mafile var serializeMaf = new Mafile
{ {
SharedSecret = template.IncludeSharedSecret ? mafile.SharedSecret : null!, SharedSecret = template.IncludeSharedSecret ? mafile.SharedSecret : null!,
@@ -102,14 +136,7 @@ public static class MafileExporter
var serialized = NebulaSerializer.SerializeMafile(serializeMaf); var serialized = NebulaSerializer.SerializeMafile(serializeMaf);
var strategy = template.UseLoginAsMafileName ? MafileNamingStrategy.Login : MafileNamingStrategy.SteamId; var strategy = template.UseLoginAsMafileName ? MafileNamingStrategy.Login : MafileNamingStrategy.SteamId;
var fileName = strategy.GetMafileName(serializeMaf); var fileName = strategy.GetMafileName(serializeMaf);
var fullPath = Path.Combine(path, fileName); return (serialized, fileName);
if (File.Exists(fullPath))
{
return null;
}
await File.WriteAllTextAsync(fullPath, serialized);
return fullPath;
} }
private static bool PathIsValid(string path) private static bool PathIsValid(string path)
+1 -6
View File
@@ -85,11 +85,6 @@ public static class NebulaSerializer
public static string SerializeMafile(MobileDataExtended data, Dictionary<string, object?>? properties) public static string SerializeMafile(MobileDataExtended data, Dictionary<string, object?>? properties)
{ {
if (Settings.Instance.LegacyMode) return MafileSerializer.SerializeLegacy(data, Serializer.Settings.SerializationOptions, properties);
{
return MafileSerializer.SerializeLegacy(data, Serializer.Settings.SerializationOptions, properties);
}
return Serializer.Serialize(data);
} }
} }
+7
View File
@@ -22,6 +22,13 @@ public static class ProxyStorage
PatternRequirement.Optional, PatternRequirement.Optional,
PatternRequirement.Optional); PatternRequirement.Optional);
public static readonly ProxyParser SignAtScheme = new(
ProxyDefaultFormats.UniversalSignAt, false, ProxyProtocol.HTTP,
ProxyPatternProtocol.All,
ProxyPatternHostFormat.Domain | ProxyPatternHostFormat.IPv4,
PatternRequirement.Required,
PatternRequirement.Required);
public static ObservableDictionary<int, ProxyData> Proxies { get; } = new(); public static ObservableDictionary<int, ProxyData> Proxies { get; } = new();
+7
View File
@@ -0,0 +1,7 @@
namespace NebulaAuth.Model;
public class RemoteLinks
{
public string? Website { get; set; }
public string? Documentation { get; set; }
}
@@ -6,7 +6,9 @@ using AchiesUtilities.Web.Models;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using NebulaAuth.Utility;
using NebulaAuth.View.Dialogs; using NebulaAuth.View.Dialogs;
using NebulaAuth.ViewModel.Other;
using SteamLib.Exceptions.Authorization; using SteamLib.Exceptions.Authorization;
namespace NebulaAuth.Model; namespace NebulaAuth.Model;
@@ -16,13 +18,13 @@ public static partial class SessionHandler
private static readonly SemaphoreSlim Semaphore = new(1, 1); private static readonly SemaphoreSlim Semaphore = new(1, 1);
public static async Task<T> Handle<T>(Func<Task<T>> func, Mafile mafile, public static async Task<T> Handle<T>(Func<Task<T>> func, Mafile mafile,
HttpClientHandlerPair? chp = null, string? snackbarPrefix = null) HttpClientHandlerPair? chp = null, string? snackbarPrefix = null, bool allowInteractiveLogin = true)
{ {
chp ??= MaClient.GetHttpClientHandlerPair(mafile); chp ??= MaClient.GetHttpClientHandlerPair(mafile);
await Semaphore.WaitAsync(); await Semaphore.WaitAsync();
try try
{ {
return await HandleInternal(func, chp.Value, mafile, snackbarPrefix); return await HandleInternal(func, chp.Value, mafile, snackbarPrefix, allowInteractiveLogin);
} }
finally finally
{ {
@@ -31,7 +33,7 @@ public static partial class SessionHandler
} }
private static async Task<T> HandleInternal<T>(Func<Task<T>> func, HttpClientHandlerPair chp, Mafile mafile, private static async Task<T> HandleInternal<T>(Func<Task<T>> func, HttpClientHandlerPair chp, Mafile mafile,
string? snackbarPrefix = null) string? snackbarPrefix = null, bool allowInteractiveLogin = true)
{ {
using var scope = Shell.Logger.PushScopeProperty("Scope", "SessionHandler"); using var scope = Shell.Logger.PushScopeProperty("Scope", "SessionHandler");
Exception? currentException = null; Exception? currentException = null;
@@ -86,6 +88,32 @@ public static partial class SessionHandler
} }
} }
if (allowInteractiveLogin && !DialogHost.IsDialogOpen(null))
{
var noProxy = mafile.Proxy == null && MaClient.DefaultProxy == null;
var currentPassword = GetPassword(mafile);
Task<LoginAgainVM?> dialogTask = null!;
Application.Current.Dispatcher.Invoke(() =>
{
dialogTask = DialogsController.ShowLoginAgainDialog(mafile.AccountName, currentPassword, noProxy);
});
var vm = await dialogTask;
if (vm != null)
{
Shell.Logger.Info("User provided credentials interactively for {name}, attempting login",
mafile.AccountName);
var logged = await LoginAgainInternal(chp, mafile, vm.Password, vm.SavePassword);
if (logged)
{
Shell.Logger.Debug("Interactive login for {name} succeeded, retrying operation",
mafile.AccountName);
return await func();
}
}
}
throw new SessionPermanentlyExpiredException(SessionPermanentlyExpiredException.SESSION_EXPIRED_MSG, throw new SessionPermanentlyExpiredException(SessionPermanentlyExpiredException.SESSION_EXPIRED_MSG,
currentException); currentException);
} }
@@ -118,6 +146,7 @@ public static partial class SessionHandler
{ {
Shell.Logger.Debug(ex, "Failed to relogin mafile {name} {steamid}", mafile.AccountName, Shell.Logger.Debug(ex, "Failed to relogin mafile {name} {steamid}", mafile.AccountName,
mafile.SessionData?.SteamId); mafile.SessionData?.SteamId);
ExceptionHandler.Handle(ex);
return false; return false;
} }
finally finally
+1 -1
View File
@@ -91,7 +91,7 @@ public partial class Settings : ObservableObject
[ObservableProperty] private Color? _iconColor; [ObservableProperty] private Color? _iconColor;
[ObservableProperty] private bool _isPasswordSet; [ObservableProperty] private bool _isPasswordSet;
[ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English; [ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English;
[ObservableProperty] private bool _legacyMode = true; [ObservableProperty] private bool _confirmMafileImport = true;
[ObservableProperty] private bool _allowAutoUpdate; [ObservableProperty] private bool _allowAutoUpdate;
[ObservableProperty] private bool _useAccountNameAsMafileName; [ObservableProperty] private bool _useAccountNameAsMafileName;
+3 -2
View File
@@ -3,7 +3,6 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.Exceptions;
using NebulaAuth.Model.MAAC; using NebulaAuth.Model.MAAC;
using NebulaAuth.Model.MafileExport; using NebulaAuth.Model.MafileExport;
using NebulaAuth.Model.Mafiles; using NebulaAuth.Model.Mafiles;
@@ -40,7 +39,8 @@ public static class Shell
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new CantAlignTimeException("", ex); Logger.Error(ex, "Failed to align time with Steam");
TimeAligner.SetTimeDifference(0);
} }
var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1; var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1;
@@ -48,6 +48,7 @@ public static class Shell
ProxyAssignmentCache.Initialize(Storage.MaFiles); ProxyAssignmentCache.Initialize(Storage.MaFiles);
MAACStorage.Initialize(); MAACStorage.Initialize();
MafileExporterStorage.Initialize(); MafileExporterStorage.Initialize();
_ = LinksManager.FetchAsync();
ExtensionsLogger.LogDebug("Application started"); ExtensionsLogger.LogDebug("Application started");
} }
+12 -12
View File
@@ -3,20 +3,20 @@ using Newtonsoft.Json;
namespace NebulaAuth.Model.Update; namespace NebulaAuth.Model.Update;
public class ChangelogEntry public class ChangelogEntry
{ {
[JsonProperty("version")] public string Version { get; set; } = string.Empty; [JsonProperty("version")] public string Version { get; set; } = string.Empty;
[JsonProperty("date")] public string Date { get; set; } = string.Empty; [JsonProperty("date")] public string Date { get; set; } = string.Empty;
[JsonProperty("changes")] public List<ChangeItem> Changes { get; set; } = new(); [JsonProperty("changes")] public List<ChangeItem> Changes { get; set; } = new();
} }
public class ChangeItem public class ChangeItem
{ {
[JsonProperty("type")] public string Type { get; set; } = string.Empty; [JsonProperty("type")] public string Type { get; set; } = string.Empty;
[JsonProperty("text")] public string Text { get; set; } = string.Empty; [JsonProperty("text")] public string Text { get; set; } = string.Empty;
[JsonProperty("link")] public string? Link { get; set; } [JsonProperty("link")] public string? Link { get; set; }
} }
+1 -1
View File
@@ -10,7 +10,7 @@
<SatelliteResourceLanguages>en;ru;ua</SatelliteResourceLanguages> <SatelliteResourceLanguages>en;ru;ua</SatelliteResourceLanguages>
<ApplicationIcon>Theme\lock.ico</ApplicationIcon> <ApplicationIcon>Theme\lock.ico</ApplicationIcon>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AssemblyVersion>1.8.4</AssemblyVersion> <AssemblyVersion>1.8.5</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
@@ -36,7 +36,8 @@
<!-- Window Buttons --> <!-- Window Buttons -->
<StackPanel <StackPanel
HorizontalAlignment="Right" HorizontalAlignment="Right"
Orientation="Horizontal"> Orientation="Horizontal"
TextElement.Foreground="{DynamicResource PrimaryContentBrush}">
<StackPanel.Resources> <StackPanel.Resources>
<Style TargetType="materialDesign:PackIcon"> <Style TargetType="materialDesign:PackIcon">
<Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="HorizontalAlignment" Value="Center" />
+1 -1
View File
@@ -93,7 +93,7 @@ public static class ExceptionHandler
} }
case LoginException e: case LoginException e:
{ {
return "LoginException".GetCodeBehindLocalization() + ": " + return "LoginException".GetCodeBehindLocalization() +
ErrorTranslatorHelper.TranslateLoginError(e.Error); ErrorTranslatorHelper.TranslateLoginError(e.Error);
} }
case UnsupportedAuthTypeException e: case UnsupportedAuthTypeException e:
@@ -12,31 +12,10 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader Title="{Tr ConfirmCancelDialog.Title}"
<Grid.ColumnDefinitions> CloseParameter="{StaticResource False}" />
<ColumnDefinition Width="*" /> <Grid Grid.Row="1" Margin="12">
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<!--<materialDesign:PackIcon Kind="WarningCircleOutline" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />-->
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr ConfirmCancelDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{StaticResource False}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<Grid Grid.Row="2" Margin="12">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -6,6 +6,7 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other" xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:model="clr-namespace:NebulaAuth.Model" xmlns:model="clr-namespace:NebulaAuth.Model"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
d:DataContext="{d:DesignInstance other:LoginAgainVM}" d:DataContext="{d:DesignInstance other:LoginAgainVM}"
@@ -15,53 +16,39 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Login"
<Grid.ColumnDefinitions> Title="{Tr LoginAgainDialog.Title}"
<ColumnDefinition Width="*" /> CloseParameter="{StaticResource False}" />
<ColumnDefinition Width="Auto" /> <Grid Grid.Row="1" Margin="12">
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Login" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr LoginAgainDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{StaticResource False}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<Grid Grid.Row="2" Margin="12">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBox TabIndex="0" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" <nebulaAuth:HintBox Grid.Row="0"
Margin="10,10,10,0"
Severity="Warning"
FontSize="14"
Visibility="{Binding ShowNoProxyWarning, Converter={StaticResource BooleanToVisibilityConverter}}"
Text="{Tr LoginAgainDialog.NoProxyWarning}" />
<TextBox TabIndex="0" Grid.Row="1" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.HasClearButton="True" materialDesign:TextFieldAssist.HasClearButton="True"
Padding="10" Padding="10"
FontSize="15" FontSize="15"
Margin="10,10,10,0" Margin="10,10,10,0"
Grid.Row="0"
Style="{StaticResource MaterialDesignOutlinedTextBox}" Style="{StaticResource MaterialDesignOutlinedTextBox}"
materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}"
materialDesign:HintAssist.IsFloating="False" materialDesign:HintAssist.IsFloating="False"
materialDesign:TextFieldAssist.LeadingIcon="Key" materialDesign:TextFieldAssist.LeadingIcon="Key"
materialDesign:TextFieldAssist.HasLeadingIcon="True" /> materialDesign:TextFieldAssist.HasLeadingIcon="True" />
<CheckBox FontSize="15" Grid.Row="1" Margin="10,5,10,0" <CheckBox FontSize="15" Grid.Row="2" Margin="10,5,10,0"
IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}"
IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" /> IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" />
<Grid Grid.Row="2" Margin="10,10,10,0"> <Grid Grid.Row="3" Margin="10,10,10,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition /> <ColumnDefinition />
@@ -7,6 +7,7 @@
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other" xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:model="clr-namespace:NebulaAuth.Model" xmlns:model="clr-namespace:NebulaAuth.Model"
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities" xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
d:DataContext="{d:DesignInstance other:LoginAgainVM}" d:DataContext="{d:DesignInstance other:LoginAgainVM}"
@@ -16,32 +17,12 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Login"
<Grid.ColumnDefinitions> Title="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}"
<ColumnDefinition Width="*" /> TitleAccent="{Binding UserName}"
<ColumnDefinition Width="Auto" /> CloseParameter="{StaticResource False}" />
</Grid.ColumnDefinitions> <Grid Grid.Row="1" Margin="12">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Login" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontSize="16" VerticalAlignment="Center" Foreground="{DynamicResource BaseContentBrush}"
Margin="7,0,0,0" HorizontalAlignment="Left">
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}" />
<Run FontWeight="Bold" Text="{Binding UserName}" />
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{StaticResource False}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<Grid Grid.Row="2" Margin="12">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -0,0 +1,112 @@
<UserControl x:Class="NebulaAuth.View.Dialogs.MafileImportDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d"
Height="Auto" Width="Auto" MaxWidth="520"
Background="{DynamicResource WindowBackground}">
<Grid MinHeight="240" MinWidth="440">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<nebulaAuth:DialogHeader Title="{Tr MafileImportDialog.Title}" />
<Grid Grid.Row="1" Margin="14,14,14,16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Margin="6,0,6,12" Padding="12,10"
CornerRadius="8"
BorderThickness="1">
<Border.Background>
<SolidColorBrush Color="{DynamicResource BaseContentColor}" Opacity="0.07" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseContentColor}" Opacity="0.12" />
</Border.BorderBrush>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Tr MafileImportDialog.TotalCount}" Opacity="0.82" />
<TextBlock Grid.Column="1" Text="{Binding TotalCount, Mode=OneWay}" FontWeight="SemiBold" />
<TextBlock Grid.Row="1" Text="{Tr MafileImportDialog.ConflictCount}" Opacity="0.82"
Visibility="{Binding HasConflicts, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ConflictCount, Mode=OneWay}"
FontWeight="SemiBold"
Foreground="{DynamicResource WarningBrush}"
Visibility="{Binding HasConflicts, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Border>
<CheckBox Grid.Row="1"
Margin="10,4,10,8"
IsChecked="{Binding OverwriteConflicts}"
Visibility="{Binding HasConflicts, Converter={StaticResource BooleanToVisibilityConverter}}"
Content="{Tr MafileImportDialog.OverwriteConflicts}" />
<Separator Grid.Row="2"
Margin="10,2,10,2" Opacity="0.35"
Visibility="{Binding HasConflicts, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox Grid.Row="3"
Margin="10,4,10,0"
IsChecked="{Binding AddToGroup}"
Content="{Tr MafileImportDialog.AddToGroup}" />
<ComboBox x:Name="GroupBox"
Grid.Row="4"
Margin="42,0,10,0"
IsEditable="True"
ItemsSource="{Binding Groups}"
Text="{Binding Group, UpdateSourceTrigger=PropertyChanged}"
materialDesign:HintAssist.Hint="{Tr MafileImportDialog.GroupFieldHint}"
materialDesign:HintAssist.IsFloating="False"
Visibility="{Binding AddToGroup, Converter={StaticResource BooleanToVisibilityConverter}}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
<Grid Grid.Row="5" Margin="10,16,10,5">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Width="130"
HorizontalAlignment="Left"
Margin="2,0,4,0"
Style="{StaticResource MaterialDesignFlatDarkBgButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{Binding}"
Content="{Tr Common.OK}"
IsDefault="True" />
<Button Grid.Column="1"
HorizontalAlignment="Right"
Width="130"
Margin="0,0,2,0"
Style="{StaticResource MaterialDesignOutlinedButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{x:Null}"
Content="{Tr Common.Cancel}"
IsCancel="True" />
</Grid>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,9 @@
namespace NebulaAuth.View.Dialogs;
public partial class MafileImportDialog
{
public MafileImportDialog()
{
InitializeComponent();
}
}
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other" xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
d:DataContext="{d:DesignInstance other:SdaPasswordDialogVM}" d:DataContext="{d:DesignInstance other:SdaPasswordDialogVM}"
@@ -14,29 +15,11 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Lock"
<Grid.ColumnDefinitions> Title="{Tr SdaPasswordDialog.Title}"
<ColumnDefinition Width="*" /> CloseParameter="{StaticResource False}" />
<ColumnDefinition Width="Auto" /> <Grid Grid.Row="1" Margin="12">
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Lock" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontSize="16" VerticalAlignment="Center" Foreground="{DynamicResource BaseContentBrush}"
Margin="7,0,0,0" HorizontalAlignment="Left"
Text="{Tr SdaPasswordDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{StaticResource False}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<Grid Grid.Row="2" Margin="12">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -5,8 +5,10 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other" xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance other:LoginAgainVM}"> d:DataContext="{d:DesignInstance other:LoginAgainVM}"
Background="{DynamicResource WindowBackground}">
<Grid MinHeight="100" MinWidth="300" MaxWidth="400"> <Grid MinHeight="100" MinWidth="300" MaxWidth="400">
@@ -15,38 +17,19 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Encryption"
<Grid.ColumnDefinitions> Title="{Tr SetEncryptionPasswordDialog.Title}"
<ColumnDefinition Width="*" /> CloseParameter="{StaticResource False}" />
<ColumnDefinition Width="Auto" /> <TextBlock Grid.Row="1" IsHitTestVisible="False" TextWrapping="Wrap" FontWeight="Normal"
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Encryption" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr SetEncryptionPasswordDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{StaticResource False}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<TextBlock Grid.Row="2" IsHitTestVisible="False" TextWrapping="Wrap" FontWeight="Normal"
FontSize="16" FontSize="16"
Text="{Tr SetEncryptionPasswordDialog.DialogText}" Text="{Tr SetEncryptionPasswordDialog.DialogText}"
Margin="10" HorizontalAlignment="Left" /> Margin="10" HorizontalAlignment="Left" />
<PasswordBox TabIndex="0" FontSize="16" x:Name="PasswordBox" <PasswordBox TabIndex="0" FontSize="16" x:Name="PasswordBox"
materialDesign:PasswordBoxAssist.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}" materialDesign:PasswordBoxAssist.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
Margin="10" Grid.Row="3" Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}" Margin="10" Grid.Row="2" Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
materialDesign:HintAssist.Hint="{Tr SetEncryptionPasswordDialog.Password}" /> materialDesign:HintAssist.Hint="{Tr SetEncryptionPasswordDialog.Password}" />
<Grid Grid.Row="4" Margin="10"> <Grid Grid.Row="3" Margin="10">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition /> <ColumnDefinition />
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
Height="Auto" Width="Auto" MaxWidth="500" Height="Auto" Width="Auto" MaxWidth="500"
Background="{DynamicResource WindowBackground}"> Background="{DynamicResource WindowBackground}">
@@ -11,31 +12,10 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader x:Name="TitleHeader"
<Grid.ColumnDefinitions> Title="{Tr TextFieldDialog.Title}" />
<ColumnDefinition Width="*" /> <Grid Grid.Row="1" Margin="12">
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<!--<materialDesign:PackIcon Kind="WarningCircleOutline" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />-->
<TextBlock x:Name="TitleTextBlock" FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr TextFieldDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" CommandParameter="{x:Null}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<Grid Grid.Row="2" Margin="12">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -14,7 +14,7 @@ public partial class TextFieldDialog
InitializeComponent(); InitializeComponent();
if (!string.IsNullOrWhiteSpace(title)) if (!string.IsNullOrWhiteSpace(title))
TitleTextBlock.Text = title; TitleHeader.Title = title;
if (!string.IsNullOrEmpty(msg)) if (!string.IsNullOrEmpty(msg))
HintAssist.SetHint(TextField, msg); HintAssist.SetHint(TextField, msg);
+6 -27
View File
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other" xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d" mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
d:DataContext="{d:DesignInstance other:UpdateDialogVM}" d:DataContext="{d:DesignInstance other:UpdateDialogVM}"
@@ -15,37 +16,15 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Header --> <!-- Header -->
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Update"
<Grid.ColumnDefinitions> Title="{Tr UpdateDialog.Title, IsDynamic=False}"
<ColumnDefinition Width="*" /> TitleAccent="{Binding Version, Mode=OneWay}" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Update" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18">
<Run Text="{Tr UpdateDialog.Title, IsDynamic=False}" />
<Run FontWeight="Bold" Text="{Binding Version, Mode=OneWay}" />
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Opacity="0.7" />
<!-- Changelog --> <!-- Changelog -->
<Grid Grid.Row="2" Margin="12,10,12,4"> <Grid Grid.Row="1" Margin="12,10,12,4">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -120,7 +99,7 @@
</Grid> </Grid>
<!-- Buttons --> <!-- Buttons -->
<Grid Grid.Row="3" Margin="12,6,12,14"> <Grid Grid.Row="2" Margin="12,6,12,14">
<!-- Normal buttons --> <!-- Normal buttons -->
<StackPanel <StackPanel
+1 -24
View File
@@ -56,30 +56,7 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Grid.Resources> </Grid.Resources>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="LinkBoxOutline" Title="{Tr LinkerDialog.Title}" />
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="LinkBoxOutline" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr LinkerDialog.Title}" />
<TextBlock Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12">
<Hyperlink Command="{Binding OpenTroubleshootingCommand}">
<Run Text="{Tr LinkerDialog.GotErrorHyperlinkText, IsDynamic=False}" />
</Hyperlink>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Background="DarkGray" Opacity="0.4" Grid.Row="1" />
<nebulaAuth:HintBox FontSize="14" <nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}" Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0" Grid.Row="2" Margin="10,10,10,0"
+11 -6
View File
@@ -8,7 +8,8 @@
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
Background="Transparent" Background="Transparent"
RenderOptions.BitmapScalingMode="HighQuality"> RenderOptions.BitmapScalingMode="HighQuality"
Loaded="LinksView_Loaded">
<materialDesign:Card Padding="24" HorizontalAlignment="Center" VerticalAlignment="Center" UniformCornerRadius="12"> <materialDesign:Card Padding="24" HorizontalAlignment="Center" VerticalAlignment="Center" UniformCornerRadius="12">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
@@ -49,16 +50,20 @@
</StackPanel> </StackPanel>
</Button> </Button>
<Button Margin="0,0,0,5" FontSize="18" Style="{StaticResource MaterialDesignFlatButton}" <Button x:Name="WebsiteButton" Margin="0,0,0,5" FontSize="18"
Click="Website_Click" IsEnabled="False"> Style="{StaticResource MaterialDesignFlatButton}"
IsEnabled="False"
Click="Website_Click">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<materialDesign:PackIcon Kind="Web" Width="24" Height="24" Margin="0 0 8 0" /> <materialDesign:PackIcon Kind="Web" Width="24" Height="24" Margin="0 0 8 0" />
<TextBlock Text="Web-site" VerticalAlignment="Center" /> <TextBlock Text="Achiefy" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Margin="0,0,0,5" FontSize="18" Style="{StaticResource MaterialDesignFlatButton}" <Button x:Name="DocumentationButton" Margin="0,0,0,5" FontSize="18"
Click="Documentation_Click" IsEnabled="False"> Style="{StaticResource MaterialDesignFlatButton}"
IsEnabled="False"
Click="Documentation_Click">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Viewbox Width="22" Height="22" Margin="0 0 8 0"> <Viewbox Width="22" Height="22" Margin="0 0 8 0">
<Canvas Width="22" Height="22"> <Canvas Width="22" Height="22">
+11 -3
View File
@@ -1,4 +1,4 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
@@ -13,6 +13,12 @@ public partial class LinksView : UserControl
InitializeComponent(); InitializeComponent();
} }
private void LinksView_Loaded(object sender, RoutedEventArgs e)
{
WebsiteButton.IsEnabled = LinksManager.WebsiteUrl != null;
DocumentationButton.IsEnabled = LinksManager.DocumentationUrl != null;
}
private void Telegram_Click(object sender, RoutedEventArgs e) private void Telegram_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://t.me/nebulaauth") {UseShellExecute = true}); Process.Start(new ProcessStartInfo("https://t.me/nebulaauth") {UseShellExecute = true});
@@ -26,12 +32,14 @@ public partial class LinksView : UserControl
private void Website_Click(object sender, RoutedEventArgs e) private void Website_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://yourwebsite.com") {UseShellExecute = true}); if (LinksManager.WebsiteUrl != null)
Process.Start(new ProcessStartInfo(LinksManager.WebsiteUrl) {UseShellExecute = true});
} }
private void Documentation_Click(object sender, RoutedEventArgs e) private void Documentation_Click(object sender, RoutedEventArgs e)
{ {
Process.Start(new ProcessStartInfo("https://yourwebsite.com") {UseShellExecute = true}); if (LinksManager.DocumentationUrl != null)
Process.Start(new ProcessStartInfo(LinksManager.DocumentationUrl) {UseShellExecute = true});
} }
private void CheckForUpdates_Click(object sender, RoutedEventArgs e) private void CheckForUpdates_Click(object sender, RoutedEventArgs e)
+12 -26
View File
@@ -9,8 +9,8 @@
mc:Ignorable="d" mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
FontFamily="{materialDesign:MaterialDesignFont}" FontFamily="{materialDesign:MaterialDesignFont}"
MinWidth="400" MinWidth="500"
MaxWidth="400" MaxWidth="500"
Background="{DynamicResource WindowBackground}" Background="{DynamicResource WindowBackground}"
d:DataContext="{d:DesignInstance other:MafileExporterVM}" d:DataContext="{d:DesignInstance other:MafileExporterVM}"
d:Background="#141119" d:Background="#141119"
@@ -19,32 +19,12 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="FileExport" Title="{Tr ExportDialog.ExportTitle}" />
<Grid.ColumnDefinitions> <Grid Grid.Row="1" Margin="15" TextElement.FontSize="14">
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="FileExport" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr ExportDialog.ExportTitle}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
IsCancel="True">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Background="DarkGray" Opacity="0.4" Grid.Row="1" />
<Grid Grid.Row="2" Margin="15" TextElement.FontSize="14">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
@@ -133,6 +113,12 @@
IsEnabled="{Binding CurrentTemplate, Converter={StaticResource NullableToBooleanConverter}}"> IsEnabled="{Binding CurrentTemplate, Converter={StaticResource NullableToBooleanConverter}}">
<StackPanel> <StackPanel>
<CheckBox IsChecked="{Binding CurrentTemplate.ExportToZip, FallbackValue=False}"
Margin="3,0,0,3"
Content="{Tr ExportDialog.ExportOptions.ExportToZip}"
ToolTipService.InitialShowDelay="200"
ToolTip="{Tr ExportDialog.Tooltips.ExportToZip}" />
<TextBlock FontWeight="DemiBold" <TextBlock FontWeight="DemiBold"
Text="{Tr ExportDialog.ExportSettingsCommon}" /> Text="{Tr ExportDialog.ExportSettingsCommon}" />
@@ -233,8 +219,8 @@
</Grid> </Grid>
<Separator Grid.Row="3" /> <Separator Grid.Row="2" />
<Button Command="{Binding ExportCommand}" Style="{StaticResource MaterialDesignFlatDarkBgButton}" Grid.Row="4" <Button Command="{Binding ExportCommand}" Style="{StaticResource MaterialDesignFlatDarkBgButton}" Grid.Row="3"
Margin="20,7,20,7" MaxWidth="150" Content="{Tr ExportDialog.ExportButton}" /> Margin="20,7,20,7" MaxWidth="150" Content="{Tr ExportDialog.ExportButton}" />
</Grid> </Grid>
</UserControl> </UserControl>
+4 -25
View File
@@ -29,9 +29,6 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.Resources> <Grid.Resources>
<Style TargetType="materialDesign:PackIcon"> <Style TargetType="materialDesign:PackIcon">
@@ -58,35 +55,17 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Grid.Resources> </Grid.Resources>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="FileMove" Title="{Tr MafileMoverView.Title}" />
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="FileMove" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr MafileMoverView.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Background="DarkGray" Opacity="0.4" Grid.Row="1" />
<nebulaAuth:HintBox FontSize="14" <nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}" Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0" Grid.Row="1" Margin="10,10,10,0"
Text="{Binding Tip}" /> Text="{Binding Tip}" />
<ContentControl <ContentControl
Grid.Row="3" Margin="20" Content="{Binding CurrentStep}" /> Grid.Row="2" Margin="20" Content="{Binding CurrentStep}" />
<nebulaAuth:HintBox FontSize="14" <nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}" Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="4" Margin="10,0,10,10" Grid.Row="3" Margin="10,0,10,10"
Text="{Binding Error}" /> Text="{Binding Error}" />
</Grid> </Grid>
</UserControl> </UserControl>
+5 -24
View File
@@ -20,34 +20,14 @@
FontSize="16"> FontSize="16">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Title --> <nebulaAuth:DialogHeader IconKind="Proxy" Title="{Tr ProxyManagerDialog.Title}" />
<Grid Margin="10,10,10,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Proxy" Width="20" Height="20" Margin="0,0,5,0" VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr ProxyManagerDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static md:DialogHost.CloseDialogCommand}"
IsCancel="True">
<md:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" />
<!-- Tabs --> <!-- Tabs -->
<TabControl Grid.Row="2" <TabControl Grid.Row="1"
md:ColorZoneAssist.Background="{DynamicResource Base100Brush}" md:ColorZoneAssist.Background="{DynamicResource Base100Brush}"
Style="{StaticResource MaterialDesignUniformTabControl}"> Style="{StaticResource MaterialDesignUniformTabControl}">
@@ -154,7 +134,8 @@
</Run> </Run>
</TextBlock> </TextBlock>
<TextBlock Grid.Column="1" VerticalAlignment="Center" FontSize="10" Margin="0,0,2,0" <TextBlock Grid.Column="1" VerticalAlignment="Center" FontSize="10"
Margin="0,0,2,0"
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}" Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}"
Text="{Binding Key, Converter={StaticResource ProxyAccountCountConverter}}" /> Text="{Binding Key, Converter={StaticResource ProxyAccountCountConverter}}" />
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0" <Button Style="{StaticResource MaterialDesignIconButton}" Padding="0"
@@ -164,7 +145,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"> CommandParameter="{Binding}">
<md:PackIcon Height="16" Width="16" Kind="Heart" /> <md:PackIcon Height="16" Width="16" Kind="Heart" />
</Button> </Button>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@@ -20,7 +20,6 @@
FontSize="16"> FontSize="16">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -29,36 +28,10 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- ===== HEADER ===== --> <nebulaAuth:DialogHeader IconKind="ShieldKey" Title="{Tr SetAccountPasswordsView.Title}" />
<Grid Margin="10,10,10,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="ShieldKey" Width="20" Height="20"
Margin="0,0,5,0" VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock Text="{Tr SetAccountPasswordsView.Title}"
FontSize="18"
VerticalAlignment="Center"
Foreground="{DynamicResource BaseContentBrush}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right"
Command="{x:Static md:DialogHost.CloseDialogCommand}"
IsCancel="True">
<md:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" />
<!-- ===== ENCRYPTION PASSWORD SECTION ===== --> <!-- ===== ENCRYPTION PASSWORD SECTION ===== -->
<Grid Grid.Row="2"> <Grid Grid.Row="1">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
<RowDefinition /> <RowDefinition />
@@ -109,7 +82,7 @@
<!-- ===== ACCOUNTS INPUT ===== --> <!-- ===== ACCOUNTS INPUT ===== -->
<TextBox Grid.Row="3" <TextBox Grid.Row="2"
Margin="15,0,15,10" Margin="15,0,15,10"
Text="{Binding AccountsPasswords, UpdateSourceTrigger=PropertyChanged}" Text="{Binding AccountsPasswords, UpdateSourceTrigger=PropertyChanged}"
FontSize="13" FontSize="13"
@@ -124,14 +97,14 @@
md:TextFieldAssist.TextBoxViewVerticalAlignment="Top" md:TextFieldAssist.TextBoxViewVerticalAlignment="Top"
VerticalContentAlignment="Top" /> VerticalContentAlignment="Top" />
<!-- ===== INFO TEXT ===== --> <!-- ===== INFO TEXT ===== -->
<TextBlock Grid.Row="4" <TextBlock Grid.Row="3"
Margin="20,0,20,15" Margin="20,0,20,15"
TextWrapping="Wrap" TextWrapping="Wrap"
FontSize="12" FontSize="12"
Foreground="{DynamicResource BaseContentBrush}" Foreground="{DynamicResource BaseContentBrush}"
Text="{Tr SetAccountPasswordsView.EncryptionHint}" /> Text="{Tr SetAccountPasswordsView.EncryptionHint}" />
<Button Grid.Row="5" <Button Grid.Row="4"
Margin="10,0,0,10" Margin="10,0,0,10"
Width="120" Width="120"
Content="{Tr Common.Save}" Content="{Tr Common.Save}"
@@ -139,7 +112,7 @@
Style="{StaticResource MaterialDesignOutlinedButton}" Cursor="Hand" /> Style="{StaticResource MaterialDesignOutlinedButton}" Cursor="Hand" />
<!-- ===== HINT / RESULT BOX ===== --> <!-- ===== HINT / RESULT BOX ===== -->
<nebulaAuth:HintBox Grid.Row="6" <nebulaAuth:HintBox Grid.Row="5"
Margin="15,0,15,10" Margin="15,0,15,10"
Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}" Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
CloseCommand="{Binding ClearTipCommand}" CloseCommand="{Binding ClearTipCommand}"
+7 -28
View File
@@ -18,36 +18,15 @@
<Grid> <Grid>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Margin="10,10,10,5"> <nebulaAuth:DialogHeader IconKind="Settings"
<Grid.ColumnDefinitions> Title="{Tr SettingsDialog.Title}"
<ColumnDefinition Width="*" /> IsCloseEnabled="{Binding ApplyRenameSettingCommand.IsRunning, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Settings" Width="20" Height="20" Margin="0,0,5,0"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryContentBrush}" />
<TextBlock FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr SettingsDialog.Title}" />
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
IsEnabled="{Binding ApplyRenameSettingCommand.IsRunning, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}"
IsCancel="True">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Margin="0,10,0,0" />
<TabControl materialDesign:ColorZoneAssist.Background="{DynamicResource Base100Brush}" <TabControl materialDesign:ColorZoneAssist.Background="{DynamicResource Base100Brush}"
Style="{StaticResource MaterialDesignUniformTabControl}" Grid.Row="2"> Style="{StaticResource MaterialDesignUniformTabControl}" Grid.Row="1">
<TabItem Header="{Tr SettingsDialog.MainSettings}"> <TabItem Header="{Tr SettingsDialog.MainSettings}">
<StackPanel Width="400" Margin="10,18,10,40" Orientation="Vertical" HorizontalAlignment="Center"> <StackPanel Width="400" Margin="10,18,10,40" Orientation="Vertical" HorizontalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,15,0,0" <ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,15,0,0"
@@ -59,9 +38,9 @@
Content="{Tr SettingsDialog.MinimizeToTray}" /> Content="{Tr SettingsDialog.MinimizeToTray}" />
<CheckBox Margin="0,5,0,0" IsChecked="{Binding LegacyMode}" <CheckBox Margin="0,5,0,0" IsChecked="{Binding ConfirmMafileImport}"
Content="{Tr SettingsDialog.LegacyMafileMode}" Content="{Tr SettingsDialog.ConfirmMafileImport}"
ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}" /> ToolTip="{Tr SettingsDialog.ConfirmMafileImportHint}" />
<!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>--> <!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>-->
<!--<CheckBox Margin="0,12,0,0" IsChecked="{Binding UseAccountNameAsMafileName}"> <!--<CheckBox Margin="0,12,0,0" IsChecked="{Binding UseAccountNameAsMafileName}">
+78 -20
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -30,6 +31,8 @@ public partial class MainVM //File //TODO: Refactor
bool SdaPasswordPrompted, bool SdaPasswordPrompted,
SDAEncryptionHelper.Context? SdaContext); SDAEncryptionHelper.Context? SdaContext);
private record MafileImportPlanItem(Mafile Mafile, bool RequiresRelogin, bool HasConflict);
public Settings Settings => Settings.Instance; public Settings Settings => Settings.Instance;
@@ -76,10 +79,11 @@ public partial class MainVM //File //TODO: Refactor
: Task.CompletedTask; : Task.CompletedTask;
} }
//TODO: extract flow and refactor, this method is too long and does too many things
public async Task AddMafile(string[] path) public async Task AddMafile(string[] path)
{ {
bool? confirmOverwrite = null;
var summary = new MafileImportSummary(); var summary = new MafileImportSummary();
var items = new List<MafileImportPlanItem>();
SDAEncryptionHelper.Context? sdaContext = null; SDAEncryptionHelper.Context? sdaContext = null;
var sdaPasswordPrompted = false; var sdaPasswordPrompted = false;
foreach (var str in path) foreach (var str in path)
@@ -95,16 +99,7 @@ public partial class MainVM //File //TODO: Refactor
continue; continue;
} }
var overwrite = confirmOverwrite ?? false; items.Add(new MafileImportPlanItem(mafile, false, HasImportConflict(mafile)));
var res = await Storage.AddNewMafileFromData(mafile, overwrite);
if (res == AddMafileResult.AlreadyExist && confirmOverwrite == null)
{
confirmOverwrite =
await DialogsController.ShowConfirmCancelDialog(GetLocalization("ConfirmMafileOverwrite"));
res = await Storage.AddNewMafileFromData(mafile, confirmOverwrite.Value);
}
summary.Apply(res);
} }
catch (FormatException) catch (FormatException)
{ {
@@ -114,18 +109,11 @@ public partial class MainVM //File //TODO: Refactor
{ {
if (path.Length == 1 && ex.Mafile != null) if (path.Length == 1 && ex.Mafile != null)
{ {
var mafile = ex.Mafile; items.Add(new MafileImportPlanItem(ex.Mafile, true, HasImportConflict(ex.Mafile)));
if (await HandleAddMafileWithoutSession(mafile))
{
summary.AddedOne();
}
else
{
summary.ErrorOne();
}
} }
else else
{ {
summary.ErrorOne();
SnackbarController.SendSnackbar( SnackbarController.SendSnackbar(
$"{GetLocalization("MafileImportError")} {Path.GetFileName(str)}{GetLocalization("MissingSessionInMafile")}", $"{GetLocalization("MafileImportError")} {Path.GetFileName(str)}{GetLocalization("MissingSessionInMafile")}",
TimeSpan.FromSeconds(4)); TimeSpan.FromSeconds(4));
@@ -133,9 +121,78 @@ public partial class MainVM //File //TODO: Refactor
} }
} }
if (items.Count == 0)
{
ShowImportSummary(summary);
return;
}
var conflictCount = items.Count(i => i.HasConflict);
var showImportDialog = Settings.ConfirmMafileImport || conflictCount > 0;
MafileImportDialogResult? importOptions = null;
if (showImportDialog)
{
importOptions = await DialogsController.ShowMafileImportDialog(Groups, items.Count, conflictCount);
if (importOptions == null)
{
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Canceled", "Canceled"));
return;
}
}
var importGroup = importOptions?.Group;
var overwriteConflicts = importOptions?.OverwriteConflicts ?? false;
foreach (var item in items)
{
ApplyImportGroup(item.Mafile, importGroup);
if (item.RequiresRelogin)
{
if (item.HasConflict && !overwriteConflicts)
{
summary.SkippedOne();
continue;
}
if (await HandleAddMafileWithoutSession(item.Mafile))
{
summary.AddedOne();
}
else
{
summary.ErrorOne();
}
continue;
}
var res = await Storage.AddNewMafileFromData(item.Mafile, overwriteConflicts);
summary.Apply(res);
}
if (summary.Added > 0)
{
QueryGroups();
if (!string.IsNullOrWhiteSpace(importGroup))
{
SelectedGroup = importGroup;
}
}
ShowImportSummary(summary); ShowImportSummary(summary);
} }
private static void ApplyImportGroup(Mafile mafile, string? group)
{
if (string.IsNullOrWhiteSpace(group)) return;
mafile.Group = group;
}
private static bool HasImportConflict(Mafile mafile)
{
var fileName = MafilesStorageHelper.CreateMafileFileName(mafile, Settings.Instance.UseAccountNameAsMafileName);
return File.Exists(Path.Combine(Storage.MafilesDirectory, fileName));
}
private async Task<MafileReadResult> TryReadMafile(string path, SDAEncryptionHelper.Context? sdaContext, private async Task<MafileReadResult> TryReadMafile(string path, SDAEncryptionHelper.Context? sdaContext,
bool sdaPasswordPrompted) bool sdaPasswordPrompted)
@@ -218,6 +275,7 @@ public partial class MainVM //File //TODO: Refactor
var result = data.SessionData != null; var result = data.SessionData != null;
if (!result) return result; if (!result) return result;
data.Filename = null;
await Storage.SaveMafileAsync(data); await Storage.SaveMafileAsync(data);
{ {
ResetQuery(); ResetQuery();
@@ -11,5 +11,7 @@ public partial class LoginAgainVM : ObservableObject
[ObservableProperty] private bool _savePassword; [ObservableProperty] private bool _savePassword;
[ObservableProperty] private bool _showNoProxyWarning;
[ObservableProperty] private string _userName = null!; [ObservableProperty] private string _userName = null!;
} }
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.MafileExport; using NebulaAuth.Model.MafileExport;
using NebulaAuth.Model.Mafiles;
namespace NebulaAuth.ViewModel.Other; namespace NebulaAuth.ViewModel.Other;
@@ -24,8 +25,7 @@ public partial class MafileExporterVM : ObservableObject
public ObservableCollection<MafileExportTemplateVM> Templates { get; } public ObservableCollection<MafileExportTemplateVM> Templates { get; }
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ExportCommand))] [ObservableProperty] private string? _accountsText;
private string? _accountsText;
private string? _cachedName; private string? _cachedName;
private MafileExportTemplateVM? _currentTemplate; private MafileExportTemplateVM? _currentTemplate;
@@ -160,6 +160,7 @@ public partial class MafileExporterVM : ObservableObject
IncludeNebulaProxy = true, IncludeNebulaProxy = true,
IncludeNebulaPassword = true, IncludeNebulaPassword = true,
IncludeNebulaGroup = true, IncludeNebulaGroup = true,
ExportToZip = false,
Path = null Path = null
}; };
MafileExporterStorage.AddTemplate(template); MafileExporterStorage.AddTemplate(template);
@@ -211,21 +212,26 @@ public partial class MafileExporterVM : ObservableObject
} }
} }
[RelayCommand(CanExecute = nameof(ExportCanExecute))] [RelayCommand(CanExecute = nameof(CurrentTemplateNotNull))]
private async Task Export() private async Task Export()
{ {
var lines = AccountsText;
var template = CurrentTemplate; var template = CurrentTemplate;
if (string.IsNullOrWhiteSpace(lines) || template == null) if (template == null) return;
{
return;
}
var split = Regex string[] split;
.Split(lines, "\r\n|\r|\n") if (string.IsNullOrWhiteSpace(AccountsText))
.Select(x => x.Trim()) {
.Where(x => !string.IsNullOrWhiteSpace(x)) split = Storage.MaFiles.Select(m => m.AccountName).ToArray();
.ToArray(); }
else
{
split = Regex
.Split(AccountsText, "\r\n|\r|\n")
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(ExtractLogin)
.ToArray();
}
ResetHintText(); ResetHintText();
ExportResult res; ExportResult res;
@@ -268,6 +274,14 @@ public partial class MafileExporterVM : ObservableObject
AccountsText = text; AccountsText = text;
} }
// Strips the password part from "login:password" input lines.
// SteamId64 values contain no colon, so any colon signals the login:password format.
private static string ExtractLogin(string line)
{
var idx = line.IndexOf(':');
return idx > 0 ? line[..idx] : line;
}
private void SetHintText(string? text, HintBoxSeverity severity = HintBoxSeverity.Error) private void SetHintText(string? text, HintBoxSeverity severity = HintBoxSeverity.Error)
{ {
HintText = text; HintText = text;
@@ -281,12 +295,6 @@ public partial class MafileExporterVM : ObservableObject
#region CanExecute #region CanExecute
private bool ExportCanExecute()
{
return CurrentTemplateNotNull() && !string.IsNullOrWhiteSpace(AccountsText);
}
private bool CurrentTemplateNotNull() private bool CurrentTemplateNotNull()
{ {
return CurrentTemplate != null; return CurrentTemplate != null;
@@ -307,6 +315,7 @@ public partial class MafileExporterVM : ObservableObject
public partial class MafileExportTemplateVM : ObservableObject public partial class MafileExportTemplateVM : ObservableObject
{ {
[ObservableProperty] private bool _exportToZip;
[ObservableProperty] private bool _includeIdentitySecret; [ObservableProperty] private bool _includeIdentitySecret;
[ObservableProperty] private bool _includeNebulaGroup; [ObservableProperty] private bool _includeNebulaGroup;
[ObservableProperty] private bool _includeNebulaPassword; [ObservableProperty] private bool _includeNebulaPassword;
@@ -334,6 +343,7 @@ public partial class MafileExportTemplateVM : ObservableObject
IncludeNebulaProxy = x.IncludeNebulaProxy, IncludeNebulaProxy = x.IncludeNebulaProxy,
IncludeNebulaPassword = x.IncludeNebulaPassword, IncludeNebulaPassword = x.IncludeNebulaPassword,
IncludeNebulaGroup = x.IncludeNebulaGroup, IncludeNebulaGroup = x.IncludeNebulaGroup,
ExportToZip = x.ExportToZip,
Path = x.Path Path = x.Path
}; };
} }
@@ -352,6 +362,7 @@ public partial class MafileExportTemplateVM : ObservableObject
IncludeNebulaProxy = IncludeNebulaProxy, IncludeNebulaProxy = IncludeNebulaProxy,
IncludeNebulaPassword = IncludeNebulaPassword, IncludeNebulaPassword = IncludeNebulaPassword,
IncludeNebulaGroup = IncludeNebulaGroup, IncludeNebulaGroup = IncludeNebulaGroup,
ExportToZip = ExportToZip,
Path = Path Path = Path
}; };
} }
@@ -368,6 +379,7 @@ public partial class MafileExportTemplateVM : ObservableObject
model.IncludeNebulaProxy = IncludeNebulaProxy; model.IncludeNebulaProxy = IncludeNebulaProxy;
model.IncludeNebulaPassword = IncludeNebulaPassword; model.IncludeNebulaPassword = IncludeNebulaPassword;
model.IncludeNebulaGroup = IncludeNebulaGroup; model.IncludeNebulaGroup = IncludeNebulaGroup;
model.ExportToZip = ExportToZip;
model.Path = Path; model.Path = Path;
} }
} }
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace NebulaAuth.ViewModel.Other;
public partial class MafileImportDialogVM : ObservableObject
{
public ObservableCollection<string> Groups { get; }
public int TotalCount { get; }
public int ConflictCount { get; }
public bool HasConflicts => ConflictCount > 0;
[ObservableProperty] private bool _addToGroup;
[ObservableProperty] private string? _group;
[ObservableProperty] private bool _overwriteConflicts;
public MafileImportDialogVM(IEnumerable<string> groups, int totalCount, int conflictCount)
{
Groups = new ObservableCollection<string>(groups);
TotalCount = totalCount;
ConflictCount = conflictCount;
}
}
public record MafileImportDialogResult(string? Group, bool OverwriteConflicts);
@@ -80,7 +80,7 @@ public partial class ProxyManagerVM
resolvedProxy = new MaProxy(parsedId, byId); resolvedProxy = new MaProxy(parsedId, byId);
} }
else if (ProxyStorage.DefaultScheme.TryParse(proxyPart, out var parsedProxy)) else if (ProxyStorage.DefaultScheme.TryParse(proxyPart, out var parsedProxy) || ProxyStorage.SignAtScheme.TryParse(proxyPart, out parsedProxy))
{ {
// Mode: login:proxy_string — find existing or add // Mode: login:proxy_string — find existing or add
resolvedProxy = FindOrAddProxy(parsedProxy); resolvedProxy = FindOrAddProxy(parsedProxy);
+3 -3
View File
@@ -168,10 +168,10 @@ public partial class SettingsVM : ObservableObject
} }
} }
public bool LegacyMode public bool ConfirmMafileImport
{ {
get => Settings.LegacyMode; get => Settings.ConfirmMafileImport;
set => Settings.LegacyMode = value; set => Settings.ConfirmMafileImport = value;
} }
public bool AllowAutoUpdate public bool AllowAutoUpdate
@@ -80,4 +80,10 @@ public static class TimeAligner
client.Dispose(); client.Dispose();
} }
} }
public static void SetTimeDifference(int timeDifference)
{
_timeDifference = timeDifference;
_aligned = true;
}
} }