diff --git a/AES_GCM/AES_GCM.vcxproj b/AES_GCM/AES_GCM.vcxproj index b97d9115f..57892bcdd 100644 --- a/AES_GCM/AES_GCM.vcxproj +++ b/AES_GCM/AES_GCM.vcxproj @@ -34,7 +34,7 @@ 14.0 true Windows Store - 10.0.17134.0 + 10.0.17763.0 10.0.15063.0 10.0 diff --git a/BackgroundSocket/BackgroundSocket.csproj b/BackgroundSocket/BackgroundSocket.csproj index 7462499f0..b57c26710 100644 --- a/BackgroundSocket/BackgroundSocket.csproj +++ b/BackgroundSocket/BackgroundSocket.csproj @@ -4,15 +4,15 @@ Debug AnyCPU - {2343CA08-3D0A-4412-A0ED-E8E68E0E208B} + {94944B01-6CE9-42BB-9180-7682BCE2298F} winmdobj Properties BackgroundSocket BackgroundSocket - de-DE + en-US UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -27,6 +27,7 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest AnyCPU @@ -36,6 +37,7 @@ TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest x86 @@ -44,9 +46,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x86 false prompt + latest x86 @@ -55,9 +57,9 @@ true ;2008 pdbonly - x86 false prompt + latest ARM @@ -66,9 +68,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - ARM false prompt + latest ARM @@ -77,9 +79,31 @@ true ;2008 pdbonly - ARM false prompt + latest + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + latest + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + latest x64 @@ -88,9 +112,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x64 false prompt + latest x64 @@ -99,36 +123,35 @@ true ;2008 pdbonly - x64 false prompt + latest PackageReference - - 6.2.2 + 6.2.3 - - - - {53bb5463-9646-4f79-be97-3d73473aad92} + {0c1fb090-4492-441d-b182-c999df71f917} Logging - {899fb043-af8b-4294-95b6-1482c79459ac} + {64b9a47f-d404-4d0b-a34a-bec280c5ff6b} XMPP_API + + + 14.0 diff --git a/BackgroundSocket/Properties/AssemblyInfo.cs b/BackgroundSocket/Properties/AssemblyInfo.cs index b1666fbeb..2567ae0d6 100644 --- a/BackgroundSocket/Properties/AssemblyInfo.cs +++ b/BackgroundSocket/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BackgroundSocket")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Component_Tests/Classes/Crypto/Test_CryptoUtils.cs b/Component_Tests/Classes/Crypto/Test_CryptoUtils.cs index d6836dbc4..c6a6265e8 100644 --- a/Component_Tests/Classes/Crypto/Test_CryptoUtils.cs +++ b/Component_Tests/Classes/Crypto/Test_CryptoUtils.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using libsignal; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Linq; using System.Text; @@ -123,5 +124,19 @@ public void Test_CryptoUtils_HexToByteArray() string saltStringHex = CryptoUtils.byteArrayToHexString(salt); Assert.IsTrue(saltStringHex.Equals(saltStringHexRef)); } + + [TestCategory("Crypto")] + [TestMethod] + public void Test_CryptoUtils_GenOmemoFingerprint() + { + string identKeyPairSerializedHex = "0a210511dbad7fcece74492f390f0a2a8387c543e802ab7f2176e303e28840559c41521220a082ae07fd8941536457cb2f3e4b560a87991d380f02af460b5204e46ca7b15a"; + byte[] identKeyPairSerialized = CryptoUtils.hexStringToByteArray(identKeyPairSerializedHex); + IdentityKeyPair identKeyPair = new IdentityKeyPair(identKeyPairSerialized); + + string outputRef = "11dbad7f cece7449 2f390f0a 2a8387c5 43e802ab 7f2176e3 03e28840 559c4152"; + string output = CryptoUtils.generateOmemoFingerprint(identKeyPair.getPublicKey(), false); + + Assert.AreEqual(outputRef, output); + } } } diff --git a/Component_Tests/Classes/Misc/Test_JidParser.cs b/Component_Tests/Classes/Misc/Test_JidParser.cs new file mode 100644 index 000000000..2d8d2aaa0 --- /dev/null +++ b/Component_Tests/Classes/Misc/Test_JidParser.cs @@ -0,0 +1,156 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using XMPP_API.Classes; + +namespace Component_Tests.Classes.Misc +{ + /// + /// Examples from: https://tools.ietf.org/html/rfc7622#section-3.5 + /// + [TestClass] + public class Test_JidParser + { + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_1() + { + string s = "juliet@example.com"; + Assert.IsTrue(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_2() + { + string s = "foo\\20bar@example.com"; + Assert.IsTrue(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_3() + { + string s = "fussball@example.com"; + Assert.IsTrue(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_4() + { + string s = "fußball@example.com"; + //Assert.IsTrue(Utils.isBareJid(s)); // TODO: Fix RFC 7622 encoding + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_5() + { + string s = "π@example.com"; + //Assert.IsTrue(Utils.isBareJid(s)); // TODO: Fix RFC 7622 encoding + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_6() + { + string s = "\"juliet\"@example.com"; + Assert.IsFalse(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_7() + { + string s = "foo bar@example.com"; + Assert.IsFalse(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsBareJid_8() + { + string s = "juliet@"; + Assert.IsFalse(Utils.isBareJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_1() + { + string s = "juliet@example.com/foo"; + Assert.IsTrue(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_2() + { + string s = "juliet@example.com/foo bar"; + Assert.IsTrue(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_3() + { + string s = "juliet@example.com/foo@bar"; + Assert.IsTrue(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_4() + { + string s = "Σ@example.com/foo"; + //Assert.IsTrue(Utils.isFullJid(s)); // TODO: Fix RFC 7622 encoding + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_5() + { + string s = "σ@example.com/foo"; + //Assert.IsTrue(Utils.isFullJid(s)); // TODO: Fix RFC 7622 encoding + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_6() + { + string s = "ς@example.com/foo"; + //Assert.IsTrue(Utils.isFullJid(s)); // TODO: Fix RFC 7622 encoding + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_7() + { + string s = "king@example.com/♚"; + Assert.IsTrue(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_8() + { + string s = "a@a.example.com/b@example.net"; + Assert.IsTrue(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_9() + { + string s = "juliet@example.com/ foo"; + Assert.IsFalse(Utils.isFullJid(s)); + } + + [TestCategory("Misc")] + [TestMethod] + public void Test_IsFullJid_10() + { + string s = "@example.com/"; + Assert.IsFalse(Utils.isFullJid(s)); + } + } +} diff --git a/Component_Tests/Component_Tests.csproj b/Component_Tests/Component_Tests.csproj index 5b4ceb406..ec80e0220 100644 --- a/Component_Tests/Component_Tests.csproj +++ b/Component_Tests/Component_Tests.csproj @@ -4,15 +4,15 @@ Debug x86 - {0C3E37F5-FF21-41B5-8C9F-492A5536471C} + {B7A8F92E-7BD9-46F5-BFC6-4EBCD8AD062F} AppContainerExe Properties Component_Tests Component_Tests en-US UAP - 10.0.17134.0 - 10.0.14393.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -29,6 +29,7 @@ false prompt true + latest bin\x86\Release\ @@ -41,6 +42,7 @@ prompt true true + latest true @@ -52,6 +54,7 @@ false prompt true + latest bin\ARM\Release\ @@ -64,6 +67,33 @@ prompt true true + latest + + + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM64 + false + prompt + true + true + latest + + + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM64 + false + prompt + true + true + latest true @@ -75,6 +105,7 @@ false prompt true + latest bin\x64\Release\ @@ -87,12 +118,12 @@ prompt true true + latest PackageReference - @@ -100,11 +131,12 @@ + - + @@ -125,6 +157,7 @@ + @@ -132,11 +165,10 @@ - - 6.2.2 + 6.2.3 1.4.0 @@ -145,20 +177,23 @@ 1.4.0 - 15.9.0 + 16.0.1 - {53BB5463-9646-4F79-BE97-3D73473AAD92} + {0c1fb090-4492-441d-b182-c999df71f917} Logging + + {c4af94da-11cf-4147-b5b7-4b2dc7c624a0} + Shared + - {899FB043-AF8B-4294-95B6-1482C79459AC} + {64b9a47f-d404-4d0b-a34a-bec280c5ff6b} XMPP_API - 14.0 diff --git a/Component_Tests/Component_Tests_TemporaryKey.pfx b/Component_Tests/Component_Tests_TemporaryKey.pfx index 128e93985..71431443e 100644 Binary files a/Component_Tests/Component_Tests_TemporaryKey.pfx and b/Component_Tests/Component_Tests_TemporaryKey.pfx differ diff --git a/Component_Tests/Package.appxmanifest b/Component_Tests/Package.appxmanifest index a52c96e7b..22f6b8685 100644 --- a/Component_Tests/Package.appxmanifest +++ b/Component_Tests/Package.appxmanifest @@ -1,23 +1,40 @@  - - - + + + + + + Component_Tests saute Assets\StoreLogo.png + + - - - - + + + diff --git a/Component_Tests/Properties/AssemblyInfo.cs b/Component_Tests/Properties/AssemblyInfo.cs index de6076aeb..179b9f922 100644 --- a/Component_Tests/Properties/AssemblyInfo.cs +++ b/Component_Tests/Properties/AssemblyInfo.cs @@ -7,7 +7,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Component_Tests")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: AssemblyMetadata("TargetPlatform","UAP")] diff --git a/Component_Tests/Properties/UnitTestApp.rd.xml b/Component_Tests/Properties/UnitTestApp.rd.xml index efee59d27..996a8392a 100644 --- a/Component_Tests/Properties/UnitTestApp.rd.xml +++ b/Component_Tests/Properties/UnitTestApp.rd.xml @@ -3,7 +3,7 @@ developers. However, you can modify these parameters to modify the behavior of the .NET Native optimizer. - Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919 + Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919 To fully enable reflection for App1.MyClass and all of its public/private members @@ -12,7 +12,7 @@ Using the Namespace directive to apply reflection policy to all the types in a particular namespace - + --> diff --git a/Component_Tests/UnitTestApp.xaml b/Component_Tests/UnitTestApp.xaml index fe9f98d17..a8bae5e17 100644 --- a/Component_Tests/UnitTestApp.xaml +++ b/Component_Tests/UnitTestApp.xaml @@ -1,6 +1,7 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:Component_Tests"> diff --git a/Component_Tests/UnitTestApp.xaml.cs b/Component_Tests/UnitTestApp.xaml.cs index 2e4504250..4cb5667e8 100644 --- a/Component_Tests/UnitTestApp.xaml.cs +++ b/Component_Tests/UnitTestApp.xaml.cs @@ -1,11 +1,18 @@ -using Logging; -using System; +using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace Component_Tests @@ -32,6 +39,7 @@ public App() /// Details about the launch request and process. protected override void OnLaunched(LaunchActivatedEventArgs e) { + #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { @@ -43,7 +51,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) // Do not repeat app initialization when the Window already has content, // just ensure that the window is active - if (rootFrame is null) + if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); diff --git a/Data_Manager2/Classes/ConnectionHandler.cs b/Data_Manager2/Classes/ConnectionHandler.cs index 2ae278268..b96c9e7e7 100644 --- a/Data_Manager2/Classes/ConnectionHandler.cs +++ b/Data_Manager2/Classes/ConnectionHandler.cs @@ -1,23 +1,27 @@ -using Data_Manager2.Classes.Events; -using Data_Manager2.Classes.DBManager; +using Data_Manager2.Classes.DBManager; using Data_Manager2.Classes.DBTables; +using Data_Manager2.Classes.Events; +using Data_Manager2.Classes.Omemo; +using Data_Manager2.Classes.Toast; using Logging; +using Shared.Classes.Collections; +using Shared.Classes.Network; using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Threading; using System.Threading.Tasks; using XMPP_API.Classes; using XMPP_API.Classes.Network; +using XMPP_API.Classes.Network.Events; using XMPP_API.Classes.Network.XML; using XMPP_API.Classes.Network.XML.Messages; using XMPP_API.Classes.Network.XML.Messages.XEP_0045; -using XMPP_API.Classes.Network.XML.Messages.XEP_0249; -using System.Threading; using XMPP_API.Classes.Network.XML.Messages.XEP_0048; using XMPP_API.Classes.Network.XML.Messages.XEP_0184; -using XMPP_API.Classes.Network.Events; +using XMPP_API.Classes.Network.XML.Messages.XEP_0249; using XMPP_API.Classes.Network.XML.Messages.XEP_0384; -using Data_Manager2.Classes.Toast; -using Data_Manager2.Classes.Omemo; +using XMPP_API.Classes.Network.XML.Messages.XEP_0384.Signal.Session; namespace Data_Manager2.Classes { @@ -27,11 +31,15 @@ public class ConnectionHandler #region --Attributes-- private static readonly SemaphoreSlim CLIENT_SEMA = new SemaphoreSlim(1); public static readonly ConnectionHandler INSTANCE = new ConnectionHandler(); - private readonly List CLIENTS; + private readonly CustomObservableCollection CLIENTS; + private readonly DownloadHandler DOWNLOAD_HANDLER = new DownloadHandler(); + public readonly ImageDownloadHandler IMAGE_DOWNLOAD_HANDLER; public delegate void ClientConnectedHandler(ConnectionHandler handler, ClientConnectedEventArgs args); + public delegate void ClientsCollectionChangedHandler(ConnectionHandler handler, NotifyCollectionChangedEventArgs args); public event ClientConnectedHandler ClientConnected; + public event ClientsCollectionChangedHandler ClientsCollectionChanged; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- @@ -43,7 +51,10 @@ public class ConnectionHandler /// public ConnectionHandler() { - this.CLIENTS = new List(); + this.IMAGE_DOWNLOAD_HANDLER = new ImageDownloadHandler(DOWNLOAD_HANDLER); + Task.Run(async () => await this.IMAGE_DOWNLOAD_HANDLER.ContinueDownloadsAsync()); + this.CLIENTS = new CustomObservableCollection(false); + this.CLIENTS.CollectionChanged += CLIENTS_CollectionChanged; loadClients(); AccountDBManager.INSTANCE.AccountChanged += INSTANCE_AccountChanged; } @@ -59,7 +70,7 @@ public XMPPClient getClient(string iDAndDomain) { foreach (XMPPClient c in CLIENTS) { - if (c.getXMPPAccount().getIdAndDomain().Equals(iDAndDomain)) + if (c.getXMPPAccount().getBareJid().Equals(iDAndDomain)) { return c; } @@ -70,7 +81,7 @@ public XMPPClient getClient(string iDAndDomain) /// /// Returns all available XMPPClients. /// - public List getClients() + public CustomObservableCollection getClients() { return CLIENTS; } @@ -144,7 +155,7 @@ public async Task removeAccountAsync(string accountId) CLIENT_SEMA.Wait(); for (int i = 0; i < CLIENTS.Count; i++) { - if (Equals(CLIENTS[i].getXMPPAccount().getIdAndDomain(), accountId)) + if (Equals(CLIENTS[i].getXMPPAccount().getBareJid(), accountId)) { await CLIENTS[i].disconnectAsync(); CLIENTS.RemoveAt(i); @@ -246,7 +257,7 @@ private void unsubscribeFromEvents(XMPPClient c) /// The Client which entered the state. private void onClientDisconnectedOrError(XMPPClient client) { - ChatDBManager.INSTANCE.resetPresence(client.getXMPPAccount().getIdAndDomain()); + ChatDBManager.INSTANCE.resetPresence(client.getXMPPAccount().getBareJid()); MUCHandler.INSTANCE.onClientDisconnected(client); } @@ -309,7 +320,7 @@ private void setOmemoChatMessagesSendFailed(IList messages, #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private void C_ConnectionStateChanged(XMPPClient client, XMPP_API.Classes.Network.Events.ConnectionStateChangedEventArgs args) + private void C_ConnectionStateChanged(XMPPClient client, ConnectionStateChangedEventArgs args) { switch (args.newState) { @@ -337,7 +348,7 @@ private async Task answerPresenceProbeAsync(string from, string to, ChatTable ch PresenceMessage answer = null; if (chat is null) { - answer = new PresenceErrorMessage(account.getIdDomainAndResource(), from, PresenceErrorType.FORBIDDEN); + answer = new PresenceErrorMessage(account.getFullJid(), from, PresenceErrorType.FORBIDDEN); Logger.Warn("Received a presence probe message for an unknown chat from: " + from + ", to: " + to); return; } @@ -347,18 +358,18 @@ private async Task answerPresenceProbeAsync(string from, string to, ChatTable ch { case "both": case "from": - answer = new PresenceMessage(account.getIdAndDomain(), from, account.presence, account.status, account.presencePriorety); + answer = new PresenceMessage(account.getBareJid(), from, account.presence, account.status, account.presencePriorety); Logger.Debug("Answered presence probe from: " + from); break; case "none" when chat.inRoster: case "to" when chat.inRoster: - answer = new PresenceErrorMessage(account.getIdDomainAndResource(), from, PresenceErrorType.FORBIDDEN); + answer = new PresenceErrorMessage(account.getFullJid(), from, PresenceErrorType.FORBIDDEN); Logger.Warn("Received a presence probe but chat has no subscription: " + from + ", to: " + to + " subscription: " + chat.subscription); break; default: - answer = new PresenceErrorMessage(account.getIdDomainAndResource(), from, PresenceErrorType.NOT_AUTHORIZED); + answer = new PresenceErrorMessage(account.getFullJid(), from, PresenceErrorType.NOT_AUTHORIZED); Logger.Warn("Received a presence probe but chat has no subscription: " + from + ", to: " + to + " subscription: " + chat.subscription); break; } @@ -371,12 +382,12 @@ private async void C_NewPresence(XMPPClient client, XMPP_API.Classes.Events.NewP string from = Utils.getBareJidFromFullJid(args.PRESENCE_MESSAGE.getFrom()); // If received a presence message from your own account, ignore it: - if (string.Equals(from, client.getXMPPAccount().getIdAndDomain())) + if (string.Equals(from, client.getXMPPAccount().getBareJid())) { return; } - string to = client.getXMPPAccount().getIdAndDomain(); + string to = client.getXMPPAccount().getBareJid(); string id = ChatTable.generateId(from, to); ChatTable chat = ChatDBManager.INSTANCE.getChat(id); switch (args.PRESENCE_MESSAGE.TYPE) @@ -415,12 +426,12 @@ private void C_NewRoosterMessage(IMessageSender sender, NewValidMessageEventArgs { if (args.MESSAGE is RosterResultMessage msg && sender is XMPPClient client) { - string to = client.getXMPPAccount().getIdAndDomain(); + string to = client.getXMPPAccount().getBareJid(); string type = msg.TYPE; if (string.Equals(type, IQMessage.RESULT)) { - ChatDBManager.INSTANCE.setAllNotInRoster(client.getXMPPAccount().getIdAndDomain()); + ChatDBManager.INSTANCE.setAllNotInRoster(client.getXMPPAccount().getBareJid()); } else if (!string.Equals(type, IQMessage.SET)) { @@ -488,17 +499,6 @@ private void C_NewChatMessage(XMPPClient client, XMPP_API.Classes.Network.Events } string from = Utils.getBareJidFromFullJid(msg.getFrom()); - - // Check if device id is valid and if, decrypt the OMEMO messages: - if (msg is OmemoMessageMessage omemoMessage) - { - // Decryption failed: - if (!omemoMessage.decrypt(client.getOmemoHelper(), client.getXMPPAccount().omemoDeviceId)) - { - return; - } - } - string to = Utils.getBareJidFromFullJid(msg.getTo()); string id; if (msg.CC_TYPE == CarbonCopyType.SENT) @@ -510,9 +510,44 @@ private void C_NewChatMessage(XMPPClient client, XMPP_API.Classes.Network.Events id = ChatTable.generateId(from, to); } + // Check if device id is valid and if, decrypt the OMEMO messages: + if (msg is OmemoMessageMessage omemoMessage) + { + OmemoHelper helper = client.getOmemoHelper(); + if (helper is null) + { + C_OmemoSessionBuildError(client, new XMPP_API.Classes.Events.OmemoSessionBuildErrorEventArgs(id, OmemoSessionBuildError.KEY_ERROR, new List { omemoMessage })); + Logger.Error("Failed to decrypt OMEMO message - OmemoHelper is null"); + return; + } + else if (!client.getXMPPAccount().checkOmemoKeys()) + { + C_OmemoSessionBuildError(client, new XMPP_API.Classes.Events.OmemoSessionBuildErrorEventArgs(id, OmemoSessionBuildError.KEY_ERROR, new List { omemoMessage })); + Logger.Error("Failed to decrypt OMEMO message - keys are corrupted"); + return; + } + else if (!omemoMessage.decrypt(client.getOmemoHelper(), client.getXMPPAccount().omemoDeviceId)) + { + return; + } + } ChatTable chat = ChatDBManager.INSTANCE.getChat(id); bool chatChanged = false; + + // Spam detection: + if (Settings.getSettingBoolean(SettingsConsts.SPAM_DETECTION_ENABLED)) + { + if (Settings.getSettingBoolean(SettingsConsts.SPAM_DETECTION_FOR_ALL_CHAT_MESSAGES) || chat is null) + { + if (SpamDBManager.INSTANCE.isSpam(msg.MESSAGE)) + { + Logger.Warn("Received spam message from " + from); + return; + } + } + } + if (chat is null) { chatChanged = true; @@ -589,7 +624,7 @@ private void C_NewChatMessage(XMPPClient client, XMPP_API.Classes.Network.Events { Task.Run(async () => { - DeliveryReceiptMessage receiptMessage = new DeliveryReceiptMessage(client.getXMPPAccount().getIdDomainAndResource(), from, msg.ID); + DeliveryReceiptMessage receiptMessage = new DeliveryReceiptMessage(client.getXMPPAccount().getFullJid(), from, msg.ID); await client.sendAsync(receiptMessage, true); }); } @@ -641,7 +676,7 @@ private async void INSTANCE_AccountChanged(AccountDBManager handler, AccountChan CLIENT_SEMA.Wait(); for (int i = 0; i < CLIENTS.Count; i++) { - if (Equals(CLIENTS[i].getXMPPAccount().getIdAndDomain(), args.ACCOUNT.getIdAndDomain())) + if (Equals(CLIENTS[i].getXMPPAccount().getBareJid(), args.ACCOUNT.getBareJid())) { // Disconnect first: await CLIENTS[i].disconnectAsync(); @@ -687,7 +722,7 @@ private void C_NewBookmarksResultMessage(XMPPClient client, NewBookmarksResultMe foreach (ConferenceItem c in args.BOOKMARKS_MESSAGE.STORAGE.CONFERENCES) { bool newMUC = false; - string to = client.getXMPPAccount().getIdAndDomain(); + string to = client.getXMPPAccount().getBareJid(); string from = c.jid; string id = ChatTable.generateId(from, to); @@ -756,7 +791,7 @@ private void C_OmemoSessionBuildError(XMPPClient client, XMPP_API.Classes.Events { Task.Run(() => { - ChatTable chat = ChatDBManager.INSTANCE.getChat(ChatTable.generateId(args.CHAT_JID, client.getXMPPAccount().getIdAndDomain())); + ChatTable chat = ChatDBManager.INSTANCE.getChat(ChatTable.generateId(args.CHAT_JID, client.getXMPPAccount().getBareJid())); if (!(chat is null)) { // Add an error chat message: @@ -778,6 +813,11 @@ private void C_OmemoSessionBuildError(XMPPClient client, XMPP_API.Classes.Events } }); } + + private void CLIENTS_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + ClientsCollectionChanged?.Invoke(this, e); + } #endregion } } diff --git a/Data_Manager2/Classes/DBManager/AccountDBManager.cs b/Data_Manager2/Classes/DBManager/AccountDBManager.cs index 737c0c3bd..37ab6f3f1 100644 --- a/Data_Manager2/Classes/DBManager/AccountDBManager.cs +++ b/Data_Manager2/Classes/DBManager/AccountDBManager.cs @@ -1,12 +1,12 @@ -using Data_Manager2.Classes.Events; +using Data_Manager2.Classes.DBManager.Omemo; using Data_Manager2.Classes.DBTables; -using System.Collections.Generic; -using XMPP_API.Classes.Network; -using Windows.Security.Cryptography.Certificates; +using Data_Manager2.Classes.Events; using Logging; +using Shared.Classes.SQLite; +using System.Collections.Generic; using System.Threading; -using Thread_Save_Components.Classes.SQLite; -using Data_Manager2.Classes.DBManager.Omemo; +using Windows.Security.Cryptography.Certificates; +using XMPP_API.Classes.Network; namespace Data_Manager2.Classes.DBManager { @@ -44,26 +44,26 @@ public AccountDBManager() /// The account which should get inserted or replaced. public void setAccount(XMPPAccount account, bool triggerAccountChanged) { - update(new AccountTable(account)); + dB.InsertOrReplace(new AccountTable(account)); Vault.storePassword(account); saveAccountConnectionConfiguration(account); - if (account.omemoPreKeys != null) + if (account.OMEMO_PRE_KEYS != null) { - OmemoSignalKeyDBManager.INSTANCE.setPreKeys(account.omemoPreKeys, account.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setPreKeys(account.OMEMO_PRE_KEYS, account.getBareJid()); } else { - OmemoSignalKeyDBManager.INSTANCE.deletePreKeys(account.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deletePreKeys(account.getBareJid()); } if (account.omemoSignedPreKeyPair != null) { - OmemoSignalKeyDBManager.INSTANCE.setSignedPreKey(account.omemoSignedPreKeyId, account.omemoSignedPreKeyPair, account.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setSignedPreKey(account.omemoSignedPreKeyId, account.omemoSignedPreKeyPair, account.getBareJid()); } else { - OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(account.omemoSignedPreKeyId, account.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(account.omemoSignedPreKeyId, account.getBareJid()); } if (triggerAccountChanged) @@ -78,7 +78,7 @@ public void setAccount(XMPPAccount account, bool triggerAccountChanged) /// The XMPPAccount with updated disabled property. public void setAccountDisabled(XMPPAccount account) { - dB.Execute("UPDATE " + DBTableConsts.ACCOUNT_TABLE + " SET disabled = ? WHERE id = ?;", account.disabled, account.getIdAndDomain()); + dB.Execute("UPDATE " + DBTableConsts.ACCOUNT_TABLE + " SET disabled = ? WHERE id = ?;", account.disabled, account.getBareJid()); AccountChanged?.Invoke(this, new AccountChangedEventArgs(account, false)); } @@ -134,18 +134,18 @@ public int getAccountCount() /// Whether to delete all OMEMO keys. public void deleteAccount(XMPPAccount account, bool triggerAccountChanged, bool deleteAllKeys) { - dB.Execute("DELETE FROM " + DBTableConsts.ACCOUNT_TABLE + " WHERE id = ?;", account.getIdAndDomain()); - dB.Execute("DELETE FROM " + DBTableConsts.IGNORED_CERTIFICATE_ERROR_TABLE + " WHERE accountId = ?;", account.getIdAndDomain()); - dB.Execute("DELETE FROM " + DBTableConsts.CONNECTION_OPTIONS_TABLE + " WHERE accountId = ?;", account.getIdAndDomain()); + dB.Execute("DELETE FROM " + DBTableConsts.ACCOUNT_TABLE + " WHERE id = ?;", account.getBareJid()); + dB.Execute("DELETE FROM " + DBTableConsts.IGNORED_CERTIFICATE_ERROR_TABLE + " WHERE accountId = ?;", account.getBareJid()); + dB.Execute("DELETE FROM " + DBTableConsts.CONNECTION_OPTIONS_TABLE + " WHERE accountId = ?;", account.getBareJid()); if (deleteAllKeys) { - OmemoDeviceDBManager.INSTANCE.deleteAllForAccount(account.getIdAndDomain()); - OmemoSignalKeyDBManager.INSTANCE.deleteAllForAccount(account.getIdAndDomain()); + OmemoDeviceDBManager.INSTANCE.deleteAllForAccount(account.getBareJid()); + OmemoSignalKeyDBManager.INSTANCE.deleteAllForAccount(account.getBareJid()); } else { - OmemoSignalKeyDBManager.INSTANCE.deletePreKeys(account.getIdAndDomain()); - OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(account.omemoSignedPreKeyId, account.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deletePreKeys(account.getBareJid()); + OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(account.omemoSignedPreKeyId, account.getBareJid()); } Vault.deletePassword(account); @@ -208,14 +208,14 @@ public void replaceAccount(XMPPAccount oldAccount, XMPPAccount account) public void loadAccountConnectionConfiguration(XMPPAccount account) { // Load general options: - ConnectionOptionsTable optionsTable = getConnectionOptionsTable(account.getIdAndDomain()); + ConnectionOptionsTable optionsTable = getConnectionOptionsTable(account.getBareJid()); if (optionsTable != null) { optionsTable.toConnectionConfiguration(account.connectionConfiguration); } // Load ignored certificate errors: - IList ignoredCertificates = getIgnoredCertificateErrorTables(account.getIdAndDomain()); + IList ignoredCertificates = getIgnoredCertificateErrorTables(account.getBareJid()); if (ignoredCertificates != null) { foreach (IgnoredCertificateErrorTable i in ignoredCertificates) @@ -234,19 +234,19 @@ public void saveAccountConnectionConfiguration(XMPPAccount account) // Save general options: ConnectionOptionsTable optionsTable = new ConnectionOptionsTable(account.connectionConfiguration) { - accountId = account.getIdAndDomain() + accountId = account.getBareJid() }; - update(optionsTable); + dB.InsertOrReplace(optionsTable); // Save ignored certificate errors: - dB.Execute("DELETE FROM " + DBTableConsts.IGNORED_CERTIFICATE_ERROR_TABLE + " WHERE accountId = ?;", account.getIdAndDomain()); + dB.Execute("DELETE FROM " + DBTableConsts.IGNORED_CERTIFICATE_ERROR_TABLE + " WHERE accountId = ?;", account.getBareJid()); foreach (ChainValidationResult i in account.connectionConfiguration.IGNORED_CERTIFICATE_ERRORS) { - update(new IgnoredCertificateErrorTable() + dB.InsertOrReplace(new IgnoredCertificateErrorTable() { - accountId = account.getIdAndDomain(), + accountId = account.getBareJid(), certificateError = i, - id = IgnoredCertificateErrorTable.generateId(account.getIdAndDomain(), i) + id = IgnoredCertificateErrorTable.generateId(account.getBareJid(), i) }); } } diff --git a/Data_Manager2/Classes/DBManager/ChatDBManager.cs b/Data_Manager2/Classes/DBManager/ChatDBManager.cs index 2793cca59..d81835daf 100644 --- a/Data_Manager2/Classes/DBManager/ChatDBManager.cs +++ b/Data_Manager2/Classes/DBManager/ChatDBManager.cs @@ -1,10 +1,11 @@ -using Data_Manager2.Classes.Events; -using Data_Manager2.Classes.DBTables; +using Data_Manager2.Classes.DBTables; +using Data_Manager2.Classes.Events; +using Shared.Classes.SQLite; using SQLite; using System.Collections.Generic; using System.Threading.Tasks; using XMPP_API.Classes; -using Thread_Save_Components.Classes.SQLite; +using XMPP_API.Classes.Network.XML.Messages.XEP_0249; namespace Data_Manager2.Classes.DBManager { @@ -47,7 +48,7 @@ public void setMessageAsDeliverd(string id, bool triggerMessageChanged) ChatMessageTable msg = getChatMessageById(id); if (msg != null) { - ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg)); + ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg, false)); } } } @@ -82,7 +83,7 @@ public ChatTable getChat(string chatId) public void setMUCDirectInvitation(MUCDirectInvitationTable invite) { - update(invite); + dB.InsertOrReplace(invite); } public void setMUCDirectInvitationState(string chatMessageId, MUCDirectInvitationState state) @@ -130,7 +131,7 @@ public void setChat(ChatTable chat, bool delete, bool triggerChatChanged) } else { - update(chat); + dB.InsertOrReplace(chat); } if (triggerChatChanged) @@ -154,7 +155,7 @@ public void setAllNotInRoster(string userAccountId) Parallel.ForEach(getAllChatsForClient(userAccountId), (c) => { c.inRoster = false; - update(c); + dB.InsertOrReplace(c); onChatChanged(c, false); }); } @@ -196,13 +197,26 @@ public void updateChatMessageState(string msgId, MessageState state) List list = dB.Query(true, "SELECT * FROM " + DBTableConsts.CHAT_MESSAGE_TABLE + " WHERE id = ?;", msgId); Parallel.ForEach(list, (msg) => { - ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg)); + ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg, false)); }); } - public void deleteAllChatMessagesForChat(string chatId) + public async Task deleteAllChatMessagesForChatAsync(string chatId) { - dB.Execute("DELETE FROM " + DBTableConsts.CHAT_MESSAGE_TABLE + " WHERE chatId = ?;", chatId); + List list = dB.Query(true, "SELECT * FROM " + DBTableConsts.CHAT_MESSAGE_TABLE + " WHERE chatId = ?;", chatId); + foreach (ChatMessageTable msg in list) + { + await deleteChatMessageAsync(msg, false); + } + } + + public async Task deleteAllChatMessagesForAccountAsync(string userAccountId) + { + List list = dB.Query(true, "SELECT m.* FROM " + DBTableConsts.CHAT_MESSAGE_TABLE + " m JOIN " + DBTableConsts.CHAT_TABLE + " c ON m.chatId = c.id WHERE c.userAccountId = ?;", userAccountId); + foreach (ChatMessageTable msg in list) + { + await deleteChatMessageAsync(msg, false); + } } public void deleteAllChatsForAccount(string userAccountId) @@ -231,28 +245,52 @@ public void markMessageAsRead(string id) public void markMessageAsRead(ChatMessageTable msg) { msg.state = MessageState.READ; - update(msg); + dB.InsertOrReplace(msg); msg.onChanged(); - ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg)); + ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(msg, false)); } public void setChatMessage(ChatMessageTable message, bool triggerNewChatMessage, bool triggerMessageChanged) { - update(message); + dB.InsertOrReplace(message); if (triggerNewChatMessage) { NewChatMessage?.Invoke(this, new NewChatMessageEventArgs(message)); - if (message.isImage) + if (message.isImage && !Settings.getSettingBoolean(SettingsConsts.DISABLE_IMAGE_AUTO_DOWNLOAD)) { cacheImage(message); } } if (triggerMessageChanged) { - ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(message)); + ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(message, false)); + } + } + + public async Task deleteChatMessageAsync(ChatMessageTable message, bool triggerMessageChanged) + { + if (message.isImage) + { + await ImageDBManager.INSTANCE.deleteImageAsync(message); + } + + if (string.Equals(message.type, DirectMUCInvitationMessage.TYPE_MUC_DIRECT_INVITATION)) + { + deleteMucDirectInvite(message); + } + + dB.Delete(message); + if (triggerMessageChanged) + { + ChatMessageChanged?.Invoke(this, new ChatMessageChangedEventArgs(message, true)); } } + public void deleteMucDirectInvite(ChatMessageTable message) + { + dB.Execute("DELETE FROM " + DBTableConsts.MUC_DIRECT_INVITATION_TABLE + " WHERE chatMessageId = ?;", message.id); + } + public void resetPresence(string userAccountId) { foreach (ChatTable c in getAllChatsForClient(userAccountId)) @@ -260,7 +298,7 @@ public void resetPresence(string userAccountId) if (c.chatType == ChatType.CHAT) { c.presence = Presence.Unavailable; - update(c); + dB.InsertOrReplace(c); onChatChanged(c, false); } } @@ -284,7 +322,10 @@ private void onChatChanged(string chatId) private void cacheImage(ChatMessageTable msg) { - ImageDBManager.INSTANCE.downloadImage(msg); + if (!Settings.getSettingBoolean(SettingsConsts.DISABLE_IMAGE_AUTO_DOWNLOAD)) + { + Task.Run(async () => await ConnectionHandler.INSTANCE.IMAGE_DOWNLOAD_HANDLER.DownloadImageAsync(msg)); + } } private void resetPresences() diff --git a/Data_Manager2/Classes/DBManager/DiscoDBManager.cs b/Data_Manager2/Classes/DBManager/DiscoDBManager.cs index d6b7843a6..5182fa88b 100644 --- a/Data_Manager2/Classes/DBManager/DiscoDBManager.cs +++ b/Data_Manager2/Classes/DBManager/DiscoDBManager.cs @@ -1,7 +1,7 @@ using Data_Manager2.Classes.DBTables; +using Shared.Classes.SQLite; using System; using System.Collections.Generic; -using Thread_Save_Components.Classes.SQLite; using XMPP_API.Classes; using XMPP_API.Classes.Network.XML.Messages; using XMPP_API.Classes.Network.XML.Messages.XEP_0030; @@ -55,7 +55,7 @@ private void addIdentities(List identities, string from) { if (from != null && i.TYPE != null && i.CATEGORY != null) { - update(new DiscoIdentityTable() + dB.InsertOrReplace(new DiscoIdentityTable() { id = DiscoIdentityTable.generateId(from, i.TYPE), fromServer = from, @@ -77,7 +77,7 @@ private void addFeatures(List features, string from) { if (from != null && f.VAR != null) { - update(new DiscoFeatureTable + dB.InsertOrReplace(new DiscoFeatureTable { id = DiscoFeatureTable.generateId(from, f.VAR), fromServer = from, @@ -97,7 +97,7 @@ private void addItems(List items, string from, XMPPClient client, boo { if (from != null && i.JID != null) { - update(new DiscoItemTable() + dB.InsertOrReplace(new DiscoItemTable() { id = DiscoItemTable.generateId(from, i.JID), fromServer = from, diff --git a/Data_Manager2/Classes/DBManager/ImageDBManager.cs b/Data_Manager2/Classes/DBManager/ImageDBManager.cs index f48e8699f..61aa6d812 100644 --- a/Data_Manager2/Classes/DBManager/ImageDBManager.cs +++ b/Data_Manager2/Classes/DBManager/ImageDBManager.cs @@ -1,14 +1,12 @@ using Data_Manager2.Classes.DBTables; using Logging; +using Shared.Classes.Network; +using Shared.Classes.SQLite; using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; using System.Threading.Tasks; -using Thread_Save_Components.Classes.SQLite; using Windows.Storage; -using Windows.Storage.Search; namespace Data_Manager2.Classes.DBManager { @@ -18,56 +16,25 @@ public class ImageDBManager : AbstractDBManager #region --Attributes-- public static readonly ImageDBManager INSTANCE = new ImageDBManager(); - // The interval for how often the ImageTable onDownloadProgressChanged() should get triggered (e.g 0.1 = every 10%): - private const double DOWNLOAD_PROGRESS_REPORT_INTERVAL = 0.05; - - // A list of all currently downloading images: - private readonly List DOWNLOADING; - #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 15/12/2017 Created [Fabian Sauter] - /// - public ImageDBManager() - { - DOWNLOADING = new List(); - contiuneAllDownloads(); - } + #endregion //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ #region --Set-, Get- Methods-- /// - /// Calculates the size of the "cachedImages" folder. + /// /// - /// Returns the "cachedImages" folder size in KB. - public async Task getCachedImagesFolderSizeAsync() - { - StorageFolder f = await getCachedImagesFolderAsync(); - StorageFileQueryResult result = f.CreateFileQuery(CommonFileQuery.OrderByName); - - var fileSizeTasks = (await result.GetFilesAsync()).Select(async file => (await file.GetBasicPropertiesAsync()).Size); - var sizes = await Task.WhenAll(fileSizeTasks); - - return sizes.Sum(l => (long)l) / 1024; - } - - /// - /// Returns the image container from a given ChatMessageTable. - /// - /// The ChatMessageTable. - /// The corresponding image container. - public ImageTable getImageForMessage(ChatMessageTable msg) + /// + /// + public async Task getImageAsync(ChatMessageTable msg) { - ImageTable img = DOWNLOADING.Find(x => string.Equals(x.messageId, msg.id)); + ImageTable img = (ImageTable)await ConnectionHandler.INSTANCE.IMAGE_DOWNLOAD_HANDLER.FindAsync((x) => { return x is ImageTable imageTable && string.Equals(imageTable.messageId, msg.id); }); if (img is null) { - List list = dB.Query(true, "SELECT * FROM " + DBTableConsts.IMAGE_TABLE + " WHERE messageId = ?;", msg.id); + List list = dB.Query(true, "SELECT * FROM " + DBTableConsts.IMAGE_TABLE + " WHERE " + nameof(ImageTable.messageId) + " = ?;", msg.id); if (list.Count > 0) { img = list[0]; @@ -76,309 +43,57 @@ public ImageTable getImageForMessage(ChatMessageTable msg) return img; } - /// - /// Checks if the image is already available locally, if yes returns the path to it. - /// Else tries to download the image and stores it. Also returns the path of the downloaded image. - /// - /// The image container. - /// The image url. - /// Returns the local path to the downloaded image. - public async Task getImagePathAsync(ImageTable img, string url) - { - try - { - string fileName = createUniqueFileName(url); - string path = await getLocalImageAsync(fileName); - if (path is null) - { - path = await downloadImageAsync(img, url, fileName); - } - return path; - } - catch (Exception e) - { - Logger.Error("Error during downloading image: " + e.Message); - img.errorMessage = e.Message; - update(img); - return null; - } - } - - /// - /// Returns the path for the given file name if the file exits. - /// Else returns null. - /// - /// The file name. - /// The path to the file if it exists else null. - private async Task getLocalImageAsync(string name) - { - StorageFolder f = await getCachedImagesFolderAsync(); - if (f != null) - { - FileInfo fI = new FileInfo(f.Path + '\\' + name); - if (fI.Exists) - { - return fI.FullName; - } - } - return null; - } - - private async Task getCachedImagesFolderAsync() + public void setImage(ImageTable image) { - if (Settings.getSettingBoolean(SettingsConsts.DISABLE_DOWNLOAD_IMAGES_TO_LIBARY)) - { - return await ApplicationData.Current.LocalFolder.CreateFolderAsync("cachedImages", CreationCollisionOption.OpenIfExists); - } - return await KnownFolders.PicturesLibrary.CreateFolderAsync("UWPX", CreationCollisionOption.OpenIfExists); + dB.InsertOrReplace(image); } - /// - /// Continues all outstanding downloads. - /// - private void contiuneAllDownloads() + public List getAllUndownloadedImages() { - List list = dB.Query(true, "SELECT * FROM " + DBTableConsts.IMAGE_TABLE + " WHERE state = 0 OR state = 1;"); - foreach (ImageTable img in list) - { - Task.Run(async () => - { - // Reset image progress: - img.progress = 0; - - ChatMessageTable msg = ChatDBManager.INSTANCE.getChatMessageById(img.messageId); - await downloadImageAsync(img, msg.message); - }); - } + return dB.Query(true, "SELECT * FROM " + DBTableConsts.IMAGE_TABLE + " WHERE " + nameof(ImageTable.State) + " != ? AND " + nameof(ImageTable.State) + " != ?;", (int)DownloadState.DONE, (int)DownloadState.ERROR); } - /// - /// Downloads the image from the given message and stores it locally. - /// - /// The image container. - /// The image url. - /// - private async Task downloadImageAsync(ImageTable img, string msg) + public async Task deleteImageAsync(ChatMessageTable msg) { - DOWNLOADING.Add(img); - - updateImageState(img, DownloadState.DOWNLOADING); - string path = await getImagePathAsync(img, msg); - - img.path = path; - if (path is null) - { - updateImageState(img, DownloadState.ERROR); - } - else + ImageTable image = await getImageAsync(msg); + if (!(image is null)) { - updateImageState(img, DownloadState.DONE); - } - } + // Cancel download: + ConnectionHandler.INSTANCE.IMAGE_DOWNLOAD_HANDLER.CancelDownload(image); - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - /// - /// Retries to download the image from the given ChatMessageTable. - /// - /// The ChatMessageTable containing the image url. - /// The image container. - public ImageTable retryImageDownload(ChatMessageTable msg) - { - if (msg.isImage) - { - ImageTable img = getImageForMessage(msg); - if (img is null) + // Try to delete local file: + try { - img = new ImageTable() + if (!string.IsNullOrEmpty(image.TargetFolderPath) && !string.IsNullOrEmpty(image.TargetFileName)) { - messageId = msg.id, - path = null, - state = DownloadState.WAITING, - progress = 0 - }; + string path = Path.Combine(image.TargetFolderPath, image.TargetFileName); + StorageFile file = await StorageFile.GetFileFromPathAsync(path); + if (!(file is null)) + { + await file.DeleteAsync(); + } + } + Logger.Info("Deleted: " + image.TargetFileName); } - else + catch (Exception e) { - img.state = DownloadState.WAITING; - img.progress = 0; - img.path = null; + Logger.Error("Failed to delete image: " + image.TargetFileName, e); } - Task.Run(async () => - { - update(img); - await downloadImageAsync(img, msg.message); - }); - - return img; - } - return null; - } - - /// - /// Deletes the "cachedImages" folder and creates a new empty one. - /// - public async Task deleteImageCacheAsync() - { - StorageFolder folder = await getCachedImagesFolderAsync(); - if (folder != null) - { - await folder.DeleteAsync(); + // Delete DB entry: + dB.Delete(image); } - await getCachedImagesFolderAsync(); - Logger.Info("Deleted image cache!"); } - /// - /// Opens the current folder containing the cached images. - /// - public async Task openCachedImagesFolderAsync() - { - StorageFolder folder = await getCachedImagesFolderAsync(); - await Windows.System.Launcher.LaunchFolderAsync(folder); - } + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- - /// - /// Creates a new task, downloads the image from the given message and stores it locally. - /// - /// The ChatMessageTable containing the image url. - public void downloadImage(ChatMessageTable msg) - { - if (msg.isImage) - { - Task.Run(async () => - { - ImageTable img = new ImageTable() - { - messageId = msg.id, - path = null, - state = DownloadState.WAITING, - progress = 0 - }; - update(img); - await downloadImageAsync(img, msg.message); - }); - } - } #endregion #region --Misc Methods (Private)-- - /// - /// Tries to download the image from the given url. - /// - /// The image url. - /// - /// Returns null if it fails, else the local path. - private async Task downloadImageAsync(ImageTable img, string url, string name) - { - Logger.Info("Started downloading image <" + name + "> from: " + url); - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); - long bytesReadTotal = 0; - double lastProgressUpdatePercent = 0; - - // Check that the remote file was found. The ContentType - // check is performed since a request for a non-existent - // image file might be redirected to a 404-page, which would - // yield the StatusCode "OK", even though the image was not - // found. - if (response.StatusCode == HttpStatusCode.OK || - response.StatusCode == HttpStatusCode.Moved || - response.StatusCode == HttpStatusCode.Redirect) - { - // if the remote file was found, download it - StorageFile f = await createImageStorageFileAsync(name); - using (Stream inputStream = response.GetResponseStream()) - using (Stream outputStream = await f.OpenStreamForWriteAsync()) - { - byte[] buffer = new byte[4096]; - int bytesRead; - do - { - bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length); - await outputStream.WriteAsync(buffer, 0, bytesRead); - // Update progress: - bytesReadTotal += bytesRead; - lastProgressUpdatePercent = updateProgress(img, response.ContentLength, bytesReadTotal, lastProgressUpdatePercent); - - } while (bytesRead != 0); - } - Logger.Info("Finished downloading image <" + name + "> from: " + url); - return f.Path; - } - else - { - img.errorMessage = "Status code check failed: " + response.StatusCode + " (" + response.StatusDescription + ')'; - update(img); - } - Logger.Error("Unable to download image <" + name + "> from: " + url + " Status code: " + response.StatusCode); - return null; - } - - /// - /// Updates the progress of the given ImageTable and triggers if necessary the onDownloadProgressChanged event. - /// - /// The image container. - /// The total length of the download in bytes. - /// How many bytes got read already? - /// When was the last onDownloadProgressChanged event triggered? - /// Returns the last progress update percentage. - private double updateProgress(ImageTable img, long totalLengt, long bytesReadTotal, double lastProgressUpdatePercent) - { - img.progress = ((double)bytesReadTotal) / ((double)totalLengt); - if ((img.progress - lastProgressUpdatePercent) >= DOWNLOAD_PROGRESS_REPORT_INTERVAL) - { - img.onDownloadProgressChanged(); - return img.progress; - } - - return lastProgressUpdatePercent; - } - - private async Task createImageStorageFileAsync(string name) - { - StorageFolder f = await getCachedImagesFolderAsync(); - return await f.CreateFileAsync(name, CreationCollisionOption.ReplaceExisting); - } - - /// - /// Creates an unique file name, bases on the given url and the current time. - /// - /// The url of the image. - /// Returns an unique file name. - private string createUniqueFileName(string url) - { - string name = DateTime.Now.ToString("dd.MM.yyyy_HH.mm.ss.ffff"); - int index = url.LastIndexOf('.'); - string ending = url.Substring(index, url.Length - index); - return name + ending; - } - - /// - /// Updates the DownloadState of the given ImageTable and triggers the onStateChanged() event. - /// - /// The ImageTable, that should get updated. - /// The new DownloadState. - private void updateImageState(ImageTable img, DownloadState state) - { - img.state = state; - img.onStateChanged(); - update(img); - switch (state) - { - case DownloadState.DONE: - case DownloadState.ERROR: - DOWNLOADING.Remove(img); - break; - - default: - break; - } - } #endregion diff --git a/Data_Manager2/Classes/DBManager/MUCDBManager.cs b/Data_Manager2/Classes/DBManager/MUCDBManager.cs index c52cab3f2..5e9c8d4ba 100644 --- a/Data_Manager2/Classes/DBManager/MUCDBManager.cs +++ b/Data_Manager2/Classes/DBManager/MUCDBManager.cs @@ -1,8 +1,8 @@ -using Data_Manager2.Classes.Events; -using Data_Manager2.Classes.DBTables; +using Data_Manager2.Classes.DBTables; +using Data_Manager2.Classes.Events; +using Shared.Classes.SQLite; using System.Collections.Generic; using XMPP_API.Classes.Network.XML.Messages.XEP_0048; -using Thread_Save_Components.Classes.SQLite; namespace Data_Manager2.Classes.DBManager { @@ -74,7 +74,7 @@ public void setMUCOccupant(MUCOccupantTable occupant, bool delete, bool triggerM } else { - update(occupant); + dB.InsertOrReplace(occupant); if (triggerMUCOccupantChanged) { @@ -122,7 +122,7 @@ public void setMUCChatInfo(MUCChatInfoTable info, bool delete, bool triggerMUCCh } else { - update(info); + dB.InsertOrReplace(info); } if (triggerMUCChanged) diff --git a/Data_Manager2/Classes/DBManager/Omemo/OmemoDeviceDBManager.cs b/Data_Manager2/Classes/DBManager/Omemo/OmemoDeviceDBManager.cs index 400777a1c..65ffd19c4 100644 --- a/Data_Manager2/Classes/DBManager/Omemo/OmemoDeviceDBManager.cs +++ b/Data_Manager2/Classes/DBManager/Omemo/OmemoDeviceDBManager.cs @@ -1,9 +1,9 @@ using Data_Manager2.Classes.DBTables; using Data_Manager2.Classes.DBTables.Omemo; using libsignal; +using Shared.Classes.SQLite; using System; using System.Collections.Generic; -using Thread_Save_Components.Classes.SQLite; using XMPP_API.Classes.Network.XML.Messages.XEP_0384; namespace Data_Manager2.Classes.DBManager.Omemo diff --git a/Data_Manager2/Classes/DBManager/Omemo/OmemoSignalKeyDBManager.cs b/Data_Manager2/Classes/DBManager/Omemo/OmemoSignalKeyDBManager.cs index 4a35c6724..04a654dbc 100644 --- a/Data_Manager2/Classes/DBManager/Omemo/OmemoSignalKeyDBManager.cs +++ b/Data_Manager2/Classes/DBManager/Omemo/OmemoSignalKeyDBManager.cs @@ -2,8 +2,8 @@ using Data_Manager2.Classes.DBTables.Omemo; using libsignal; using libsignal.state; +using Shared.Classes.SQLite; using System.Collections.Generic; -using Thread_Save_Components.Classes.SQLite; namespace Data_Manager2.Classes.DBManager.Omemo { diff --git a/Data_Manager2/Classes/DBManager/SpamDBManager.cs b/Data_Manager2/Classes/DBManager/SpamDBManager.cs new file mode 100644 index 000000000..5c1042d72 --- /dev/null +++ b/Data_Manager2/Classes/DBManager/SpamDBManager.cs @@ -0,0 +1,116 @@ +using Data_Manager2.Classes.DBTables; +using Logging; +using Shared.Classes.SQLite; +using SQLite; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Data_Manager2.Classes.DBManager +{ + public class SpamDBManager : AbstractDBManager + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public static readonly SpamDBManager INSTANCE = new SpamDBManager(); + + public const string DEFAULT_SPAM_REGEX = @"\p{IsCyrillic}"; + private Regex spamRegex = null; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + public bool isSpam(string text) + { + if(!(spamRegex is null)) + { + return !(getSpam(text) is null) || spamRegex.IsMatch(text); + } + return !(getSpam(text) is null); + } + + public SpamMessageTable getSpam(string text) + { + SQLiteCommand cmd = dB.CreateCommand("SELECT * FROM " + DBTableConsts.SPAM_MESSAGE_TABLE + " WHERE text LIKE @TEXT;"); + cmd.Bind("@TEXT", '%' + text + '%'); + List result = dB.ExecuteCommand(true, cmd); + if (result.Count > 0) + { + return result[0]; + } + return null; + } + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public void addSpamMessage(string text, DateTime dateTime) + { + SpamMessageTable spam = getSpam(text); + if(spam is null) + { + spam = new SpamMessageTable + { + lastReceived = dateTime, + text = text, + count = 1, + }; + } + else + { + spam.count++; + spam.lastReceived = dateTime; + } + dB.InsertOrReplace(spam); + } + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + protected override void createTables() + { + dB.CreateTable(); + } + + protected override void dropTables() + { + dB.DropTable(); + } + + public override void initManager() + { + string regex = Settings.getSettingString(SettingsConsts.SPAM_REGEX, DEFAULT_SPAM_REGEX); + updateSpamRegex(regex); + } + + public void updateSpamRegex(string regex) + { + try + { + spamRegex = new Regex(regex); + } + catch (Exception e) + { + Logger.Error("Failed to create spam regular expression for: " + regex, e); + spamRegex = null; + } + } + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Data_Manager2/Classes/DBTables/AccountTable.cs b/Data_Manager2/Classes/DBTables/AccountTable.cs index b10546fec..e5429a6e4 100644 --- a/Data_Manager2/Classes/DBTables/AccountTable.cs +++ b/Data_Manager2/Classes/DBTables/AccountTable.cs @@ -1,7 +1,6 @@ using SQLite; -using XMPP_API.Classes.Network; using XMPP_API.Classes; -using XMPP_API.Classes.Network.XML.Messages.XEP_0384; +using XMPP_API.Classes.Network; namespace Data_Manager2.Classes.DBTables { @@ -63,10 +62,10 @@ public AccountTable() public AccountTable(XMPPAccount account) { - this.id = account.getIdAndDomain(); - this.userId = account.user.userId; - this.domain = account.user.domain; - this.resource = account.user.resource; + this.id = account.getBareJid(); + this.userId = account.user.localPart; + this.domain = account.user.domainPart; + this.resource = account.user.resourcePart; this.serverAddress = account.serverAddress; this.port = account.port; this.disabled = account.disabled; @@ -82,8 +81,10 @@ public AccountTable(XMPPAccount account) internal XMPPAccount toXMPPAccount() { - return new XMPPAccount(new XMPPUser(userId, domain, resource), serverAddress, port) + return new XMPPAccount(new XMPPUser(userId, domain, resource)) { + serverAddress = serverAddress, + port = port, color = color, presencePriorety = presencePriorety, disabled = disabled, diff --git a/Data_Manager2/Classes/DBTables/ChatTable.cs b/Data_Manager2/Classes/DBTables/ChatTable.cs index d95c3f441..e1f120d68 100644 --- a/Data_Manager2/Classes/DBTables/ChatTable.cs +++ b/Data_Manager2/Classes/DBTables/ChatTable.cs @@ -33,7 +33,7 @@ public class ChatTable : IComparable // online, dnd, xa, ... public Presence presence { get; set; } [Ignore] - // The state of the chat (XEP-0083) - only interesting during runtime + // The state of the chat (XEP-0085) - only interesting during runtime public string chatState { get; set; } [NotNull] // The type of the chat e.g. MUC/MIX/... @@ -63,7 +63,7 @@ public ChatTable(string chatJabberId, string userAccountId) this.chatType = ChatType.CHAT; this.inRoster = false; this.muted = false; - this.omemoEnabled = false; + this.omemoEnabled = Settings.getSettingBoolean(SettingsConsts.ENABLE_OMEMO_BY_DEFAULT_FOR_NEW_CHATS); this.presence = Presence.Unavailable; this.status = null; this.subscription = null; diff --git a/Data_Manager2/Classes/DBTables/DBTableConsts.cs b/Data_Manager2/Classes/DBTables/DBTableConsts.cs index 6944ff4ef..455185d0f 100644 --- a/Data_Manager2/Classes/DBTables/DBTableConsts.cs +++ b/Data_Manager2/Classes/DBTables/DBTableConsts.cs @@ -8,12 +8,13 @@ static class DBTableConsts public const string DISCO_FEATURE_TABLE = "DiscoFeatureTable"; public const string DISCO_IDENTITY_TABLE = "DiscoIdentityTable"; public const string DISCO_ITEM_TABLE = "DiscoItemTable"; - public const string IMAGE_TABLE = "ImageTable"; + public const string IMAGE_TABLE = "ImageTable_2"; public const string MUC_CHAT_INFO_TABLE = "MUCChatInfoTable_3"; public const string MUC_OCCUPANT_TABLE = "MUCOccupantTable"; public const string MUC_DIRECT_INVITATION_TABLE = "MUCDirectInvitationTable"; public const string IGNORED_CERTIFICATE_ERROR_TABLE = "IgnoredCertificateErrorTable"; public const string CONNECTION_OPTIONS_TABLE = "ConnectionOptionsTable_2"; + public const string SPAM_MESSAGE_TABLE = "SpamMessageTable"; public const string OMEMO_SIGNED_PRE_KEY_TABLE = "OmemoSignedPreKeyTable"; public const string OMEMO_PRE_KEY_TABLE = "OmemoPreKeyTable"; diff --git a/Data_Manager2/Classes/DBTables/ImageTable.cs b/Data_Manager2/Classes/DBTables/ImageTable.cs index cdd38804b..dac44a2ae 100644 --- a/Data_Manager2/Classes/DBTables/ImageTable.cs +++ b/Data_Manager2/Classes/DBTables/ImageTable.cs @@ -1,49 +1,21 @@ -using Data_Manager2.Classes.Events; +using Shared.Classes.Network; using SQLite; -using System.Threading.Tasks; -using Windows.Storage; -using Windows.UI.Xaml.Media.Imaging; -using System; namespace Data_Manager2.Classes.DBTables { [Table(DBTableConsts.IMAGE_TABLE)] - public class ImageTable + public class ImageTable : AbstractDownloadableObject { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- [PrimaryKey] // The id of the message: public string messageId { get; set; } - // The local path: - public string path { get; set; } - // The state of the image download: - public DownloadState state { get; set; } - // If the image download failed: - public string errorMessage { get; set; } - - // The image download progress: - [Ignore] - public double progress { get; set; } - - public delegate void DownloadStateChangedHandler(ImageTable img, DownloadStateChangedEventArgs args); - public delegate void DownloadProgressChangedHandler(ImageTable img, DownloadProgressChangedEventArgs args); - - public event DownloadStateChangedHandler DownloadStateChanged; - public event DownloadProgressChangedHandler DownloadProgressChanged; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 17/11/2017 Created [Fabian Sauter] - /// - public ImageTable() - { - } + #endregion //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ @@ -53,45 +25,7 @@ public ImageTable() #endregion //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ #region --Misc Methods (Public)-- - public void onStateChanged() - { - DownloadStateChanged?.Invoke(this, new DownloadStateChangedEventArgs(state)); - } - - public void onDownloadProgressChanged() - { - DownloadProgressChanged?.Invoke(this, new DownloadProgressChangedEventArgs(progress)); - } - - /// - /// Converts the path to an BitmapImage. - /// This is a workaround to open also images that are stored on a separate drive. - /// - /// The BitmapImage representation of the current path object. - public async Task getBitmapImageAsync() - { - if(path is null) - { - return null; - } - - try - { - StorageFile file = await StorageFile.GetFileFromPathAsync(path); - if(file is null) - { - return null; - } - - BitmapImage img = new BitmapImage(); - img.SetSource(await file.OpenReadAsync()); - return img; - } - catch (Exception) - { - return null; - } - } + #endregion diff --git a/Data_Manager2/Classes/DBTables/SpamMessageTable.cs b/Data_Manager2/Classes/DBTables/SpamMessageTable.cs new file mode 100644 index 000000000..865999533 --- /dev/null +++ b/Data_Manager2/Classes/DBTables/SpamMessageTable.cs @@ -0,0 +1,49 @@ +using SQLite; +using System; + +namespace Data_Manager2.Classes.DBTables +{ + [Table(DBTableConsts.SPAM_MESSAGE_TABLE)] + public class SpamMessageTable + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + [AutoIncrement, PrimaryKey] + public int id { get; set; } + public DateTime lastReceived { get; set; } + public int count { get; set; } + public string text { get; set; } + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Data_Manager2/Classes/DownloadState.cs b/Data_Manager2/Classes/DownloadState.cs deleted file mode 100644 index 24e7b8001..000000000 --- a/Data_Manager2/Classes/DownloadState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Data_Manager2.Classes -{ - public enum DownloadState - { - WAITING, - DOWNLOADING, - DONE, - ERROR - } -} diff --git a/Data_Manager2/Classes/Events/ChatMessageChangedEventArgs.cs b/Data_Manager2/Classes/Events/ChatMessageChangedEventArgs.cs index d5705a793..23b01a930 100644 --- a/Data_Manager2/Classes/Events/ChatMessageChangedEventArgs.cs +++ b/Data_Manager2/Classes/Events/ChatMessageChangedEventArgs.cs @@ -8,6 +8,7 @@ public class ChatMessageChangedEventArgs : EventArgs //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- public readonly ChatMessageTable MESSAGE; + public readonly bool REMOVED; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ @@ -18,9 +19,10 @@ public class ChatMessageChangedEventArgs : EventArgs /// /// 01/01/2018 Created [Fabian Sauter] /// - public ChatMessageChangedEventArgs(ChatMessageTable message) + public ChatMessageChangedEventArgs(ChatMessageTable message, bool removed) { this.MESSAGE = message; + this.REMOVED = removed; } #endregion diff --git a/Data_Manager2/Classes/ImageDownloadHandler.cs b/Data_Manager2/Classes/ImageDownloadHandler.cs new file mode 100644 index 000000000..c1408c1ec --- /dev/null +++ b/Data_Manager2/Classes/ImageDownloadHandler.cs @@ -0,0 +1,155 @@ +using Data_Manager2.Classes.DBManager; +using Data_Manager2.Classes.DBTables; +using Logging; +using Shared.Classes.Network; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Windows.Storage; + +namespace Data_Manager2.Classes +{ + public class ImageDownloadHandler + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + private readonly DownloadHandler DOWNLOAD_HANDLER; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ImageDownloadHandler(DownloadHandler downloadHandler) + { + this.DOWNLOAD_HANDLER = downloadHandler; + this.DOWNLOAD_HANDLER.DownloadStateChanged += DOWNLOAD_HANDLER_DownloadStateChanged; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + public async Task GetImageCacheFolderAsync() + { + if (Settings.getSettingBoolean(SettingsConsts.DISABLE_DOWNLOAD_IMAGES_TO_LIBARY)) + { + return await ApplicationData.Current.LocalFolder.CreateFolderAsync("cachedImages", CreationCollisionOption.OpenIfExists); + } + return await KnownFolders.PicturesLibrary.CreateFolderAsync("UWPX", CreationCollisionOption.OpenIfExists); + } + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public async Task DownloadImageAsync(ImageTable image) + { + if (image.State != DownloadState.DOWNLOADING && image.State != DownloadState.QUEUED) + { + await DOWNLOAD_HANDLER.EnqueueDownloadAsync(image); + } + } + + public async Task DownloadImageAsync(ChatMessageTable msg) + { + ImageTable image = await ImageDBManager.INSTANCE.getImageAsync(msg); + if (image is null) + { + StorageFolder folder = await GetImageCacheFolderAsync(); + image = new ImageTable() + { + messageId = msg.id, + SourceUrl = msg.message, + TargetFileName = CreateUniqueFileName(msg.message), + TargetFolderPath = folder.Path, + State = DownloadState.NOT_QUEUED, + Progress = 0, + }; + ImageDBManager.INSTANCE.setImage(image); + } + await DownloadImageAsync(image); + return image; + } + + public async Task RedownloadImageAsync(ImageTable image) + { + await DOWNLOAD_HANDLER.EnqueueDownloadAsync(image); + } + + public void CancelDownload(ImageTable image) + { + DOWNLOAD_HANDLER.CancelDownload(image); + } + + public async Task FindAsync(Predicate predicate) + { + return await DOWNLOAD_HANDLER.FindAsync(predicate); + } + + public async Task ContinueDownloadsAsync() + { + if (!Settings.getSettingBoolean(SettingsConsts.DISABLE_IMAGE_AUTO_DOWNLOAD)) + { + List images = ImageDBManager.INSTANCE.getAllUndownloadedImages(); + foreach (ImageTable image in images) + { + if (image.State == DownloadState.DOWNLOADING || image.State == DownloadState.QUEUED) + { + image.State = DownloadState.NOT_QUEUED; + ImageDBManager.INSTANCE.setImage(image); + } + await DownloadImageAsync(image); + } + } + } + + public async Task ClearImageCacheAsync() + { + StorageFolder folder = await GetImageCacheFolderAsync(); + if (folder != null) + { + await folder.DeleteAsync(); + } + // Recreate the image cache folder: + await GetImageCacheFolderAsync(); + Logger.Info("Image cache cleared!"); + } + + public async Task OpenImageCacheFolderAsync() + { + StorageFolder folder = await GetImageCacheFolderAsync(); + await Windows.System.Launcher.LaunchFolderAsync(folder); + } + + #endregion + + #region --Misc Methods (Private)-- + /// + /// Creates an unique file name, bases on the given url and the current time. + /// + /// The url of the image. + /// Returns an unique file name. + private string CreateUniqueFileName(string url) + { + string name = DateTime.Now.ToString("dd.MM.yyyy_HH.mm.ss.ffff"); + int index = url.LastIndexOf('.'); + string ending = url.Substring(index, url.Length - index); + return name + ending; + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void DOWNLOAD_HANDLER_DownloadStateChanged(AbstractDownloadableObject o, DownloadStateChangedEventArgs args) + { + if (o is ImageTable image) + { + ImageDBManager.INSTANCE.setImage(image); + } + } + + #endregion + } +} diff --git a/Data_Manager2/Classes/MUCHandler.cs b/Data_Manager2/Classes/MUCHandler.cs index a26e25e99..66eceef0b 100644 --- a/Data_Manager2/Classes/MUCHandler.cs +++ b/Data_Manager2/Classes/MUCHandler.cs @@ -1,9 +1,9 @@ using Data_Manager2.Classes.DBManager; using Data_Manager2.Classes.DBTables; using Logging; +using Shared.Classes.Collections; using System; using System.Threading.Tasks; -using Thread_Save_Components.Classes.Collections; using XMPP_API.Classes; using XMPP_API.Classes.Network.XML.Messages; using XMPP_API.Classes.Network.XML.Messages.XEP_0045; @@ -50,11 +50,11 @@ public void onClientConnected(XMPPClient client) client.NewValidMessage -= Client_NewValidMessage; client.NewValidMessage += Client_NewValidMessage; - MUCDBManager.INSTANCE.resetMUCState(client.getXMPPAccount().getIdAndDomain(), true); + MUCDBManager.INSTANCE.resetMUCState(client.getXMPPAccount().getBareJid(), true); if (!Settings.getSettingBoolean(SettingsConsts.DISABLE_AUTO_JOIN_MUC)) { - Logger.Info("Entering all MUC rooms for '" + client.getXMPPAccount().getIdAndDomain() + '\''); + Logger.Info("Entering all MUC rooms for '" + client.getXMPPAccount().getBareJid() + '\''); enterAllMUCs(client); } } @@ -68,7 +68,7 @@ public void onClientDisconnecting(XMPPClient client) { client.NewMUCMemberPresenceMessage -= C_NewMUCMemberPresenceMessage; client.NewValidMessage -= Client_NewValidMessage; - MUCDBManager.INSTANCE.resetMUCState(client.getXMPPAccount().getIdAndDomain(), true); + MUCDBManager.INSTANCE.resetMUCState(client.getXMPPAccount().getBareJid(), true); } public void onMUCRoomSubjectMessage(MUCRoomSubjectMessage mucRoomSubject) @@ -113,7 +113,7 @@ private void stopMUCJoinHelper(ChatTable muc) private async Task sendMUCLeaveMessageAsync(XMPPClient client, ChatTable muc, MUCChatInfoTable info) { - string from = client.getXMPPAccount().getIdDomainAndResource(); + string from = client.getXMPPAccount().getFullJid(); string to = muc.chatJabberId + '/' + info.nickname; LeaveRoomMessage msg = new LeaveRoomMessage(from, to); await client.sendAsync(msg, false); @@ -123,7 +123,7 @@ private void enterAllMUCs(XMPPClient client) { Task.Run(async () => { - foreach (ChatTable muc in ChatDBManager.INSTANCE.getAllMUCs(client.getXMPPAccount().getIdAndDomain())) + foreach (ChatTable muc in ChatDBManager.INSTANCE.getAllMUCs(client.getXMPPAccount().getBareJid())) { MUCChatInfoTable info = MUCDBManager.INSTANCE.getMUCInfo(muc.id); if (info is null) @@ -150,7 +150,7 @@ private async Task onMUCErrorMessageAsync(XMPPClient client, MUCErrorMessage err string room = Utils.getBareJidFromFullJid(errorMessage.getFrom()); if (room != null) { - string chatId = ChatTable.generateId(room, client.getXMPPAccount().getIdAndDomain()); + string chatId = ChatTable.generateId(room, client.getXMPPAccount().getBareJid()); ChatTable muc = ChatDBManager.INSTANCE.getChat(chatId); if (muc != null) { @@ -243,7 +243,7 @@ private void C_NewMUCMemberPresenceMessage(XMPPClient client, XMPP_API.Classes.N { return; } - string chatId = ChatTable.generateId(roomJid, client.getXMPPAccount().getIdAndDomain()); + string chatId = ChatTable.generateId(roomJid, client.getXMPPAccount().getBareJid()); MUCOccupantTable member = MUCDBManager.INSTANCE.getMUCOccupant(chatId, msg.FROM_NICKNAME); if (member is null) diff --git a/Data_Manager2/Classes/MUCJoinHelper.cs b/Data_Manager2/Classes/MUCJoinHelper.cs index 8a27fc54e..d19c4add4 100644 --- a/Data_Manager2/Classes/MUCJoinHelper.cs +++ b/Data_Manager2/Classes/MUCJoinHelper.cs @@ -1,9 +1,9 @@ using Data_Manager2.Classes.DBManager; using Data_Manager2.Classes.DBTables; using Logging; +using Shared.Classes.Collections; using System; using System.Threading.Tasks; -using Thread_Save_Components.Classes.Collections; using XMPP_API.Classes; using XMPP_API.Classes.Network.XML.Messages.XEP_0045; @@ -47,7 +47,7 @@ public MUCJoinHelper(XMPPClient client, ChatTable muc, MUCChatInfoTable info) #region --Misc Methods (Public)-- public async Task requestReservedNicksAsync() { - DiscoReservedRoomNicknamesMessages msg = new DiscoReservedRoomNicknamesMessages(CLIENT.getXMPPAccount().getIdDomainAndResource(), MUC.chatJabberId); + DiscoReservedRoomNicknamesMessages msg = new DiscoReservedRoomNicknamesMessages(CLIENT.getXMPPAccount().getFullJid(), MUC.chatJabberId); await CLIENT.sendAsync(msg, false); } @@ -61,7 +61,7 @@ public async Task enterRoomAsync() MUCDBManager.INSTANCE.deleteAllOccupantsforChat(MUC.id); // Create message: - JoinRoomRequestMessage msg = new JoinRoomRequestMessage(CLIENT.getXMPPAccount().getIdDomainAndResource(), MUC.chatJabberId, INFO.nickname, INFO.password); + JoinRoomRequestMessage msg = new JoinRoomRequestMessage(CLIENT.getXMPPAccount().getFullJid(), MUC.chatJabberId, INFO.nickname, INFO.password); // Subscribe to events for receiving answers: CLIENT.NewMUCMemberPresenceMessage -= CLIENT_NewMUCMemberPresenceMessage; diff --git a/Data_Manager2/Classes/Omemo/OmemoStore.cs b/Data_Manager2/Classes/Omemo/OmemoStore.cs index b0fefe298..4f5ff7f90 100644 --- a/Data_Manager2/Classes/Omemo/OmemoStore.cs +++ b/Data_Manager2/Classes/Omemo/OmemoStore.cs @@ -52,7 +52,7 @@ public bool IsTrustedIdentity(string name, IdentityKey identityKey) public List GetSubDeviceSessions(string name) { - return OmemoSignalKeyDBManager.INSTANCE.getDeviceIds(name, ACCOUNT.getIdAndDomain()); + return OmemoSignalKeyDBManager.INSTANCE.getDeviceIds(name, ACCOUNT.getBareJid()); } #endregion @@ -60,19 +60,19 @@ public List GetSubDeviceSessions(string name) #region --Misc Methods (Public)-- public bool SaveIdentity(string name, IdentityKey identityKey) { - bool contains = OmemoSignalKeyDBManager.INSTANCE.containsIdentityKey(name, ACCOUNT.getIdAndDomain()); - OmemoSignalKeyDBManager.INSTANCE.setIdentityKey(name, identityKey, ACCOUNT.getIdAndDomain()); + bool contains = OmemoSignalKeyDBManager.INSTANCE.containsIdentityKey(name, ACCOUNT.getBareJid()); + OmemoSignalKeyDBManager.INSTANCE.setIdentityKey(name, identityKey, ACCOUNT.getBareJid()); return contains; } public bool ContainsPreKey(uint preKeyId) { - return OmemoSignalKeyDBManager.INSTANCE.containsPreKeyRecord(preKeyId, ACCOUNT.getIdAndDomain()); + return OmemoSignalKeyDBManager.INSTANCE.containsPreKeyRecord(preKeyId, ACCOUNT.getBareJid()); } public PreKeyRecord LoadPreKey(uint preKeyId) { - PreKeyRecord preKeyRecord = OmemoSignalKeyDBManager.INSTANCE.getPreKeyRecord(preKeyId, ACCOUNT.getIdAndDomain()); + PreKeyRecord preKeyRecord = OmemoSignalKeyDBManager.INSTANCE.getPreKeyRecord(preKeyId, ACCOUNT.getBareJid()); if (preKeyRecord is null) { throw new InvalidKeyIdException("No such key: " + preKeyId); @@ -82,17 +82,17 @@ public PreKeyRecord LoadPreKey(uint preKeyId) public void RemovePreKey(uint preKeyId) { - OmemoSignalKeyDBManager.INSTANCE.deletePreKey(preKeyId, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deletePreKey(preKeyId, ACCOUNT.getBareJid()); } public void StorePreKey(uint preKeyId, PreKeyRecord preKey) { - OmemoSignalKeyDBManager.INSTANCE.setPreKey(preKeyId, preKey, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setPreKey(preKeyId, preKey, ACCOUNT.getBareJid()); } public bool ContainsSession(SignalProtocolAddress address) { - SessionRecord session = OmemoSignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getIdAndDomain()); + SessionRecord session = OmemoSignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getBareJid()); return session != null && session.getSessionState().hasSenderChain() && session.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION; } @@ -103,12 +103,12 @@ public void DeleteAllSessions(string name) public void DeleteSession(SignalProtocolAddress address) { - OmemoSignalKeyDBManager.INSTANCE.deleteSession(address, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deleteSession(address, ACCOUNT.getBareJid()); } public SessionRecord LoadSession(SignalProtocolAddress address) { - SessionRecord session = OmemoSignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getIdAndDomain()); + SessionRecord session = OmemoSignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getBareJid()); if (session is null) { Logger.Warn("No existing libsignal session found for: " + address.ToString()); @@ -119,17 +119,17 @@ public SessionRecord LoadSession(SignalProtocolAddress address) public void StoreSession(SignalProtocolAddress address, SessionRecord record) { - OmemoSignalKeyDBManager.INSTANCE.setSession(address, record, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setSession(address, record, ACCOUNT.getBareJid()); } public bool ContainsSignedPreKey(uint signedPreKeyId) { - return OmemoSignalKeyDBManager.INSTANCE.containsSignedPreKey(signedPreKeyId, ACCOUNT.getIdAndDomain()); + return OmemoSignalKeyDBManager.INSTANCE.containsSignedPreKey(signedPreKeyId, ACCOUNT.getBareJid()); } public SignedPreKeyRecord LoadSignedPreKey(uint signedPreKeyId) { - SignedPreKeyRecord signedPreKeyRecord = OmemoSignalKeyDBManager.INSTANCE.getSignedPreKey(signedPreKeyId, ACCOUNT.getIdAndDomain()); + SignedPreKeyRecord signedPreKeyRecord = OmemoSignalKeyDBManager.INSTANCE.getSignedPreKey(signedPreKeyId, ACCOUNT.getBareJid()); if (signedPreKeyRecord is null) { throw new InvalidKeyIdException("No such key: " + signedPreKeyId); @@ -139,52 +139,52 @@ public SignedPreKeyRecord LoadSignedPreKey(uint signedPreKeyId) public List LoadSignedPreKeys() { - return OmemoSignalKeyDBManager.INSTANCE.getAllSignedPreKeys(ACCOUNT.getIdAndDomain()); + return OmemoSignalKeyDBManager.INSTANCE.getAllSignedPreKeys(ACCOUNT.getBareJid()); } public void RemoveSignedPreKey(uint signedPreKeyId) { - OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(signedPreKeyId, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.deleteSignedPreKey(signedPreKeyId, ACCOUNT.getBareJid()); } public void StoreSignedPreKey(uint signedPreKeyId, SignedPreKeyRecord signedPreKey) { - OmemoSignalKeyDBManager.INSTANCE.setSignedPreKey(signedPreKeyId, signedPreKey, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setSignedPreKey(signedPreKeyId, signedPreKey, ACCOUNT.getBareJid()); } public IList LoadPreKeys() { - return OmemoSignalKeyDBManager.INSTANCE.getAllPreKeys(ACCOUNT.getIdAndDomain()); + return OmemoSignalKeyDBManager.INSTANCE.getAllPreKeys(ACCOUNT.getBareJid()); } public void StorePreKeys(IList preKeys) { - OmemoSignalKeyDBManager.INSTANCE.setPreKeys(preKeys, ACCOUNT.getIdAndDomain()); + OmemoSignalKeyDBManager.INSTANCE.setPreKeys(preKeys, ACCOUNT.getBareJid()); } public void StoreDevices(IList devices) { - OmemoDeviceDBManager.INSTANCE.setDevices(devices, ACCOUNT.getIdAndDomain()); + OmemoDeviceDBManager.INSTANCE.setDevices(devices, ACCOUNT.getBareJid()); } public void StoreDevice(SignalProtocolAddress device) { - OmemoDeviceDBManager.INSTANCE.setDevice(device, ACCOUNT.getIdAndDomain()); + OmemoDeviceDBManager.INSTANCE.setDevice(device, ACCOUNT.getBareJid()); } public IList LoadDevices(string name) { - return OmemoDeviceDBManager.INSTANCE.getDevices(name, ACCOUNT.getIdAndDomain()); + return OmemoDeviceDBManager.INSTANCE.getDevices(name, ACCOUNT.getBareJid()); } public void StoreDeviceListSubscription(string name, Tuple lastUpdate) { - OmemoDeviceDBManager.INSTANCE.setOmemoDeviceListSubscription(name, lastUpdate, ACCOUNT.getIdAndDomain()); + OmemoDeviceDBManager.INSTANCE.setOmemoDeviceListSubscription(name, lastUpdate, ACCOUNT.getBareJid()); } public Tuple LoadDeviceListSubscription(string name) { - return OmemoDeviceDBManager.INSTANCE.getOmemoDeviceListSubscription(name, ACCOUNT.getIdAndDomain()); + return OmemoDeviceDBManager.INSTANCE.getOmemoDeviceListSubscription(name, ACCOUNT.getBareJid()); } #endregion diff --git a/Data_Manager2/Classes/SettingsConsts.cs b/Data_Manager2/Classes/SettingsConsts.cs index 8d5ccaad2..03c1af7a7 100644 --- a/Data_Manager2/Classes/SettingsConsts.cs +++ b/Data_Manager2/Classes/SettingsConsts.cs @@ -4,7 +4,7 @@ public static class SettingsConsts { public const string INITIALLY_STARTED = "initially_started"; public const string HIDE_INITIAL_START_DIALOG_ALPHA = "hide_initial_start_dialog_alpha"; - public const string HIDE_WHATS_NEW_DIALOG = "hide_whats_new_dialog_alpha_12"; + public const string HIDE_WHATS_NEW_DIALOG = "hide_whats_new_dialog_alpha_14"; public const string APP_REQUESTED_THEME = "app_requested_theme"; @@ -18,10 +18,13 @@ public static class SettingsConsts public const string DONT_SEND_CHAT_MESSAGE_RECEIVED_MARKERS = "dont_send_chat_message_received_markers"; public const string DISABLE_PUSH = "disable_push"; public const string DISABLE_DOWNLOAD_IMAGES_TO_LIBARY = "disable_download_images_to_libary"; + public const string DISABLE_IMAGE_AUTO_DOWNLOAD = "disable_image_auto_download"; public const string DISABLE_CRASH_REPORTING = "disable_crash_reporting"; public const string DISABLE_ANALYTICS = "disable_analytics"; public const string DISABLE_AUTO_JOIN_MUC = "disable_auto_join_muc"; public const string DISABLE_ADVANCED_CHAT_MESSAGE_PROCESSING = "disable_advanced_chat_message_processing"; + public const string ENABLE_OMEMO_BY_DEFAULT_FOR_NEW_CHATS = "enable_omemo_by_default_for_new_chats"; + public const string DISABLE_VIBRATION_FOR_NEW_CHAT_MESSAGES = "disable_vibration_for_new_chat_messages"; public const string PUSH_CHANNEL_TOKEN_URL = "push_channel_token_url"; public const string PUSH_CHANNEL_SEND_SUCCESS = "push_channel_send_success"; @@ -31,13 +34,22 @@ public static class SettingsConsts public const string VERSION_BUILD = "version_build"; public const string VERSION_REVISION = "version_revision"; public const string LOG_LEVEL = "log_level"; + public const string DEBUG_SETTINGS_ENABLED = "debug_settings_enabled"; - public const string CHAT_FILTER_PRESENCES = "chat_filter_presences"; + public const string CHAT_FILTER_PRESENCE_ONLINE = "chat_filter_presence_online"; + public const string CHAT_FILTER_PRESENCE_AWAY = "chat_filter_presence_away"; + public const string CHAT_FILTER_PRESENCE_UNAVAILABLE = "chat_filter_presence_unavailable"; + public const string CHAT_FILTER_PRESENCE_DND = "chat_filter_presence_dnd"; + public const string CHAT_FILTER_PRESENCE_XA = "chat_filter_presence_xa"; + public const string CHAT_FILTER_PRESENCE_CHAT = "chat_filter_presence_chat"; public const string CHAT_FILTER_NOT_ONLINE = "chat_filter_not_online"; public const string CHAT_FILTER_NOT_UNAVAILABLE = "chat_filter_not_unavailable"; public const string CHAT_FILTER_QUERY = "chat_filter_query"; public const string CHAT_FILTER_QUERY_ENABLED = "chat_filter_query_enabled"; - public const string CHAT_FILTER_CHAT = "chat_filter_chat"; - public const string CHAT_FILTER_MUC = "chat_filter_muc"; + public const string CHAT_FILTER_CHATS_ONLY = "chat_filter_chats_only"; + public const string CHAT_FILTER_MUCS_ONLY = "chat_filter_mucs_only"; + public const string SPAM_DETECTION_ENABLED = "spam_detection_enabled"; + public const string SPAM_DETECTION_FOR_ALL_CHAT_MESSAGES = "spam_detection_for_all_chat_messages"; + public const string SPAM_REGEX = "spam_regex"; } } diff --git a/Data_Manager2/Classes/Toast/ToastHelper.cs b/Data_Manager2/Classes/Toast/ToastHelper.cs index 36dda6cd1..b01d79fc3 100644 --- a/Data_Manager2/Classes/Toast/ToastHelper.cs +++ b/Data_Manager2/Classes/Toast/ToastHelper.cs @@ -1,10 +1,10 @@ using Data_Manager2.Classes.DBTables; using Logging; using Microsoft.Toolkit.Uwp.Notifications; -using Windows.UI.Notifications; -using Windows.Phone.Devices.Notification; using System; using Windows.Foundation.Metadata; +using Windows.Phone.Devices.Notification; +using Windows.UI.Notifications; namespace Data_Manager2.Classes.Toast { @@ -185,7 +185,7 @@ private static void popToast(ToastContent content, ChatTable chat) private static void popToastReduced() { - if(ApiInformation.IsTypePresent("Windows.Phone.Devices.Notification.VibrationDevice")) + if (ApiInformation.IsTypePresent("Windows.Phone.Devices.Notification.VibrationDevice") && !Settings.getSettingBoolean(SettingsConsts.DISABLE_VIBRATION_FOR_NEW_CHAT_MESSAGES)) { VibrationDevice.GetDefault().Vibrate(VIBRATE_TS); } diff --git a/Data_Manager2/Classes/Vault.cs b/Data_Manager2/Classes/Vault.cs index 9fdec8549..18f11b2cd 100644 --- a/Data_Manager2/Classes/Vault.cs +++ b/Data_Manager2/Classes/Vault.cs @@ -28,10 +28,10 @@ public static class Vault /// The XMPPAccount you want to retrieve the PasswordCredential for. private static PasswordCredential getPasswordCredentialForAccount(XMPPAccount account) { - string vaultName = VAULT_NAME_PREFIX + account.getIdAndDomain(); + string vaultName = VAULT_NAME_PREFIX + account.getBareJid(); try { - return PASSWORD_VAULT.Retrieve(vaultName, account.user.userId); + return PASSWORD_VAULT.Retrieve(vaultName, account.user.localPart); } catch (Exception) { @@ -69,12 +69,12 @@ public static void loadPassword(XMPPAccount account) PasswordCredential passwordCredential = getPasswordCredentialForAccount(account); if (passwordCredential is null) { - Logger.Warn("No password found for: " + account.user.getIdAndDomain()); - account.user.userPassword = ""; + Logger.Warn("No password found for: " + account.user.getBareJid()); + account.user.password = ""; return; } passwordCredential.RetrievePassword(); - account.user.userPassword = passwordCredential.Password; + account.user.password = passwordCredential.Password; } /// @@ -89,10 +89,10 @@ public static void storePassword(XMPPAccount account) //removeAll(); // Store the new password: - if(!string.IsNullOrEmpty(account.user.userPassword)) + if (!string.IsNullOrEmpty(account.user.password)) { - string vaultName = VAULT_NAME_PREFIX + account.getIdAndDomain(); - PASSWORD_VAULT.Add(new PasswordCredential(vaultName, account.user.userId, account.user.userPassword)); + string vaultName = VAULT_NAME_PREFIX + account.getBareJid(); + PASSWORD_VAULT.Add(new PasswordCredential(vaultName, account.user.localPart, account.user.password)); } } diff --git a/Data_Manager2/Data_Manager2.csproj b/Data_Manager2/Data_Manager2.csproj index 080694272..54430dfcf 100644 --- a/Data_Manager2/Data_Manager2.csproj +++ b/Data_Manager2/Data_Manager2.csproj @@ -4,15 +4,15 @@ Debug AnyCPU - {CB58CE53-8E53-49C8-9936-15FE2EA6A7EB} + {58925BF1-5B7D-48D9-B411-126CBEFDA98A} Library Properties Data_Manager2 Data_Manager2 - de-DE + en-US UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -26,6 +26,7 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest AnyCPU @@ -35,6 +36,7 @@ TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest x86 @@ -43,9 +45,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x86 false prompt + latest x86 @@ -54,9 +56,9 @@ true ;2008 pdbonly - x86 false prompt + latest ARM @@ -65,9 +67,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - ARM false prompt + latest ARM @@ -76,9 +78,31 @@ true ;2008 pdbonly - ARM false prompt + latest + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + latest + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + latest x64 @@ -87,9 +111,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x64 false prompt + latest x64 @@ -98,15 +122,14 @@ true ;2008 pdbonly - x64 false prompt + latest PackageReference - @@ -116,40 +139,41 @@ + + + + + - - - - - - - - - - + + + + - + + + @@ -162,40 +186,36 @@ - - 6.2.2 + 6.2.3 4.0.0 - - 4.0.0 - - - - - Windows Mobile Extensions for the UWP - - {53bb5463-9646-4f79-be97-3d73473aad92} + {0c1fb090-4492-441d-b182-c999df71f917} Logging - - {60938c69-a2d7-4c27-8b73-84c203bd4787} - Thread_Save_Components + + {c4af94da-11cf-4147-b5b7-4b2dc7c624a0} + Shared - {899fb043-af8b-4294-95b6-1482c79459ac} + {64b9a47f-d404-4d0b-a34a-bec280c5ff6b} XMPP_API + + + Windows Mobile Extensions for the UWP + + 14.0 diff --git a/Data_Manager2/Properties/AssemblyInfo.cs b/Data_Manager2/Properties/AssemblyInfo.cs index 297dd12e2..1e77633c6 100644 --- a/Data_Manager2/Properties/AssemblyInfo.cs +++ b/Data_Manager2/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Data_Manager2")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Data_Manager2/Properties/Data_Manager2.rd.xml b/Data_Manager2/Properties/Data_Manager2.rd.xml index 7a208c2d5..bf9a4cbf3 100644 --- a/Data_Manager2/Properties/Data_Manager2.rd.xml +++ b/Data_Manager2/Properties/Data_Manager2.rd.xml @@ -1,33 +1,33 @@ - + diff --git a/Logging/Logger.cs b/Logging/Logger.cs index c0ceac295..fb0d74cc4 100644 --- a/Logging/Logger.cs +++ b/Logging/Logger.cs @@ -70,7 +70,7 @@ public static async Task getLogFolderSizeAsync() /// Returns the "Logs" folder and creates it, if it does not exist. /// /// Returns the "Logs" folder. - private static async Task getLogFolderAsync() + public static async Task getLogFolderAsync() { return await ApplicationData.Current.LocalFolder.CreateFolderAsync("Logs", CreationCollisionOption.OpenIfExists); } @@ -204,7 +204,7 @@ public static async Task deleteLogsAsync() /// /// Exports all logs as a .zip file to the selected path. /// - public static async Task exportLogs() + public static async Task exportLogsAsync() { // Get the target path/file. StorageFile targetFile = await getTargetPathAsync().ConfigureAwait(false); diff --git a/Logging/Logging.csproj b/Logging/Logging.csproj index c466bef41..a35b3e79c 100644 --- a/Logging/Logging.csproj +++ b/Logging/Logging.csproj @@ -4,15 +4,15 @@ Debug AnyCPU - {53BB5463-9646-4F79-BE97-3D73473AAD92} + {0C1FB090-4492-441D-B182-C999DF71F917} Library Properties Logging Logging - de-DE + en-US UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -26,6 +26,7 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest AnyCPU @@ -35,6 +36,7 @@ TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest x86 @@ -43,9 +45,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x86 false prompt + latest x86 @@ -54,9 +56,9 @@ true ;2008 pdbonly - x86 false prompt + latest ARM @@ -65,9 +67,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - ARM false prompt + latest ARM @@ -76,9 +78,31 @@ true ;2008 pdbonly - ARM false prompt + latest + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + latest + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + latest x64 @@ -87,9 +111,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x64 false prompt + latest x64 @@ -98,25 +122,24 @@ true ;2008 pdbonly - x64 false prompt + latest PackageReference - - + - 6.2.2 + 6.2.3 4.5.11 diff --git a/Logging/Properties/AssemblyInfo.cs b/Logging/Properties/AssemblyInfo.cs index eb56bee48..23adf979a 100644 --- a/Logging/Properties/AssemblyInfo.cs +++ b/Logging/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Logging")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Logging/Properties/Logging.rd.xml b/Logging/Properties/Logging.rd.xml index 0de744a6d..f3b106ce5 100644 --- a/Logging/Properties/Logging.rd.xml +++ b/Logging/Properties/Logging.rd.xml @@ -1,33 +1,33 @@ - + diff --git a/Push_App_Server/Classes/DataWriter.cs b/Push_App_Server/Classes/DataWriter.cs index 7f571bd7e..c2dd7ec5e 100644 --- a/Push_App_Server/Classes/DataWriter.cs +++ b/Push_App_Server/Classes/DataWriter.cs @@ -37,22 +37,22 @@ public DataWriter(XMPPClient client) #region --Set-, Get- Methods-- private bool getChannelSuccess() { - return Settings.getSettingBoolean(SettingsConsts.PUSH_CHANNEL_SEND_SUCCESS + client.getXMPPAccount().getIdAndDomain()); + return Settings.getSettingBoolean(SettingsConsts.PUSH_CHANNEL_SEND_SUCCESS + client.getXMPPAccount().getBareJid()); } private void setChannelSuccess(bool success) { - Settings.setSetting(SettingsConsts.PUSH_CHANNEL_SEND_SUCCESS + client.getXMPPAccount().getIdAndDomain(), success); + Settings.setSetting(SettingsConsts.PUSH_CHANNEL_SEND_SUCCESS + client.getXMPPAccount().getBareJid(), success); } private string getOldChannelTokenUrl() { - return Settings.getSettingString(SettingsConsts.PUSH_CHANNEL_TOKEN_URL + client.getXMPPAccount().getIdAndDomain()); + return Settings.getSettingString(SettingsConsts.PUSH_CHANNEL_TOKEN_URL + client.getXMPPAccount().getBareJid()); } private void setChannelTokenUrl(string url) { - Settings.setSetting(SettingsConsts.PUSH_CHANNEL_TOKEN_URL + client.getXMPPAccount().getIdAndDomain(), url); + Settings.setSetting(SettingsConsts.PUSH_CHANNEL_TOKEN_URL + client.getXMPPAccount().getBareJid(), url); } #endregion @@ -162,7 +162,7 @@ public async Task requestChannelAsync() private string getMessage(string url) { XElement n = new XElement("push"); - n.Add(new XAttribute("clientId", client.getXMPPAccount().getIdAndDomain())); + n.Add(new XAttribute("clientId", client.getXMPPAccount().getBareJid())); n.Add(new XAttribute("pushChannel", url)); return n.ToString(); } diff --git a/Push_App_Server/Classes/PushConnection.cs b/Push_App_Server/Classes/PushConnection.cs index c4b03fae1..1277878d7 100644 --- a/Push_App_Server/Classes/PushConnection.cs +++ b/Push_App_Server/Classes/PushConnection.cs @@ -20,7 +20,11 @@ public class PushConnection : AbstractConnection2 /// /// A dummy XMPPAccount its only purpose it is to allow using the TCPConnection. /// - private static readonly XMPPAccount DUMMY_XMPP_ACCOUNT = new XMPPAccount(null, Consts.PUSH_SERVER_ADDRESS, Consts.PORT); + private static readonly XMPPAccount DUMMY_XMPP_ACCOUNT = new XMPPAccount(null) + { + serverAddress = Consts.PUSH_SERVER_ADDRESS, + port = Consts.PORT + }; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ diff --git a/Push_App_Server/Properties/AssemblyInfo.cs b/Push_App_Server/Properties/AssemblyInfo.cs index a88f59260..f728f3d10 100644 --- a/Push_App_Server/Properties/AssemblyInfo.cs +++ b/Push_App_Server/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Push_App_Server")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Push_App_Server/Properties/Push_App_Server.rd.xml b/Push_App_Server/Properties/Push_App_Server.rd.xml index 97e98d529..b2652f1a5 100644 --- a/Push_App_Server/Properties/Push_App_Server.rd.xml +++ b/Push_App_Server/Properties/Push_App_Server.rd.xml @@ -1,33 +1,33 @@ - + diff --git a/Push_App_Server/Push_App_Server.csproj b/Push_App_Server/Push_App_Server.csproj index f36ff7bda..c4189c33d 100644 --- a/Push_App_Server/Push_App_Server.csproj +++ b/Push_App_Server/Push_App_Server.csproj @@ -4,15 +4,15 @@ Debug AnyCPU - {A8DA7BD0-DC43-4F30-AB1E-3FCF942274E4} + {C8F34602-1D87-428C-836A-E4295C4C15D8} Library Properties Push_App_Server Push_App_Server - de-DE + en-US UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -43,7 +43,6 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x86 false prompt @@ -54,7 +53,6 @@ true ;2008 pdbonly - x86 false prompt @@ -65,7 +63,6 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - ARM false prompt @@ -76,7 +73,26 @@ true ;2008 pdbonly - ARM + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly false prompt @@ -87,7 +103,6 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x64 false prompt @@ -98,7 +113,6 @@ true ;2008 pdbonly - x64 false prompt @@ -106,7 +120,6 @@ PackageReference - @@ -116,20 +129,24 @@ - 6.2.2 + 6.2.3 - {cb58ce53-8e53-49c8-9936-15fe2ea6a7eb} + {58925bf1-5b7d-48d9-b411-126cbefda98a} Data_Manager2 - {53bb5463-9646-4f79-be97-3d73473aad92} + {0c1fb090-4492-441d-b182-c999df71f917} Logging + + {c4af94da-11cf-4147-b5b7-4b2dc7c624a0} + Shared + - {899fb043-af8b-4294-95b6-1482c79459ac} + {64b9a47f-d404-4d0b-a34a-bec280c5ff6b} XMPP_API diff --git a/README.md b/README.md index 98125c3b9..f25dcc12e 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,11 @@ An alpha version of the app is available in the Windows Store [LINK](https://www [Here](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) you can find more information about: How to install UWP apps, using the developer mode. ## Examples: - -

- - + + + + + ## References: This project wouldn’t be possible without the great work of all those people working on the libraries used by UWPX. diff --git a/Shared/Classes/AbstractDataTemplate.cs b/Shared/Classes/AbstractDataTemplate.cs new file mode 100644 index 000000000..3c087701f --- /dev/null +++ b/Shared/Classes/AbstractDataTemplate.cs @@ -0,0 +1,75 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Shared.Classes +{ + /// + /// Based on: https://github.com/Microsoft/Windows-appsample-trafficapp/blob/master/LocationHelper/BindableBase.cs + /// + public abstract class AbstractDataTemplate : INotifyPropertyChanged + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Decides whether the property changed event should be fired in the UI thread. + /// Default: true + /// + protected bool invokeInUiThread = true; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (Equals(storage, value)) + { + return false; + } + + storage = value; + OnPropertyChanged(propertyName); + return true; + } + + protected virtual async void OnPropertyChanged([CallerMemberName] string name = "") + { + if (invokeInUiThread) + { + // Make sure we call the PropertyChanged event from the UI thread: + await SharedUtils.CallDispatcherAsync(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name))); + } + else + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + } + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Shared/Classes/Collections/CustomObservableCollection.cs b/Shared/Classes/Collections/CustomObservableCollection.cs new file mode 100644 index 000000000..8491af089 --- /dev/null +++ b/Shared/Classes/Collections/CustomObservableCollection.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; + +namespace Shared.Classes.Collections +{ + /// + /// Based on: https://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each/45364074#45364074 + /// + public class CustomObservableCollection : ObservableCollection + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + private const string CountName = nameof(Count); + private const string IndexerName = "Item[]"; + public readonly bool INVOKE_IN_UI_THREAD; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public CustomObservableCollection(bool invokeInUiThread) : base() + { + this.INVOKE_IN_UI_THREAD = invokeInUiThread; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + /// + /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). + /// + public void AddRange(IEnumerable collection) + { + if (collection is null) + { + throw new ArgumentNullException(nameof(collection)); + } + + if (collection is ICollection list) + { + if (list.Count == 0) return; + } + else if (!collection.Any()) + { + return; + } + else + { + list = new List(collection); + } + + CheckReentrancy(); + + int startIndex = Count; + foreach (var i in collection) + { + Items.Add(i); + } + + NotifyProperties(); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list is IList ? list : list.ToList(), startIndex)); + } + + #endregion + + #region --Misc Methods (Private)-- + private void NotifyProperties(bool count = true) + { + if (count) + { + OnPropertyChanged(new PropertyChangedEventArgs(CountName)); + } + OnPropertyChanged(new PropertyChangedEventArgs(IndexerName)); + } + + private void OnCollectionReset() => OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + #endregion + + #region --Misc Methods (Protected)-- + protected override void RemoveItem(int index) + { + if (Items[index] is INotifyPropertyChanged i) + { + i.PropertyChanged -= CustomObservableCollection_PropertyChanged; + } + base.RemoveItem(index); + } + + protected override void InsertItem(int index, T item) + { + if (item is INotifyPropertyChanged i) + { + i.PropertyChanged += CustomObservableCollection_PropertyChanged; + } + base.InsertItem(index, item); + } + + protected override void SetItem(int index, T item) + { + if (Items[index] is INotifyPropertyChanged i) + { + i.PropertyChanged -= CustomObservableCollection_PropertyChanged; + } + if (item is INotifyPropertyChanged iNew) + { + iNew.PropertyChanged += CustomObservableCollection_PropertyChanged; + } + (item as INotifyPropertyChanged).PropertyChanged += CustomObservableCollection_PropertyChanged; + base.SetItem(index, item); + } + + protected async override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (INVOKE_IN_UI_THREAD) + { + await SharedUtils.CallDispatcherAsync(() => base.OnCollectionChanged(e)); + } + else + { + base.OnCollectionChanged(e); + } + } + + protected async override void OnPropertyChanged(PropertyChangedEventArgs e) + { + if (INVOKE_IN_UI_THREAD) + { + await SharedUtils.CallDispatcherAsync(() => base.OnPropertyChanged(e)); + } + else + { + base.OnPropertyChanged(e); + } + } + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void CustomObservableCollection_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + OnPropertyChanged(e); + } + + #endregion + } +} diff --git a/Thread_Save_Components/Classes/Collections/ITimedEntry.cs b/Shared/Classes/Collections/ITimedEntry.cs similarity index 58% rename from Thread_Save_Components/Classes/Collections/ITimedEntry.cs rename to Shared/Classes/Collections/ITimedEntry.cs index 5998fc1c9..156e73fff 100644 --- a/Thread_Save_Components/Classes/Collections/ITimedEntry.cs +++ b/Shared/Classes/Collections/ITimedEntry.cs @@ -1,4 +1,4 @@ -namespace Thread_Save_Components.Classes.Collections +namespace Shared.Classes.Collections { public interface ITimedEntry { diff --git a/Thread_Save_Components/Classes/Collections/TSTimedList.cs b/Shared/Classes/Collections/TSTimedList.cs similarity index 98% rename from Thread_Save_Components/Classes/Collections/TSTimedList.cs rename to Shared/Classes/Collections/TSTimedList.cs index 6408c1a94..2b17006b3 100644 --- a/Thread_Save_Components/Classes/Collections/TSTimedList.cs +++ b/Shared/Classes/Collections/TSTimedList.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading; -namespace Thread_Save_Components.Classes.Collections +namespace Shared.Classes.Collections { public class TSTimedList { diff --git a/Thread_Save_Components/Classes/Collections/TimedListEntry.cs b/Shared/Classes/Collections/TimedListEntry.cs similarity index 95% rename from Thread_Save_Components/Classes/Collections/TimedListEntry.cs rename to Shared/Classes/Collections/TimedListEntry.cs index 138e54a0f..1b055fe41 100644 --- a/Thread_Save_Components/Classes/Collections/TimedListEntry.cs +++ b/Shared/Classes/Collections/TimedListEntry.cs @@ -1,6 +1,6 @@ using System; -namespace Thread_Save_Components.Classes.Collections +namespace Shared.Classes.Collections { public class TimedListEntry : ITimedEntry { @@ -34,7 +34,7 @@ public TimedListEntry(T item) #region --Misc Methods (Public)-- public bool canGetRemoved() { - if(item is ITimedEntry) + if (item is ITimedEntry) { return (item as ITimedEntry).canGetRemoved(); } diff --git a/Shared/Classes/Network/AbstractDownloadableObject.cs b/Shared/Classes/Network/AbstractDownloadableObject.cs new file mode 100644 index 000000000..c63bb0df9 --- /dev/null +++ b/Shared/Classes/Network/AbstractDownloadableObject.cs @@ -0,0 +1,96 @@ +namespace Shared.Classes.Network +{ + public abstract class AbstractDownloadableObject : AbstractDataTemplate + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + protected DownloadState _State; + /// + /// The current state of the download. + /// + public DownloadState State + { + get { return _State; } + set { SetProperty(ref _State, value); } + } + protected double _Progress; + /// + /// The download progress in percent. + /// + public double Progress + { + get { return _Progress; } + set { SetProperty(ref _Progress, value); } + } + protected string _SourceUrl; + /// + /// The url where the object should get downloaded from. + /// + public string SourceUrl + { + get { return _SourceUrl; } + set { SetProperty(ref _SourceUrl, value); } + } + protected string _TargetFolderPath; + /// + /// The target folder path, where the downloaded object should get saved to. + /// E.g.: C:\Program Files\Git + /// + public string TargetFolderPath + { + get { return _TargetFolderPath; } + set { SetProperty(ref _TargetFolderPath, value); } + } + protected string _TargetFileName; + /// + /// The name of the downloaded object with extension. + /// E.g.: file.png + /// + public string TargetFileName + { + get { return _TargetFileName; } + set { SetProperty(ref _TargetFileName, value); } + } + protected DownloadError _Error; + /// + /// The error code if one occurred. + /// + public DownloadError Error + { + get { return _Error; } + set { SetProperty(ref _Error, value); } + } + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Shared/Classes/Network/DownloadError.cs b/Shared/Classes/Network/DownloadError.cs new file mode 100644 index 000000000..9cffed060 --- /dev/null +++ b/Shared/Classes/Network/DownloadError.cs @@ -0,0 +1,8 @@ +namespace Shared.Classes.Network +{ + public enum DownloadError + { + FAILED_TO_CREATE_LOCAL_PATH, + INVALID_STATUS_CODE + } +} diff --git a/Shared/Classes/Network/DownloadHandler.cs b/Shared/Classes/Network/DownloadHandler.cs new file mode 100644 index 000000000..a1e5634e5 --- /dev/null +++ b/Shared/Classes/Network/DownloadHandler.cs @@ -0,0 +1,306 @@ +using Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Windows.Storage; + +namespace Shared.Classes.Network +{ + public class DownloadHandler : IDisposable + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public const int MAX_CONCURRENT_DOWNLOADS = 2; + private readonly Task[] DOWNLOADER = new Task[MAX_CONCURRENT_DOWNLOADS]; + private readonly CancellationTokenSource[] DOWNLOADER_CANCELLATION_TOKENS = new CancellationTokenSource[MAX_CONCURRENT_DOWNLOADS]; + + private readonly List TO_DOWNLOAD = new List(); + private readonly List DOWNLOADING = new List(); + private readonly SemaphoreSlim DOWNLOADING_SEMA = new SemaphoreSlim(1); + private readonly SemaphoreSlim TO_DOWNLOAD_SEMA = new SemaphoreSlim(1); + private readonly SemaphoreSlim TO_DOWNLOAD_COUNT_SEMA = new SemaphoreSlim(0); + + public delegate void DownloadStateChangedHandler(AbstractDownloadableObject o, DownloadStateChangedEventArgs args); + + public event DownloadStateChangedHandler DownloadStateChanged; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public DownloadHandler() + { + StartDownloaderTasks(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + public async Task FindAsync(Predicate predicate) + { + AbstractDownloadableObject result = null; + await TO_DOWNLOAD_SEMA.WaitAsync(); + result = TO_DOWNLOAD.Find(predicate); + TO_DOWNLOAD_SEMA.Release(); + if (!(result is null)) + { + return result; + } + + DOWNLOADING_SEMA.Wait(); + result = DOWNLOADING.Find(predicate); + DOWNLOADING_SEMA.Release(); + if (!(result is null)) + { + return result; + } + return null; + } + + private void SetDownloadState(AbstractDownloadableObject o, DownloadState newState) + { + if (o.State != newState) + { + DownloadStateChangedEventArgs args = new DownloadStateChangedEventArgs(o.State, newState); + o.State = newState; + DownloadStateChanged?.Invoke(o, args); + } + } + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public async Task EnqueueDownloadAsync(AbstractDownloadableObject o) + { + SetDownloadState(o, DownloadState.QUEUED); + await TO_DOWNLOAD_SEMA.WaitAsync(); + TO_DOWNLOAD.Add(o); + TO_DOWNLOAD_SEMA.Release(); + TO_DOWNLOAD_COUNT_SEMA.Release(); + } + + public void CancelDownload(AbstractDownloadableObject o) + { + SetDownloadState(o, DownloadState.CANCELED); + } + + #endregion + + #region --Misc Methods (Private)-- + private void StartDownloaderTasks() + { + for (int i = 0; i < DOWNLOADER.Length; i++) + { + DOWNLOADER_CANCELLATION_TOKENS[i] = new CancellationTokenSource(); + DOWNLOADER[i] = StartDownlaoderTask(i); + } + } + + /// + /// Starts a new downloader Task and returns it. + /// + /// The index of the downloader Task. + /// Returns a new downloader Task. + private Task StartDownlaoderTask(int index) + { + return Task.Run(async () => + { + while (!DOWNLOADER_CANCELLATION_TOKENS[index].IsCancellationRequested) + { + try + { + // Wait until downloads are available: + await TO_DOWNLOAD_COUNT_SEMA.WaitAsync(); + Logger.Debug("Downloader task " + index + " started job."); + + // Remove one download: + await TO_DOWNLOAD_SEMA.WaitAsync(); + AbstractDownloadableObject o = TO_DOWNLOAD[0]; + TO_DOWNLOAD.RemoveAt(0); + TO_DOWNLOAD_SEMA.Release(); + + if (o.State != DownloadState.CANCELED) + { + SetDownloadState(o, DownloadState.DOWNLOADING); + o.Progress = 0; + + // Add to currently downloading: + await DOWNLOADING_SEMA.WaitAsync(); + DOWNLOADING.Add(o); + DOWNLOADING_SEMA.Release(); + + // Download: + await DownloadAsync(o); + + // Remove since the download finished: + await DOWNLOADING_SEMA.WaitAsync(); + DOWNLOADING.Remove(o); + DOWNLOADING_SEMA.Release(); + } + Logger.Debug("Downloader task " + index + " finished job."); + } + catch (Exception e) + { + Logger.Error("Downloader task " + index + " job failed with: ", e); + } + } + + }, DOWNLOADER_CANCELLATION_TOKENS[index].Token); + } + + private void CancelDownloaderTasks() + { + for (int i = 0; i < DOWNLOADER.Length; i++) + { + if (!(DOWNLOADER[i] is null)) + { + DOWNLOADER_CANCELLATION_TOKENS[i].Cancel(); + DOWNLOADER[i] = null; + } + } + } + + private async Task DownloadAsync(AbstractDownloadableObject o) + { + Logger.Info("Started downloading <" + o.TargetFileName + "> from: " + o.SourceUrl); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(o.SourceUrl); + HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); + long bytesReadTotal = 0; + + // Check that the remote file was found. + // The ContentType check is performed since a request for a non-existent image file might be redirected to a 404-page, which would yield the StatusCode "OK", even though the image was not found. + if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect) + { + StorageFile file = await GenFilePathAsync(o.TargetFolderPath, o.TargetFileName); + if (file is null) + { + o.Error = DownloadError.FAILED_TO_CREATE_LOCAL_PATH; + SetDownloadState(o, DownloadState.ERROR); + return; + } + + using (Stream inputStream = response.GetResponseStream()) + using (Stream outputStream = await file.OpenStreamForWriteAsync()) + { + byte[] buffer = new byte[4096]; + int bytesRead; + do + { + bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length); + await outputStream.WriteAsync(buffer, 0, bytesRead); + + // Update progress: + bytesReadTotal += bytesRead; + UpdateDownloadProgress(o, response.ContentLength, bytesReadTotal); + + } while (bytesRead != 0 && o.State != DownloadState.CANCELED); + } + + if (o.State == DownloadState.CANCELED) + { + Logger.Info("Canceled downloading <" + o.TargetFileName + "> from: " + o.SourceUrl); + return; + } + + SetDownloadState(o, DownloadState.DONE); + Logger.Info("Finished downloading <" + o.TargetFileName + "> from: " + o.SourceUrl); + } + else + { + o.Error = DownloadError.INVALID_STATUS_CODE; + SetDownloadState(o, DownloadState.ERROR); + Logger.Error("Unable to download image <" + o.TargetFileName + "> from: " + o.SourceUrl + "- Status code check failed: " + response.StatusCode + "(" + response.StatusDescription + ')'); + } + } + + private async Task GenFilePathAsync(string folderPath, string fileName) + { + //CreateDirectoryRecursively(folderPath); + StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folderPath); + if (!(folder is null)) + { + return await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); + } + Logger.Error("Failed to get folder from path: " + folderPath); + return null; + } + + /// + /// Creates all directories for the given path recursively. + /// Based on: https://stackoverflow.com/questions/10941657/creating-files-recursively-creating-directories + /// + /// The absolute directory path. + private void CreateDirectoryRecursively(string path) + { + string[] pathParts = path.Split('\\'); + + for (int i = 0; i < pathParts.Length; i++) + { + if (i > 0) + { + pathParts[i] = Path.Combine(pathParts[i - 1], pathParts[i]); + } + + if (!Directory.Exists(pathParts[i])) + { + Directory.CreateDirectory(pathParts[i]); + Logger.Debug("Created directory: " + pathParts[i]); + } + } + } + + /// + /// Calculates the new download progress and sets it for the given AbstractDownloadableObject. + /// + /// The AbstractDownloadableObject. + /// The total size of the download in bytes. + /// How many bytes have been downloaded? + /// Returns the last progress update percentage. + private double UpdateDownloadProgress(AbstractDownloadableObject o, long totalSize, long bytesDownloadedTotal) + { + o.Progress = bytesDownloadedTotal / ((double)totalSize); + return o.Progress; + } + + public void Dispose() + { + // Cancel all downloader tasks: + CancelDownloaderTasks(); + + // Clear download queue: + TO_DOWNLOAD_SEMA.Wait(); + foreach (AbstractDownloadableObject o in TO_DOWNLOAD) + { + SetDownloadState(o, DownloadState.NOT_QUEUED); + } + TO_DOWNLOAD.Clear(); + TO_DOWNLOAD_SEMA.Release(); + + // Stop all downloads: + DOWNLOADING_SEMA.Wait(); + foreach (AbstractDownloadableObject o in DOWNLOADING) + { + if (o.State != DownloadState.DONE && o.State != DownloadState.ERROR) + { + SetDownloadState(o, DownloadState.NOT_QUEUED); + } + } + DOWNLOADING.Clear(); + DOWNLOADING_SEMA.Release(); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Shared/Classes/Network/DownloadState.cs b/Shared/Classes/Network/DownloadState.cs new file mode 100644 index 000000000..56fc8e4bd --- /dev/null +++ b/Shared/Classes/Network/DownloadState.cs @@ -0,0 +1,30 @@ +namespace Shared.Classes.Network +{ + public enum DownloadState + { + /// + /// The object is not in the download queue. + /// + NOT_QUEUED, + /// + /// The object is in the download queue and ready to be downloaded. + /// + QUEUED, + /// + /// The object is being downloaded. + /// + DOWNLOADING, + /// + /// The download finished without any errors. + /// + DONE, + /// + /// The download ended with an error. + /// + ERROR, + /// + /// The download has been canceled. + /// + CANCELED, + } +} diff --git a/Data_Manager2/Classes/Events/DownloadStateChangedEventArgs.cs b/Shared/Classes/Network/DownloadStateChangedEventArgs.cs similarity index 75% rename from Data_Manager2/Classes/Events/DownloadStateChangedEventArgs.cs rename to Shared/Classes/Network/DownloadStateChangedEventArgs.cs index db892274d..e45994e65 100644 --- a/Data_Manager2/Classes/Events/DownloadStateChangedEventArgs.cs +++ b/Shared/Classes/Network/DownloadStateChangedEventArgs.cs @@ -1,26 +1,21 @@ -using Data_Manager2.Classes; -using System; +using System; -namespace Data_Manager2.Classes.Events +namespace Shared.Classes.Network { public class DownloadStateChangedEventArgs : EventArgs { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public readonly DownloadState STATE; + public readonly DownloadState OLD_STATE; + public readonly DownloadState NEW_STATE; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 18/12/2017 Created [Fabian Sauter] - /// - public DownloadStateChangedEventArgs(DownloadState state) + public DownloadStateChangedEventArgs(DownloadState oldState, DownloadState newState) { - this.STATE = state; + this.OLD_STATE = oldState; + this.NEW_STATE = newState; } #endregion diff --git a/Thread_Save_Components/Classes/SQLite/AbstractDBManager.cs b/Shared/Classes/SQLite/AbstractDBManager.cs similarity index 86% rename from Thread_Save_Components/Classes/SQLite/AbstractDBManager.cs rename to Shared/Classes/SQLite/AbstractDBManager.cs index 466675356..0c82cb6f1 100644 --- a/Thread_Save_Components/Classes/SQLite/AbstractDBManager.cs +++ b/Shared/Classes/SQLite/AbstractDBManager.cs @@ -3,7 +3,7 @@ using System.IO; using Windows.Storage; -namespace Thread_Save_Components.Classes.SQLite +namespace Shared.Classes.SQLite { public abstract class AbstractDBManager { @@ -82,21 +82,6 @@ protected void deleteDB() /// protected abstract void createTables(); - /// - /// Inserts or replaces the given object into the db. - /// - protected virtual void update(object obj) - { - try - { - dB.InsertOrReplace(obj); - } - catch (Exception e) - { - Logger.Error("Error in update", e); - } - } - #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- diff --git a/Thread_Save_Components/Classes/SQLite/TSSQLiteConnection.cs b/Shared/Classes/SQLite/TSSQLiteConnection.cs similarity index 57% rename from Thread_Save_Components/Classes/SQLite/TSSQLiteConnection.cs rename to Shared/Classes/SQLite/TSSQLiteConnection.cs index 3a6660029..0887b5b95 100644 --- a/Thread_Save_Components/Classes/SQLite/TSSQLiteConnection.cs +++ b/Shared/Classes/SQLite/TSSQLiteConnection.cs @@ -1,9 +1,10 @@ -using SQLite; +using Logging; +using SQLite; using System; using System.Collections.Generic; using System.Threading; -namespace Thread_Save_Components.Classes.SQLite +namespace Shared.Classes.SQLite { public class TSSQLiteConnection : IDisposable { @@ -49,7 +50,7 @@ public TSSQLiteConnection(string dBPath) #region --Misc Methods (Public)-- public SQLiteCommand CreateCommand(string cmdText, params object[] args) { - return DB_CONNECTIONS[DB_PATH].Item3.CreateCommand(cmdText, args); + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.CreateCommand(cmdText, args)); } public void BeginTransaction() @@ -58,7 +59,7 @@ public void BeginTransaction() connection.Item2.WaitOne(); if (connection.Item1) { - connection.Item3.BeginTransaction(); + SharedUtils.RetryOnException(() => connection.Item3.BeginTransaction()); DB_CONNECTIONS[DB_PATH] = new Tuple(true, connection.Item2, connection.Item3); } connection.Item2.ReleaseMutex(); @@ -66,39 +67,74 @@ public void BeginTransaction() public int DeleteAll() { - return DB_CONNECTIONS[DB_PATH].Item3.DeleteAll(); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.DeleteAll()); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB delete all!", e); + return -1; + } } public List ExecuteCommand(bool readOnly, SQLiteCommand cmd) where T : new() { - return cmd.ExecuteQuery(); + try + { + return SharedUtils.RetryOnException(() => cmd.ExecuteQuery()); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB execute command!", e); + return new List(); + } } public int InsertOrReplace(object obj) { - // Not using DB_CONNECTIONS[DB_PATH].Item3.InsertOrReplace(obj); to prevent exceptions (https://github.com/praeclarum/sqlite-net/issues/761): - BeginTransaction(); - DB_CONNECTIONS[DB_PATH].Item3.Delete(obj); - int i = DB_CONNECTIONS[DB_PATH].Item3.Insert(obj); - Commit(); - return i; + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.InsertOrReplace(obj)); ; + } + catch (Exception e) + { + Logger.Error("Failed to execute DB insert or replace!", e); + return -1; + } } public int Insert(object obj) { - return DB_CONNECTIONS[DB_PATH].Item3.Insert(obj); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.Insert(obj)); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB insert!", e); + return -1; + } } public int InsertAll(IEnumerable objects, bool runInTransaction = true) { - return DB_CONNECTIONS[DB_PATH].Item3.InsertAll(objects); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.InsertAll(objects)); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB insert all!", e); + return -1; + } } public void Close() { DB_CONNECTION_MUTEX.WaitOne(); Tuple connection = DB_CONNECTIONS[DB_PATH]; - connection.Item3.Close(); + SharedUtils.RetryOnException(() => connection.Item3.Close()); connection.Item2.Dispose(); DB_CONNECTIONS.Remove(DB_PATH); DB_CONNECTION_MUTEX.ReleaseMutex(); @@ -106,7 +142,15 @@ public void Close() public int Execute(string query, params object[] args) { - return DB_CONNECTIONS[DB_PATH].Item3.Execute(query, args); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.Execute(query, args)); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB execute!", e); + return -1; + } } public void Commit() @@ -115,7 +159,7 @@ public void Commit() connection.Item2.WaitOne(); if (connection.Item1) { - connection.Item3.Commit(); + SharedUtils.RetryOnException(() => connection.Item3.Commit()); DB_CONNECTIONS[DB_PATH] = new Tuple(false, connection.Item2, connection.Item3); } connection.Item2.ReleaseMutex(); @@ -124,35 +168,58 @@ public void Commit() /// Unused/placeholder! public List Query(bool readOnly, string query, params object[] args) where T : new() { - return DB_CONNECTIONS[DB_PATH].Item3.Query(query, args); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.Query(query, args)); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB query!", e); + return new List(); + } } public CreateTableResult CreateTable() where T : new() { - return DB_CONNECTIONS[DB_PATH].Item3.CreateTable(); + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.CreateTable()); } public int DropTable() where T : new() { - return DB_CONNECTIONS[DB_PATH].Item3.DropTable(); + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.DropTable()); } public CreateTableResult RecreateTable() where T : new() { - DB_CONNECTIONS[DB_PATH].Item3.DropTable(); - return DB_CONNECTIONS[DB_PATH].Item3.CreateTable(); + SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.DropTable()); + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.CreateTable()); } public int Delete(object objectToDelete) { - return DB_CONNECTIONS[DB_PATH].Item3.Delete(objectToDelete); + try + { + return SharedUtils.RetryOnException(() => DB_CONNECTIONS[DB_PATH].Item3.Delete(objectToDelete)); + } + catch (Exception e) + { + Logger.Error("Failed to execute DB delete!", e); + return -1; + } } public void Dispose() { foreach (var connection in DB_CONNECTIONS) { - connection.Value.Item3?.Close(); + try + { + connection.Value.Item3?.Close(); + } + catch (Exception e) + { + Logger.Error("Failed to close DB!", e); + } } } diff --git a/Shared/Classes/SharedUtils.cs b/Shared/Classes/SharedUtils.cs new file mode 100644 index 000000000..7d8c7ccc3 --- /dev/null +++ b/Shared/Classes/SharedUtils.cs @@ -0,0 +1,140 @@ +using Logging; +using System; +using System.Threading.Tasks; +using Windows.ApplicationModel.Core; +using Windows.UI.Core; + +namespace Shared.Classes +{ + public static class SharedUtils + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + /// + /// Calls the UI thread dispatcher and executes the given callback on it. + /// + /// The callback that should be executed in the UI thread. + public static async Task CallDispatcherAsync(DispatchedHandler callback) + { + if (CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess) + { + callback(); + } + else + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, callback); + } + } + + /// + /// Retries the given action once on exception. + /// + /// The action that should get executed. + public static void RetryOnException(Action action) + { + RetryOnException(action, 1); + } + + /// + /// Retries the given action retryCount times on exception. + /// + /// The action that should get executed. + /// How many times should we try to execute the given action? + public static void RetryOnException(Action action, int retryCount) + { + int i = 0; + do + { + try + { + action(); + } + catch (Exception e) + { + if (retryCount <= i) + { + throw e; + } + else + { + Logger.Error("Retry exception: ", e); + i++; + } + } + } while (true); + } + + /// + /// Retries the given function once on exception. + /// + /// The function that should get executed. + /// How many times should we try to execute the given action? + /// The return value of the given function. + public static T RetryOnException(Func funct) + { + return RetryOnException(funct, 1); + } + + /// + /// Retries the given function retryCount times on exception. + /// + /// The function that should get executed. + /// How many times should we try to execute the given action? + /// The return value of the given function. + public static T RetryOnException(Func funct, int retryCount) + { + int i = 0; + do + { + try + { + return funct(); + } + catch (Exception e) + { + if (retryCount <= i) + { + throw e; + } + else + { + Logger.Error("Retry exception: ", e); + i++; + } + } + } while (true); + } + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/Thread_Save_Components/Classes/Threading/MySemaphoreSlim.cs b/Shared/Classes/Threading/MySemaphoreSlim.cs similarity index 97% rename from Thread_Save_Components/Classes/Threading/MySemaphoreSlim.cs rename to Shared/Classes/Threading/MySemaphoreSlim.cs index 51745ec98..d7d72d2e7 100644 --- a/Thread_Save_Components/Classes/Threading/MySemaphoreSlim.cs +++ b/Shared/Classes/Threading/MySemaphoreSlim.cs @@ -1,6 +1,6 @@ using System.Threading; -namespace Thread_Save_Components.Classes.Threading +namespace Shared.Classes.Threading { public class MySemaphoreSlim : SemaphoreSlim { diff --git a/Thread_Save_Components/Properties/AssemblyInfo.cs b/Shared/Properties/AssemblyInfo.cs similarity index 85% rename from Thread_Save_Components/Properties/AssemblyInfo.cs rename to Shared/Properties/AssemblyInfo.cs index d91166eed..7a18a0164 100644 --- a/Thread_Save_Components/Properties/AssemblyInfo.cs +++ b/Shared/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Thread_Save_Components")] +[assembly: AssemblyTitle("Shared")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Thread_Save_Components")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyProduct("Shared")] +[assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Shared/Properties/Shared.rd.xml b/Shared/Properties/Shared.rd.xml new file mode 100644 index 000000000..c9e8f29a3 --- /dev/null +++ b/Shared/Properties/Shared.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/Thread_Save_Components/Thread_Save_Components.csproj b/Shared/Shared.csproj similarity index 72% rename from Thread_Save_Components/Thread_Save_Components.csproj rename to Shared/Shared.csproj index 0fddc9235..f73d1043f 100644 --- a/Thread_Save_Components/Thread_Save_Components.csproj +++ b/Shared/Shared.csproj @@ -4,15 +4,15 @@ Debug AnyCPU - {60938C69-A2D7-4C27-8B73-84C203BD4787} + {C4AF94DA-11CF-4147-B5B7-4B2DC7C624A0} Library Properties - Thread_Save_Components - Thread_Save_Components - de-DE + Shared + Shared + en-US UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17763.0 + 10.0.15063.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -26,6 +26,7 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest AnyCPU @@ -35,6 +36,7 @@ TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 + latest x86 @@ -43,9 +45,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x86 false prompt + latest x86 @@ -54,9 +56,9 @@ true ;2008 pdbonly - x86 false prompt + latest ARM @@ -65,9 +67,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - ARM false prompt + latest ARM @@ -76,9 +78,31 @@ true ;2008 pdbonly - ARM false prompt + latest + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + latest + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + latest x64 @@ -87,9 +111,9 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP ;2008 full - x64 false prompt + latest x64 @@ -98,27 +122,34 @@ true ;2008 pdbonly - x64 false prompt + latest PackageReference - + + - + + + + + + + - + - 6.2.2 + 6.2.3 1.5.231 @@ -126,7 +157,7 @@ - {53bb5463-9646-4f79-be97-3d73473aad92} + {0c1fb090-4492-441d-b182-c999df71f917} Logging diff --git a/Thread_Save_Components/Properties/Thread_Save_Components.rd.xml b/Thread_Save_Components/Properties/Thread_Save_Components.rd.xml deleted file mode 100644 index dea495a51..000000000 --- a/Thread_Save_Components/Properties/Thread_Save_Components.rd.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - diff --git a/UWP XMPP Client/App.xaml b/UWP XMPP Client/App.xaml deleted file mode 100644 index 28586ec44..000000000 --- a/UWP XMPP Client/App.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/App.xaml.cs b/UWP XMPP Client/App.xaml.cs deleted file mode 100644 index 17a852a4b..000000000 --- a/UWP XMPP Client/App.xaml.cs +++ /dev/null @@ -1,428 +0,0 @@ -using System; -using UWP_XMPP_Client.Pages; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; -using UWP_XMPP_Client.Classes; -using Data_Manager2.Classes; -using Data_Manager2.Classes.DBManager; -using System.Threading.Tasks; -using Logging; -using Microsoft.AppCenter.Push; -using Microsoft.AppCenter.Crashes; -using UWP_XMPP_Client.Dialogs; -using Windows.ApplicationModel.Core; -using Windows.UI.Core; -using Data_Manager2.Classes.Toast; -using Windows.UI.Notifications; -using Data_Manager2.Classes.DBTables; -using System.Text; -using Microsoft.AppCenter.Analytics; - -namespace UWP_XMPP_Client -{ - sealed partial class App : Application - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - private readonly string APP_CENTER_SECRET = "523e7039-f6cb-4bf1-9000-53277ed97c53"; - - /// - /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. - /// - public static ElementTheme RootTheme - { - get - { - if (Window.Current.Content is FrameworkElement rootElement) - { - return rootElement.RequestedTheme; - } - - return ElementTheme.Default; - } - set - { - if (Window.Current.Content is FrameworkElement rootElement) - { - rootElement.RequestedTheme = value; - } - Settings.setSetting(SettingsConsts.APP_REQUESTED_THEME, value.ToString()); - } - } - - private bool isRunning; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - public App() - { - this.isRunning = false; - - // Setup App Center crashes, push: - setupAppCenter(); - - // Init buy content helper: - BuyContentHelper.INSTANCE.init(); - - this.InitializeComponent(); - this.Suspending += OnSuspending; - this.Resuming += App_Resuming; - this.UnhandledException += App_UnhandledException; - - // Perform App update tasks if necessary: - AppUpdateHandler.onAppStart(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - /// - /// Sets up App Center crash and push support. - /// - private void setupAppCenter() - { - try - { - Microsoft.AppCenter.AppCenter.Start(APP_CENTER_SECRET, typeof(Crashes)); - if (Settings.getSettingBoolean(SettingsConsts.DISABLE_CRASH_REPORTING)) - { - Crashes.Instance.InstanceEnabled = false; - Logger.Info("AppCenter crash reporting is disabled."); - } - - Microsoft.AppCenter.AppCenter.Start(APP_CENTER_SECRET, typeof(Analytics)); - if (Settings.getSettingBoolean(SettingsConsts.DISABLE_ANALYTICS)) - { - Analytics.SetEnabledAsync(false); - Logger.Info("AppCenter analytics are disabled."); - } -#if DEBUG - // Only enable push for debug builds: - Microsoft.AppCenter.AppCenter.Start(APP_CENTER_SECRET, typeof(Push)); -#endif - - if (!Microsoft.AppCenter.AppCenter.Configured) - { - Push.PushNotificationReceived -= Push_PushNotificationReceived; - Push.PushNotificationReceived += Push_PushNotificationReceived; - } - } - catch (Exception e) - { - Logger.Error("Failed to start APPCenter!", e); - throw e; - } - Logger.Info("App Center crash reporting registered."); - Logger.Info("App Center push registered."); - } - - /// - /// Inits all db managers in a new task to force event subscriptions. - /// - private void initAllDBManagers() - { - Task.Run(() => - { - AccountDBManager.INSTANCE.initManager(); - ChatDBManager.INSTANCE.initManager(); - DiscoDBManager.INSTANCE.initManager(); - ImageDBManager.INSTANCE.initManager(); - MUCDBManager.INSTANCE.initManager(); - }); - } - - /// - /// Sets the log level for the logger class. - /// - private void initLogLevel() - { - object o = Settings.getSetting(SettingsConsts.LOG_LEVEL); - if (o is int) - { - Logger.logLevel = (LogLevel)o; - } - else - { - Settings.setSetting(SettingsConsts.LOG_LEVEL, (int)LogLevel.INFO); - Logger.logLevel = LogLevel.INFO; - } - } - - private async Task onActivatedOrLaunchedAsync(IActivatedEventArgs args) - { - // Prevent window from extending into title bar: - CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = false; - - // Sets the log level: - initLogLevel(); - - // Register background tasks: - Logger.Info("Registering background tasks..."); - await BackgroundTaskHelper.registerToastBackgroundTaskAsync(); - Logger.Info("Finished registering background tasks."); - - // Init all db managers to force event subscriptions: - initAllDBManagers(); - - // Set default background: - if (!Settings.getSettingBoolean(SettingsConsts.INITIALLY_STARTED)) - { - Settings.setSetting(SettingsConsts.CHAT_EXAMPLE_BACKGROUND_IMAGE_NAME, "light_bulb.jpeg"); - } - // Loads all background images into the cache: - BackgroundImageCache.loadCache(); - - // Setup push server connection: - if (!Settings.getSettingBoolean(SettingsConsts.DISABLE_PUSH)) - { - Push_App_Server.Classes.PushManager.init(); - } - - isRunning = true; - - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (!(Window.Current.Content is Frame rootFrame)) - { - // Create a Frame to act as the navigation context and navigate to the first page: - rootFrame = new Frame(); - - rootFrame.NavigationFailed += OnNavigationFailed; - - if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) - { - // TODO: Load state from previously suspended application - } - - // Place the frame in the current Window - Window.Current.Content = rootFrame; - } - - if (args is ProtocolActivatedEventArgs protocolActivationArgs) - { - Logger.Info("App activated by protocol activation with: " + protocolActivationArgs.Uri.ToString()); - - // If we're currently not on a page, navigate to the main page - if (!Settings.getSettingBoolean(SettingsConsts.INITIALLY_STARTED)) - { - rootFrame.Navigate(typeof(AddAccountPage), "App.xaml.cs"); // ToDo add arguments - } - else - { - rootFrame.Navigate(typeof(ChatPage), protocolActivationArgs); // ToDo add arguments - } - } - else if (args is ToastNotificationActivatedEventArgs toastActivationArgs) - { - Logger.Info("App activated by toast with: " + toastActivationArgs.Argument); - // If empty args, no specific action (just launch the app) - if (string.IsNullOrEmpty(toastActivationArgs.Argument)) - { - if (rootFrame.Content is null) - { - if (!Settings.getSettingBoolean(SettingsConsts.INITIALLY_STARTED)) - { - rootFrame.Navigate(typeof(AddAccountPage), "App.xaml.cs"); - } - else - { - rootFrame.Navigate(typeof(ChatPage), "App.xaml.cs"); - } - } - } - else - { - rootFrame.Navigate(typeof(ChatPage), ToastActivationArgumentParser.parseArguments(toastActivationArgs.Argument)); - } - if (rootFrame.BackStack.Count == 0) - { - rootFrame.BackStack.Add(new PageStackEntry(typeof(ChatPage), null, null)); - } - } - else if (args is LaunchActivatedEventArgs launchActivationArgs) - { - Push.CheckLaunchedFromNotification(launchActivationArgs); - - // If launched with arguments (not a normal primary tile/applist launch) - if (launchActivationArgs.Arguments.Length > 0) - { - Logger.Debug(launchActivationArgs.Arguments); - // TODO: Handle arguments for cases = launching from secondary Tile, so we navigate to the correct page - //throw new NotImplementedException(); - } - - // If we're currently not on a page, navigate to the main page - if (rootFrame.Content is null) - { - if (!Settings.getSettingBoolean(SettingsConsts.INITIALLY_STARTED)) - { - rootFrame.Navigate(typeof(AddAccountPage), "App.xaml.cs"); - } - else - { - rootFrame.Navigate(typeof(ChatPage), "App.xaml.cs"); - } - } - } - - // Set requested theme: - string themeString = Settings.getSettingString(SettingsConsts.APP_REQUESTED_THEME); - ElementTheme theme = ElementTheme.Dark; - if (themeString != null) - { - Enum.TryParse(themeString, out theme); - } - RootTheme = theme; - - Window.Current.Activate(); - - // Connect to all clients: - ConnectionHandler.INSTANCE.connectAll(); - } - - #endregion - - #region --Misc Methods (Protected)-- - protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) - { - var deferral = args.TaskInstance.GetDeferral(); - - switch (args.TaskInstance.Task.Name) - { - case BackgroundTaskHelper.TOAST_BACKGROUND_TASK_NAME: - ToastNotificationActionTriggerDetail details = args.TaskInstance.TriggerDetails as ToastNotificationActionTriggerDetail; - if (details != null) - { - initLogLevel(); - - string arguments = details.Argument; - var userInput = details.UserInput; - - Logger.Debug("App activated in background through toast with: " + arguments); - AbstractToastActivation abstractToastActivation = ToastActivationArgumentParser.parseArguments(arguments); - - if (abstractToastActivation is MarkChatAsReadToastActivation markChatAsRead) - { - ToastHelper.removeToastGroup(markChatAsRead.CHAT_ID); - ChatDBManager.INSTANCE.markAllMessagesAsRead(markChatAsRead.CHAT_ID); - } - else if (abstractToastActivation is MarkMessageAsReadToastActivation markMessageAsRead) - { - ChatDBManager.INSTANCE.markMessageAsRead(markMessageAsRead.CHAT_MESSAGE_ID); - } - else if (abstractToastActivation is SendReplyToastActivation sendReply) - { - ChatTable chat = ChatDBManager.INSTANCE.getChat(sendReply.CHAT_ID); - if (chat != null && userInput[ToastHelper.TEXT_BOX_ID] != null) - { - if (isRunning) - { - - } - else - { - - } - } - } - } - break; - - default: - break; - } - - deferral.Complete(); - } - - protected async override void OnLaunched(LaunchActivatedEventArgs args) - { - await onActivatedOrLaunchedAsync(args); - } - - protected async override void OnActivated(IActivatedEventArgs args) - { - await onActivatedOrLaunchedAsync(args); - } - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private async void OnSuspending(object sender, SuspendingEventArgs e) - { - isRunning = false; - - var deferral = e.SuspendingOperation.GetDeferral(); - // TODO re-implement transfer socket ownership: - //await ConnectionHandler.INSTANCE.transferSocketOwnershipAsync(); - - // Disconnect all clients: - await ConnectionHandler.INSTANCE.disconnectAllAsync(); - deferral.Complete(); - } - - private void App_Resuming(object sender, object e) - { - // Connect to all clients: - ConnectionHandler.INSTANCE.connectAll(); - isRunning = true; - } - - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - } - - private async void Push_PushNotificationReceived(object sender, PushNotificationReceivedEventArgs e) - { - // Add the notification message and title to the message: - StringBuilder pushSummary = new StringBuilder("Push notification received:\n"); - pushSummary.Append($"\tNotification title: {e.Title}\n"); - pushSummary.Append($"\tMessage: {e.Message}"); - - // If there is custom data associated with the notification, print the entries: - if (e.CustomData != null) - { - pushSummary.Append("\n\tCustom data:\n"); - foreach (var key in e.CustomData.Keys) - { - pushSummary.Append($"\t\t{key} : {e.CustomData[key]}\n"); - } - } - - // Log notification summary: - Logger.Info(pushSummary.ToString()); - - // Show push dialog: - if (e.CustomData.TryGetValue("markdown", out string markdownText)) - { - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => - { - AppCenterPushDialog dialog = new AppCenterPushDialog(e.Title, markdownText); - await UiUtils.showDialogAsyncQueue(dialog); - }); - } - } - - private void App_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - Logger.Error("Unhanded exception: ", e.Exception); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Classes/AppUpdateHandler.cs b/UWP XMPP Client/Classes/AppUpdateHandler.cs index ada3a46bd..f90928872 100644 --- a/UWP XMPP Client/Classes/AppUpdateHandler.cs +++ b/UWP XMPP Client/Classes/AppUpdateHandler.cs @@ -2,8 +2,8 @@ using Data_Manager2.Classes.DBManager; using Data_Manager2.Classes.DBTables; using Data_Manager2.Classes.DBTables.Omemo; +using Shared.Classes.SQLite; using System; -using Thread_Save_Components.Classes.SQLite; using Windows.ApplicationModel; using XMPP_API.Classes.Network; @@ -132,7 +132,7 @@ public static void onAppStart() Logging.Logger.Info("Started generating OMEMO keys for accounts..."); foreach (XMPPAccount account in AccountDBManager.INSTANCE.loadAllAccounts()) { - Logging.Logger.Info("Generating OMEMO keys for: " + account.getIdAndDomain()); + Logging.Logger.Info("Generating OMEMO keys for: " + account.getBareJid()); account.generateOmemoKeys(); AccountDBManager.INSTANCE.setAccount(account, false); } @@ -151,7 +151,7 @@ public static void onAppStart() AbstractDBManager.dB.RecreateTable(); foreach (XMPPAccount account in AccountDBManager.INSTANCE.loadAllAccounts()) { - Logging.Logger.Info("Reseting OMEMO keys for: " + account.getIdAndDomain()); + Logging.Logger.Info("Reseting OMEMO keys for: " + account.getBareJid()); account.omemoBundleInfoAnnounced = false; account.omemoDeviceId = 0; account.omemoKeysGenerated = false; diff --git a/UWP XMPP Client/Classes/ChatFilter.cs b/UWP XMPP Client/Classes/ChatFilter.cs index 9f7d5dcc9..6d71c6a23 100644 --- a/UWP XMPP Client/Classes/ChatFilter.cs +++ b/UWP XMPP Client/Classes/ChatFilter.cs @@ -37,15 +37,15 @@ class ChatFilter public ChatFilter(MyAdvancedCollectionView chatsACV) { this.CHATS_ACV = chatsACV; - this.PRESENCES = Settings.LOCAL_OBJECT_STORAGE_HELPER.Read(SettingsConsts.CHAT_FILTER_PRESENCES, new HashSet()); + //this.PRESENCES = Settings.LOCAL_OBJECT_STORAGE_HELPER.Read(SettingsConsts.CHAT_FILTER_PRESENCES, new HashSet()); this.chatQuery = Settings.getSettingString(SettingsConsts.CHAT_FILTER_QUERY, string.Empty); this.chatQueryLow = this.chatQuery.ToLowerInvariant(); this.chatQueryEnabled = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_QUERY_ENABLED); this.notOnline = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_NOT_ONLINE); this.notUnavailable = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_NOT_UNAVAILABLE); - this.chat = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_CHAT); - this.muc = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_MUC); + this.chat = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_CHATS_ONLY); + this.muc = Settings.getSettingBoolean(SettingsConsts.CHAT_FILTER_MUCS_ONLY); } #endregion @@ -227,7 +227,7 @@ private bool filterPresence(ChatTemplate chat) } else if (notUnavailable) { - if(chat.chat.chatType == ChatType.CHAT) + if (chat.chat.chatType == ChatType.CHAT) { return chat.chat.presence != Presence.Unavailable; } @@ -255,7 +255,7 @@ private void onFilterChanged() private void savePresences() { - Settings.LOCAL_OBJECT_STORAGE_HELPER.Save(SettingsConsts.CHAT_FILTER_PRESENCES, PRESENCES); + //Settings.LOCAL_OBJECT_STORAGE_HELPER.Save(SettingsConsts.CHAT_FILTER_PRESENCES, PRESENCES); } private void saveNotOnline() @@ -280,12 +280,12 @@ private void saveChatQueryEnabled() private void saveChat() { - Settings.setSetting(SettingsConsts.CHAT_FILTER_CHAT, chat); + Settings.setSetting(SettingsConsts.CHAT_FILTER_CHATS_ONLY, chat); } private void saveMUC() { - Settings.setSetting(SettingsConsts.CHAT_FILTER_MUC, muc); + Settings.setSetting(SettingsConsts.CHAT_FILTER_MUCS_ONLY, muc); } #endregion diff --git a/UWP XMPP Client/Classes/Events/NavigatedToMUCInfoEventArgs.cs b/UWP XMPP Client/Classes/Events/NavigatedToMUCInfoEventArgs.cs index 398cbc75f..7d0db2472 100644 --- a/UWP XMPP Client/Classes/Events/NavigatedToMUCInfoEventArgs.cs +++ b/UWP XMPP Client/Classes/Events/NavigatedToMUCInfoEventArgs.cs @@ -4,7 +4,7 @@ namespace UWP_XMPP_Client.Classes.Events { - class NavigatedToMUCInfoEventArgs : EventArgs + public class NavigatedToMUCInfoEventArgs : EventArgs { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- diff --git a/UWP XMPP Client/Classes/Events/NavigatedToUserProfileEventArgs.cs b/UWP XMPP Client/Classes/Events/NavigatedToUserProfileEventArgs.cs index 65316b476..f1228881a 100644 --- a/UWP XMPP Client/Classes/Events/NavigatedToUserProfileEventArgs.cs +++ b/UWP XMPP Client/Classes/Events/NavigatedToUserProfileEventArgs.cs @@ -4,7 +4,7 @@ namespace UWP_XMPP_Client.Classes.Events { - class NavigatedToUserProfileEventArgs : EventArgs + public class NavigatedToUserProfileEventArgs : EventArgs { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- diff --git a/UWP XMPP Client/Classes/UIUtils.cs b/UWP XMPP Client/Classes/UIUtils.cs index 139a87ce7..ea50afb9a 100644 --- a/UWP XMPP Client/Classes/UIUtils.cs +++ b/UWP XMPP Client/Classes/UIUtils.cs @@ -1,22 +1,20 @@ -using System; +using Microsoft.Toolkit.Uwp.UI.Controls; +using System; +using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Data_Manager2.Classes; -using Microsoft.Toolkit.Uwp.UI.Controls; using UWP_XMPP_Client.DataTemplates; -using UWP_XMPP_Client.Dialogs; using Windows.ApplicationModel.DataTransfer; using Windows.Foundation.Metadata; using Windows.System.Profile; using Windows.UI; +using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; using XMPP_API.Classes; -using Windows.UI.ViewManagement; -using System.Linq; -using System.Text; namespace UWP_XMPP_Client.Classes { @@ -99,22 +97,22 @@ public static SolidColorBrush getPresenceBrush(Presence presence) switch (presence) { case Presence.Online: - return (SolidColorBrush)Application.Current.Resources["PresenceOnline"]; + return (SolidColorBrush)Application.Current.Resources["PresenceOnlineBrush"]; case Presence.Chat: - return (SolidColorBrush)Application.Current.Resources["PresenceChat"]; + return (SolidColorBrush)Application.Current.Resources["PresenceChatBrush"]; case Presence.Away: - return (SolidColorBrush)Application.Current.Resources["PresenceAway"]; + return (SolidColorBrush)Application.Current.Resources["PresenceAwayBrush"]; case Presence.Xa: - return (SolidColorBrush)Application.Current.Resources["PresenceXa"]; + return (SolidColorBrush)Application.Current.Resources["PresenceXaBrush"]; case Presence.Dnd: - return (SolidColorBrush)Application.Current.Resources["PresenceDnd"]; + return (SolidColorBrush)Application.Current.Resources["PresenceDndBrush"]; default: - return (SolidColorBrush)Application.Current.Resources["PresenceUnavailable"]; + return (SolidColorBrush)Application.Current.Resources["PresenceUnavailableBrush"]; } } @@ -228,26 +226,6 @@ public static async Task launchUriAsync(Uri url) return await Windows.System.Launcher.LaunchUriAsync(url); } - public static async Task showInitialStartDialogAsync() - { - if (!Settings.getSettingBoolean(SettingsConsts.HIDE_INITIAL_START_DIALOG_ALPHA)) - { - InitialStartDialog dialog = new InitialStartDialog(); - await showDialogAsyncQueue(dialog); - Settings.setSetting(SettingsConsts.HIDE_INITIAL_START_DIALOG_ALPHA, !dialog.showOnStartup); - } - } - - public static async Task showWhatsNewDialog() - { - if (!Settings.getSettingBoolean(SettingsConsts.HIDE_WHATS_NEW_DIALOG)) - { - WhatsNewDialog dialog = new WhatsNewDialog(); - await showDialogAsyncQueue(dialog); - Settings.setSetting(SettingsConsts.HIDE_WHATS_NEW_DIALOG, !dialog.showOnStartup); - } - } - public static void addTextToClipboard(string text) { DataPackage package = new DataPackage(); diff --git a/UWP XMPP Client/Controls/AccountControl.xaml b/UWP XMPP Client/Controls/AccountControl.xaml deleted file mode 100644 index 09eb45ca9..000000000 --- a/UWP XMPP Client/Controls/AccountControl.xaml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Controls/AccountSettingsControl.xaml.cs b/UWP XMPP Client/Controls/AccountSettingsControl.xaml.cs deleted file mode 100644 index a22bf4f6f..000000000 --- a/UWP XMPP Client/Controls/AccountSettingsControl.xaml.cs +++ /dev/null @@ -1,336 +0,0 @@ -using System; -using System.Threading.Tasks; -using UWP_XMPP_Client.Classes; -using Windows.UI.Core; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes; -using XMPP_API.Classes.Network; -using Data_Manager2.Classes; -using Data_Manager2.Classes.DBManager; -using UWP_XMPP_Client.Dialogs; -using Data_Manager2.Classes.DBTables; -using Windows.UI; -using Windows.UI.Xaml.Media; - -namespace UWP_XMPP_Client.Controls -{ - public sealed partial class AccountSettingsControl : UserControl - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public XMPPAccount Account - { - get { return (XMPPAccount)GetValue(AccountProperty); } - set - { - SetValue(AccountProperty, value); - showAccount(); - } - } - public static readonly DependencyProperty AccountProperty = DependencyProperty.Register("Account", typeof(XMPPAccount), typeof(AccountSettingsControl), null); - - private XMPPClient client; - private ConnectionError lastConnectionError; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 04/09/2017 Created [Fabian Sauter] - /// - public AccountSettingsControl() - { - this.client = null; - this.lastConnectionError = null; - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private async Task showErrorDialogAsync(string text) - { - TextDialog dialog = new TextDialog(text, Localisation.getLocalizedString("error_text")); - await UiUtils.showDialogAsyncQueue(dialog); - } - - private void showAccount() - { - if (Account != null) - { - IsEnabled = false; - if (client != null) - { - client.ConnectionStateChanged -= Client_ConnectionStateChanged; - } - string accountId = Account.getIdAndDomain(); - disableAccount_tggls.IsOn = !Account.disabled; - Task.Run(() => - { - client = ConnectionHandler.INSTANCE.getClient(accountId); - - if (client != null) - { - client.ConnectionStateChanged -= Client_ConnectionStateChanged; - client.ConnectionStateChanged += Client_ConnectionStateChanged; - client.getXMPPAccount().CONNECTION_INFO.PropertyChanged -= CONNECTION_INFO_PropertyChanged; - client.getXMPPAccount().CONNECTION_INFO.PropertyChanged += CONNECTION_INFO_PropertyChanged; - Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => showConnectionState(client, client.getConnetionState(), client.getLastConnectionError())).AsTask(); - } - Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => IsEnabled = true).AsTask(); - }); - } - } - - private void replaceAccount(XMPPAccount newAccount) - { - XMPPAccount oldAccount = Account; - - Task.Run(() => AccountDBManager.INSTANCE.replaceAccount(oldAccount, newAccount)); - - Account = newAccount; - } - - private async Task saveAccountAsync() - { - if (await account_acc.isAccountVaildAsync()) - { - XMPPAccount newAccount = account_acc.getAccount(); - if (newAccount is null) - { - await showErrorDialogAsync(Localisation.getLocalizedString("invalid_jabber_id_text")); - } - else - { - replaceAccount(newAccount); - } - return newAccount != null; - } - return false; - } - - private void updateSecurityButton(XMPPClient client, ConnectionState state) - { - if (client != null && state == ConnectionState.CONNECTED) - { - if (client.getXMPPAccount().CONNECTION_INFO.tlsConnected) - { - accountSecurityStatus_tbx.Foreground = new SolidColorBrush(Colors.Green); - accountSecurityStatus_tbx.Text = "\uE72E"; - } - else - { - accountSecurityStatus_tbx.Foreground = new SolidColorBrush(Colors.Red); - accountSecurityStatus_tbx.Text = "\uE785"; - } - } - else - { - accountSecurityStatus_tbx.Foreground = UiUtils.getPresenceBrush(Presence.Unavailable); - accountSecurityStatus_tbx.Text = "\uE785"; - } - } - - private void showConnectionState(XMPPClient client, ConnectionState state, object param) - { - updateSecurityButton(client, state); - - error_tblck.Visibility = Visibility.Collapsed; - lastConnectionError = null; - - switch (state) - { - case ConnectionState.DISCONNECTED: - image_aciwp.PresenceP = Presence.Unavailable; - break; - case ConnectionState.CONNECTING: - image_aciwp.PresenceP = Presence.Chat; - break; - case ConnectionState.CONNECTED: - image_aciwp.PresenceP = Presence.Online; - break; - case ConnectionState.DISCONNECTING: - image_aciwp.PresenceP = Presence.Chat; - break; - case ConnectionState.ERROR: - image_aciwp.PresenceP = Presence.Dnd; - showError(param); - break; - default: - break; - } - } - - private void showError(object param) - { - if (param is ConnectionError connectionError) - { - lastConnectionError = connectionError; - - switch (connectionError.ERROR_CODE) - { - case ConnectionErrorCode.UNKNOWN: - error_tblck.Text = connectionError.ERROR_MESSAGE ?? ""; - break; - - case ConnectionErrorCode.SOCKET_ERROR: - error_tblck.Text = connectionError.SOCKET_ERROR.ToString(); - break; - - default: - error_tblck.Text = connectionError.ERROR_CODE.ToString(); - break; - } - - error_tblck.Visibility = Visibility.Visible; - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private async void edit_btn_Click(object sender, RoutedEventArgs e) - { - if (accountOptions_stckp.Visibility == Visibility.Collapsed) - { - accountOptions_stckp.Visibility = Visibility.Visible; - edit_btn.Content = "\uE105"; - } - else - { - if (await saveAccountAsync()) - { - accountOptions_stckp.Visibility = Visibility.Collapsed; - edit_btn.Content = "\uE1C2"; - } - } - } - - private async void deleteAccount_btn_Click(object sender, RoutedEventArgs e) - { - DeleteAccountDialog dialog = new DeleteAccountDialog(); - await UiUtils.showDialogAsyncQueue(dialog); - XMPPAccount account = Account; - Task t = Task.Run(async () => - { - if (dialog.deleteAccount) - { - await ConnectionHandler.INSTANCE.removeAccountAsync(account.getIdAndDomain()); - - AccountDBManager.INSTANCE.deleteAccount(account, true, true); - - if (!dialog.keepChatMessages) - { - foreach (ChatTable chat in ChatDBManager.INSTANCE.getAllChatsForClient(account.getIdAndDomain())) - { - ChatDBManager.INSTANCE.deleteAllChatMessagesForChat(chat.id); - } - } - - if (!dialog.keepChats) - { - ChatDBManager.INSTANCE.deleteAllChatsForAccount(account.getIdAndDomain()); - } - } - }); - } - - private void disableAccount_tggls_Toggled(object sender, RoutedEventArgs e) - { - if (Account != null && Account.disabled == disableAccount_tggls.IsOn) - { - IsEnabled = false; - Account.disabled = !disableAccount_tggls.IsOn; - XMPPAccount newAccount = Account; - Task.Run(() => - { - AccountDBManager.INSTANCE.setAccountDisabled(newAccount); - Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => IsEnabled = true).AsTask(); - }); - } - } - - private async void Client_ConnectionStateChanged(XMPPClient client, XMPP_API.Classes.Network.Events.ConnectionStateChangedEventArgs args) - { - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => showConnectionState(client, args.newState, args.param)); - } - - private async void CONNECTION_INFO_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case "tlsConnected": - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => updateSecurityButton(client, client.getConnetionState())); - break; - } - } - - private async void Grid_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) - { - if (error_tblck.Visibility == Visibility.Visible && lastConnectionError != null) - { - ConnectionErrorDialog dialog = new ConnectionErrorDialog(lastConnectionError); - await dialog.ShowAsync(); - } - } - - private async void showConnectionInfo_btn_Click(object sender, RoutedEventArgs e) - { - if (client != null) - { - ConnectionInfoDialog dialog = new ConnectionInfoDialog() - { - Cert = client.getXMPPAccount().CONNECTION_INFO.socketInfo?.ServerCertificate, - ConnectionInfo = client.getXMPPAccount().CONNECTION_INFO, - ParserStats = client.getMessageParserStats(), - OmemoState = client.getOmemoHelper().STATE, - Client = client - }; - await UiUtils.showDialogAsyncQueue(dialog); - } - } - - private async void accountSecurityStatus_tbx_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) - { - string tlsState = ""; - if (client != null && client.getConnetionState() == ConnectionState.CONNECTED) - { - if (client.getXMPPAccount().CONNECTION_INFO.tlsConnected) - { - tlsState = "connected"; - } - else - { - tlsState = "disconnected"; - } - } - else - { - tlsState = "client not connected"; - } - TextDialog dialog = new TextDialog("TLS state: " + tlsState, "TLS state:"); - await dialog.ShowAsync(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Controls/BrowseMUCRoomsMasterControl.xaml.cs b/UWP XMPP Client/Controls/BrowseMUCRoomsMasterControl.xaml.cs index bf406d503..883b56957 100644 --- a/UWP XMPP Client/Controls/BrowseMUCRoomsMasterControl.xaml.cs +++ b/UWP XMPP Client/Controls/BrowseMUCRoomsMasterControl.xaml.cs @@ -1,5 +1,4 @@ using UWP_XMPP_Client.Classes; -using UWP_XMPP_Client.Pages; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -62,7 +61,7 @@ private void showRoom() #region --Events-- private void addRoom_btn_Click(object sender, RoutedEventArgs e) { - (Window.Current.Content as Frame).Navigate(typeof(ChatPage), new ShowAddMUCNavigationParameter(RoomInfo.jid)); + //(Window.Current.Content as Frame).Navigate(typeof(ChatPage), new ShowAddMUCNavigationParameter(RoomInfo.jid)); } private void UserControl_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) diff --git a/UWP XMPP Client/Controls/CertificateControl.xaml b/UWP XMPP Client/Controls/CertificateControl.xaml deleted file mode 100644 index 25b221935..000000000 --- a/UWP XMPP Client/Controls/CertificateControl.xaml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Controls/CertificateControl.xaml.cs b/UWP XMPP Client/Controls/CertificateControl.xaml.cs deleted file mode 100644 index 081954cb0..000000000 --- a/UWP XMPP Client/Controls/CertificateControl.xaml.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Logging; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using System.Threading.Tasks; -using UWP_XMPP_Client.DataTemplates; -using Windows.Security.Cryptography.Certificates; -using Windows.Storage; -using Windows.Storage.Pickers; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace UWP_XMPP_Client.Controls -{ - public sealed partial class CertificateControl : UserControl - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public Certificate Cert - { - get { return (Certificate)GetValue(CertProperty); } - set - { - SetValue(CertProperty, value); - updateCertDetails(); - } - } - public static readonly DependencyProperty CertProperty = DependencyProperty.Register("Cert", typeof(Certificate), typeof(CertificateControl), null); - - private readonly ObservableCollection CERT_DETAILS; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 25/05/2018 Created [Fabian Sauter] - /// - public CertificateControl() - { - this.CERT_DETAILS = new ObservableCollection(); - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void updateCertDetails() - { - CERT_DETAILS.Clear(); - if (Cert is null) - { - validFormShort_run.Text = "-"; - validToShort_run.Text = "-"; - return; - } - - validFormShort_run.Text = Cert.ValidFrom.ToString("dd.MM.yyyy"); - validToShort_run.Text = Cert.ValidTo.ToString("dd.MM.yyyy"); - - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.FriendlyName, - name = "Friendly name" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.HasPrivateKey.ToString(), - name = "Has private key" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.IsPerUser.ToString(), - name = "Is per user" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.Issuer, - name = "Issuer" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.KeyAlgorithmName, - name = "Key algorithm name" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.KeyStorageProviderName, - name = "Key storage provider name" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.ValidTo.ToString("dd.MM.yyyy HH:mm"), - name = "Valid to" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.ValidFrom.ToString("dd.MM.yyyy HH:mm"), - name = "Valid from" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.Subject, - name = "Subject" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = byteArryToString(Cert.SerialNumber), - name = "Serial number" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.SignatureAlgorithmName, - name = "Signature algorithm name" - }); - CERT_DETAILS.Add(new CertificateDetailsTemplate() - { - value = Cert.SignatureHashAlgorithmName, - name = "Signature hash algorithm name" - }); - } - - private string byteArryToString(byte[] b) - { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < b.Length; i++) - { - s.Append(b[i]); - if (i < b.Length) - { - s.Append(' '); - } - } - return s.ToString(); - } - - private async Task exportCertificate() - { - try - { - FileSavePicker picker = new FileSavePicker() - { - SuggestedStartLocation = PickerLocationId.Desktop, - SuggestedFileName = Cert.Subject?.Replace('.', '_').Replace(' ', '-'), - DefaultFileExtension = ".cer" - }; - picker.FileTypeChoices.Add("Certificate", new List() { ".cer" }); - - StorageFile file = await picker.PickSaveFileAsync(); - - if (file != null) - { - var blob = Cert.GetCertificateBlob(); - await FileIO.WriteBufferAsync(file, blob); - } - } - catch (Exception e) - { - Logger.Error("Failed to export certificate: ", e); - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private async void exportCert_btn_Click(object sender, RoutedEventArgs e) - { - if (Cert != null) - { - await exportCertificate(); - } - } - - #endregion - } -} diff --git a/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml b/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml deleted file mode 100644 index e1d17d810..000000000 --- a/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml.cs b/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml.cs deleted file mode 100644 index b3d4377c2..000000000 --- a/UWP XMPP Client/Controls/Chat/ChatDetailsControl.xaml.cs +++ /dev/null @@ -1,898 +0,0 @@ -using Windows.UI.Core; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes; -using System; -using System.Threading.Tasks; -using XMPP_API.Classes.Network.XML.Messages; -using UWP_XMPP_Client.Classes; -using UWP_XMPP_Client.Pages; -using Microsoft.Toolkit.Uwp.UI.Controls; -using UWP_XMPP_Client.Classes.Events; -using Data_Manager2.Classes; -using XMPP_API.Classes.Network.XML.Messages.XEP_0085; -using Data_Manager2.Classes.DBTables; -using Data_Manager2.Classes.DBManager; -using UWP_XMPP_Client.DataTemplates; -using System.Collections.Generic; -using Windows.UI.Xaml.Input; -using Data_Manager2.Classes.Events; -using Windows.UI.Xaml.Media; -using Windows.UI; -using Windows.UI.Xaml.Controls.Primitives; -using XMPP_API.Classes.Network.XML.Messages.XEP_0384; -using Data_Manager2.Classes.Toast; -using UWP_XMPP_Client.Classes.Collections; -using System.ComponentModel; - -namespace UWP_XMPP_Client.Controls.Chat -{ - public sealed partial class ChatDetailsControl : UserControl - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public bool IsDummy - { - get { return (bool)GetValue(IsDummyProperty); } - set - { - SetValue(IsDummyProperty, value); - if (!IsDummy) - { - showChatMessages(Chat); - } - } - } - public static readonly DependencyProperty IsDummyProperty = DependencyProperty.Register(nameof(IsDummy), typeof(bool), typeof(ChatDetailsControl), null); - - public ChatTemplate ChatTemp - { - get { return (ChatTemplate)GetValue(ChatTempProperty); } - set - { - ChatTemplate cur = (ChatTemplate)GetValue(ChatTempProperty); - if (value != cur) - { - if (cur != null) - { - cur.PropertyChanged -= Value_PropertyChanged; - } - if (value != null) - { - value.PropertyChanged += Value_PropertyChanged; - } - SetValue(ChatTempProperty, value); - - onChatTemplateChanged(value); - } - } - } - public static readonly DependencyProperty ChatTempProperty = DependencyProperty.Register(nameof(ChatTemp), typeof(ChatTemplate), typeof(ChatDetailsControl), new PropertyMetadata(null)); - - private ChatTable Chat - { - get { return ChatTemp?.chat; } - set { if (ChatTemp is null) { throw new InvalidOperationException("Can't set ChatTemp.chat - ChatTemp is null in ChatDetailsControl."); } ChatTemp.chat = value; } - } - - private XMPPClient Client - { - get { return ChatTemp?.client; } - set { if (ChatTemp is null) { throw new InvalidOperationException("Can't set ChatTemp.client - ChatTemp is null in ChatDetailsControl."); } ChatTemp.client = value; } - } - - private MUCChatInfoTable MUCInfo - { - get { return ChatTemp?.mucInfo; } - set { if (ChatTemp is null) { throw new InvalidOperationException("Can't set ChatTemp.MUCInfo - ChatTemp is null in ChatDetailsControl."); } ChatTemp.mucInfo = value; } - } - - private readonly CustomObservableCollection CHAT_MESSAGES; - - private int sendDummyMessages; - private string curChatId; - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 29/08/2017 Created [Fabian Sauter] - /// - public ChatDetailsControl() - { - this.sendDummyMessages = 0; - this.CHAT_MESSAGES = new CustomObservableCollection(); - this.curChatId = null; - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - private string getChatType() - { - switch (Chat.chatType) - { - case ChatType.MUC: - return MessageMessage.TYPE_GROUPCHAT; - case ChatType.CHAT: - default: - // For backwards compatibility with older versions of the app: - return MessageMessage.TYPE_CHAT; - } - } - - private bool shouldSendChatState() - { - return !IsDummy && !Settings.getSettingBoolean(SettingsConsts.DONT_SEND_CHAT_STATE) && Chat != null && Chat.chatType == ChatType.CHAT; - } - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - public void loadBackgrundImage() - { - UiUtils.setBackgroundImage(backgroundImage_img); - } - - public void loadDummyContent() - { - ChatTemp = new ChatTemplate - { - chat = new ChatTable - { - chatJabberId = "dave@example.com", - userAccountId = "kevin@example.com", - chatType = ChatType.CHAT, - presence = Presence.Away - } - }; - - addDummyMessage("Hi", Chat.userAccountId, MessageState.READ); - addDummyMessage("Hey, what's up?", Chat.chatJabberId, MessageState.SEND); - addDummyMessage("That's a great app.", Chat.userAccountId, MessageState.READ); - message_tbx.Text = "Yes, its awesome :D !"; - - invertedListView_lstv.Visibility = Visibility.Visible; - loading_ldng.IsLoading = false; - } - - #endregion - - #region --Misc Methods (Private)-- - private void onChatTemplateChanged(ChatTemplate chatTemp) - { - showClient(chatTemp.client); - showChat(chatTemp.chat); - showMUCInfo(chatTemp.mucInfo, chatTemp.chat); - - if (!IsDummy) - { - showChatMessages(Chat); - } - } - - private void addDummyMessage(string msg, string fromUser, MessageState state) - { - addDummyMessage(msg, fromUser, state, false); - } - - private void addDummyMessage(string msg, string fromUser, MessageState state, bool isImage) - { - CHAT_MESSAGES.Add(new ChatMessageDataTemplate - { - chat = Chat, - message = new ChatMessageTable - { - message = msg, - chatId = Chat.id, - fromUser = fromUser, - date = DateTime.Now, - state = state, - type = MessageMessage.TYPE_CHAT, - isImage = isImage, - isDummyMessage = true - } - }); - } - - private void showChatMessages(ChatTable chat) - { - if (chat != null) - { - // Only show chat messages if the chat changed: - if (curChatId != null && Equals(chat.id, curChatId)) - { - return; - } - curChatId = chat.id; - - // Show loading: - loading_ldng.IsLoading = true; - invertedListView_lstv.Visibility = Visibility.Collapsed; - - // Create a copy to prevent multi threading issues: - ChatTable chatCpy = chat; - - Task.Run(async () => - { - // Show all chat messages: - List msgs = new List(); - foreach (ChatMessageTable msg in ChatDBManager.INSTANCE.getAllChatMessagesForChat(chatCpy.id)) - { - msgs.Add(new ChatMessageDataTemplate - { - message = msg, - chat = chatCpy - }); - } - - // Mark all unread messages as read for this chat: - ChatDBManager.INSTANCE.markAllMessagesAsRead(chatCpy.id); - // Remove notification group: - ToastHelper.removeToastGroup(chatCpy.id); - - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - CHAT_MESSAGES.Clear(); - CHAT_MESSAGES.AddRange(msgs); - invertedListView_lstv.Visibility = Visibility.Visible; - loading_ldng.IsLoading = false; - }); - }); - } - } - - private void showChat(ChatTable chat) - { - if (chat != null) - { - if (chat.chatType == ChatType.CHAT) - { - chatName_tblck.Text = chat.chatJabberId ?? ""; - chatState_tblck.Text = chat.chatState ?? ""; - join_mfo.Visibility = Visibility.Collapsed; - leave_mfo.Visibility = Visibility.Collapsed; - omemo_tmfo.Visibility = Visibility.Visible; - if (chat.omemoEnabled) - { - omemo_tmfo.Icon = new FontIcon() - { - Glyph = "\uE72E", - Foreground = new SolidColorBrush(Colors.Green) - }; - omemoIndicator_tbx.Visibility = Visibility.Visible; - } - else - { - omemo_tmfo.Icon = new FontIcon() - { - Glyph = "\uE785", - Foreground = new SolidColorBrush(Colors.Red) - }; - omemoIndicator_tbx.Visibility = Visibility.Collapsed; - } - omemo_tmfo.IsChecked = chat.omemoEnabled; - } - else - { - omemo_tmfo.Visibility = Visibility.Collapsed; - omemoIndicator_tbx.Visibility = Visibility.Collapsed; - omemo_tmfo.IsChecked = false; - } - } - } - - private void showClient(XMPPClient client) - { - if (client != null) - { - accountName_tblck.Text = client.getXMPPAccount().getIdAndDomain(); - } - } - - private void showMUCInfo(MUCChatInfoTable mucInfo, ChatTable chat) - { - if (mucInfo != null && chat != null && Equals(mucInfo.chatId, chat.id)) - { - chatName_tblck.Text = string.IsNullOrWhiteSpace(mucInfo.name) ? chat.chatJabberId : mucInfo.name; - chatState_tblck.Text = mucInfo.subject ?? ""; - } - } - - private void sendBotMessage(string text, bool isImage, int millisecondsDelay) - { - storeChatState(ChatState.COMPOSING); - Task.Run(async () => - { - await Task.Delay(millisecondsDelay); - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - addDummyMessage(text, Chat.chatJabberId, MessageState.READ, isImage); - storeChatState(ChatState.ACTIVE); - }); - }); - } - - private void sendMessage() - { - if (!string.IsNullOrWhiteSpace(message_tbx.Text)) - { - if (IsDummy) - { - addDummyMessage(message_tbx.Text, Chat.userAccountId, MessageState.SEND); - sendDummyMessages++; - - - switch (sendDummyMessages) - { - case 1: - accImg_aiwp.PresenceP = Presence.Online; - break; - - case 3: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_3_img"), true, 3000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_3"), false, 4000); - accImg_aiwp.PresenceP = Presence.Chat; - break; - - case 4: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_4"), false, 3000); - accImg_aiwp.PresenceP = Presence.Online; - break; - - case 7: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_7"), false, 3000); - break; - - case 11: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_11"), false, 3000); - break; - - case 15: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_15"), false, 3000); - accImg_aiwp.PresenceP = Presence.Xa; - break; - - case 20: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_20"), false, 3000); - break; - - case 30: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_30"), false, 3000); - break; - - case 50: - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_1"), false, 3000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_2"), false, 4000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_3"), false, 5000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_4"), false, 6000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_5"), false, 7000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_6"), false, 8000); - sendBotMessage(Localisation.getLocalizedString("chat_details_dummy_answer_50_7"), true, 9000); - Task.Run(async () => - { - await Task.Delay(9000); - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - storeChatState(ChatState.GONE); - accImg_aiwp.PresenceP = Presence.Unavailable; - }); - }); - break; - } - } - else - { - MessageMessage sendMessage; - - string messageText = message_tbx.Text; - // Remove all tailing whitespaces, tabs and newlines: - messageText = messageText.TrimEnd(UiUtils.TRIM_CHARS).TrimStart(UiUtils.TRIM_CHARS); - - // For MUC messages also pass the nickname: - bool toEncrypt = false; - if (Chat.chatType == ChatType.MUC && MUCInfo != null) - { - sendMessage = new MessageMessage(Client.getXMPPAccount().getIdAndDomain(), Chat.chatJabberId, messageText, getChatType(), MUCInfo.nickname, false); - } - else - { - if (Chat.omemoEnabled) - { - sendMessage = new OmemoMessageMessage(Client.getXMPPAccount().getIdAndDomain(), Chat.chatJabberId, messageText, getChatType(), true); - toEncrypt = true; - } - else - { - sendMessage = new MessageMessage(Client.getXMPPAccount().getIdAndDomain(), Chat.chatJabberId, messageText, getChatType(), true); - } - } - ChatMessageTable sendMessageTable = new ChatMessageTable(sendMessage, Chat) - { - state = toEncrypt ? MessageState.TO_ENCRYPT : MessageState.SENDING - }; - - // Set chatMessageId: - sendMessage.chatMessageId = sendMessageTable.id; - - // Add message to DB and update chat last active: - Chat.lastActive = DateTime.Now; - ChatTable chatCpy = Chat; - Task.Run(() => - { - ChatDBManager.INSTANCE.setChatMessage(sendMessageTable, true, false); - ChatDBManager.INSTANCE.setChat(chatCpy, false, true); - }); - if (sendMessage is OmemoMessageMessage omemoMsg) - { - Client.sendOmemoMessage(omemoMsg, Chat.chatJabberId, Client.getXMPPAccount().getIdAndDomain()); - } - else - { - Client.sendMessage(sendMessage); - } - } - - message_tbx.Text = ""; - } - } - - private void showBackgroundForViewState(MasterDetailsViewState state) - { - if (state == MasterDetailsViewState.Both) - { - backgroundImage_img.Visibility = Visibility.Collapsed; - main_grid.Background = new SolidColorBrush(Colors.Transparent); - } - else - { - backgroundImage_img.Visibility = Visibility.Visible; - main_grid.Background = (SolidColorBrush)Application.Current.Resources["ApplicationPageBackgroundThemeBrush"]; - } - } - - private void storeChatState(ChatState state) - { - switch (state) - { - case ChatState.ACTIVE: - Chat.chatState = "Active"; - break; - case ChatState.COMPOSING: - Chat.chatState = "Typing..."; - break; - case ChatState.PAUSED: - Chat.chatState = "Paused"; - break; - case ChatState.INACTIVE: - Chat.chatState = "Inactive"; - break; - case ChatState.GONE: - Chat.chatState = "Gone"; - break; - default: - Chat.chatState = ""; - break; - } - chatState_tblck.Text = Chat.chatState; - } - - private void showProfile() - { - if (Chat != null && Chat.chatType == ChatType.MUC) - { - (Window.Current.Content as Frame).Navigate(typeof(MUCInfoPage), new NavigatedToMUCInfoEventArgs(Chat, Client, MUCInfo)); - } - else - { - (Window.Current.Content as Frame).Navigate(typeof(UserProfilePage), new NavigatedToUserProfileEventArgs(Chat, Client)); - } - } - - private async Task leaveRoomAsync() - { - if (Client != null && MUCInfo != null && Chat != null) - { - await MUCHandler.INSTANCE.leaveRoomAsync(Client, Chat, MUCInfo).ConfigureAwait(false); - } - } - - private async Task joinRoomAsync() - { - if (Client != null && MUCInfo != null && Chat != null) - { - await MUCHandler.INSTANCE.enterMUCAsync(Client, Chat, MUCInfo).ConfigureAwait(false); - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private async void INSTANCE_NewChatMessage(ChatDBManager handler, NewChatMessageEventArgs args) - { - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - if (Chat != null && Chat.id.Equals(args.MESSAGE.chatId)) - { - // Only update for unread messages: - if (args.MESSAGE.state == MessageState.UNREAD) - { - Task.Run(() => ChatDBManager.INSTANCE.markMessageAsRead(args.MESSAGE)); - } - - CHAT_MESSAGES.Add(new ChatMessageDataTemplate() - { - message = args.MESSAGE, - chat = Chat - }); - } - }); - } - - private void send_btn_Click(object sender, RoutedEventArgs e) - { - sendMessage(); - } - - private void invertedListView_lstv_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - invertedListView_lstv.SelectedIndex = -1; - } - - private void clip_btn_Click(object sender, RoutedEventArgs e) - { - if (!IsDummy) - { - //TODO Add clip menu - } - } - - private void message_tbx_TextChanged(object sender, TextChangedEventArgs e) - { - if (string.IsNullOrWhiteSpace(message_tbx.Text)) - { - send_btn.IsEnabled = false; - } - else - { - send_btn.IsEnabled = true; - } - } - - private void UserControl_Loaded(object sender, RoutedEventArgs e) - { - loadBackgrundImage(); - - object o = (Window.Current.Content as Frame).Content; - if (o is ChatPage) - { - ChatPage chatPage = o as ChatPage; - MasterDetailsView masterDetailsView = chatPage.getMasterDetailsView(); - if (masterDetailsView != null) - { - masterDetailsView.ViewStateChanged -= MasterDetailsView_ViewStateChanged; - masterDetailsView.ViewStateChanged += MasterDetailsView_ViewStateChanged; - showBackgroundForViewState(masterDetailsView.ViewState); - } - } - - if (!IsDummy) - { - // Subscribe to chat and chat message changed events: - ChatDBManager.INSTANCE.NewChatMessage -= INSTANCE_NewChatMessage; - ChatDBManager.INSTANCE.NewChatMessage += INSTANCE_NewChatMessage; - ChatDBManager.INSTANCE.ChatMessageChanged -= INSTANCE_ChatMessageChanged; - ChatDBManager.INSTANCE.ChatMessageChanged += INSTANCE_ChatMessageChanged; - } - else - { - loadDummyContent(); - } - - // Enable the test button only on debug builds: -#if DEBUG - test_mfo.Visibility = Visibility.Visible; -#endif - } - - private void UserControl_Unloaded(object sender, RoutedEventArgs e) - { - if (!IsDummy) - { - // Unsubscribe to chat and chat message changed events: - ChatDBManager.INSTANCE.NewChatMessage -= INSTANCE_NewChatMessage; - ChatDBManager.INSTANCE.ChatMessageChanged -= INSTANCE_ChatMessageChanged; - - if (Client != null) - { - Client.NewChatState -= Client_NewChatState; - } - } - } - - private void MasterDetailsView_ViewStateChanged(object sender, MasterDetailsViewState e) - { - showBackgroundForViewState(e); - } - - private async void Client_NewChatState(XMPPClient client, XMPP_API.Classes.Network.Events.NewChatStateEventArgs args) - { - if (args.Cancel) - { - return; - } - string chatId = ChatTable.generateId(args.FROM, args.TO); - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - if (Chat != null && Chat.chatType == ChatType.CHAT && string.Equals(chatId, Chat.id)) - { - storeChatState(args.STATE); - args.Cancel = true; - } - }); - } - - private void message_tbx_KeyUp(object sender, KeyRoutedEventArgs e) - { - if (e.Key == Windows.System.VirtualKey.Enter) - { - if (Settings.getSettingBoolean(SettingsConsts.ENTER_TO_SEND_MESSAGES)) - { - sendMessage(); - } - else - { - int selectionStart = message_tbx.SelectionStart; - message_tbx.Text += "\r"; - message_tbx.SelectionStart = selectionStart + 1; - } - e.Handled = true; - } - } - - private void Test_mfo_Click(object sender, RoutedEventArgs e) - { - Client.PUB_SUB_COMMAND_HELPER.requestSubscriptions("pubsub.404.city", null, null); - } - - private async void message_tbx_GotFocus(object sender, RoutedEventArgs e) - { - if (shouldSendChatState()) - { - await Client.GENERAL_COMMAND_HELPER.sendChatStateAsync(Chat.chatJabberId, ChatState.COMPOSING).ConfigureAwait(false); - } - } - - private async void message_tbx_LostFocus(object sender, RoutedEventArgs e) - { - if (shouldSendChatState()) - { - await Client.GENERAL_COMMAND_HELPER.sendChatStateAsync(Chat.chatJabberId, ChatState.ACTIVE).ConfigureAwait(false); - } - } - - private async void INSTANCE_ChatMessageChanged(ChatDBManager handler, ChatMessageChangedEventArgs args) - { - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - if (Chat != null && Equals(args.MESSAGE.chatId, Chat.id)) - { - Task.Run(async () => - { - for (int i = 0; i < CHAT_MESSAGES.Count; i++) - { - if (CHAT_MESSAGES[i].message != null && Equals(CHAT_MESSAGES[i].message.id, args.MESSAGE.id)) - { - // Only the main thread should update the list to prevent problems: - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => CHAT_MESSAGES[i].message = args.MESSAGE); - } - } - }); - } - }); - } - - private void AccountImageWithPresenceControl_Tapped(object sender, TappedRoutedEventArgs e) - { - if (!IsDummy) - { - showProfile(); - } - } - - private async void leave_mfo_Click(object sender, RoutedEventArgs e) - { - if (!IsDummy) - { - await leaveRoomAsync().ConfigureAwait(false); - } - } - - private async void join_mfo_Click(object sender, RoutedEventArgs e) - { - if (!IsDummy) - { - await joinRoomAsync().ConfigureAwait(false); - } - } - - private void info_mfo_Click(object sender, RoutedEventArgs e) - { - if (!IsDummy) - { - showProfile(); - } - } - - private void MenuFlyout_Opening(object sender, object e) - { - if (MUCInfo != null) - { - switch (MUCInfo.state) - { - case MUCState.ERROR: - case MUCState.DISCONNECTED: - case MUCState.KICKED: - case MUCState.BANED: - join_mfo.Visibility = Visibility.Visible; - leave_mfo.Visibility = Visibility.Collapsed; - break; - - case MUCState.DISCONNECTING: - join_mfo.Visibility = Visibility.Collapsed; - leave_mfo.Visibility = Visibility.Collapsed; - break; - - case MUCState.ENTERING: - case MUCState.ENTERD: - join_mfo.Visibility = Visibility.Collapsed; - leave_mfo.Visibility = Visibility.Visible; - break; - } - } - else - { - join_mfo.Visibility = Visibility.Collapsed; - leave_mfo.Visibility = Visibility.Collapsed; - } - } - - private void copyChatName_mfi_Click(object sender, RoutedEventArgs e) - { - if (Chat != null) - { - switch (Chat.chatType) - { - case ChatType.MUC: - if (MUCInfo != null && !string.IsNullOrEmpty(MUCInfo.name)) - { - UiUtils.addTextToClipboard(MUCInfo.name); - return; - } - break; - } - - UiUtils.addTextToClipboard(Chat.chatJabberId); - } - } - - private void copyAccountName_mfi_Click(object sender, RoutedEventArgs e) - { - if (Chat != null) - { - UiUtils.addTextToClipboard(Chat.userAccountId); - } - } - - private void copyChatState_mfi_Click(object sender, RoutedEventArgs e) - { - if (Chat != null) - { - switch (Chat.chatType) - { - case ChatType.MUC: - if (MUCInfo != null) - { - UiUtils.addTextToClipboard(MUCInfo.subject); - return; - } - break; - } - - UiUtils.addTextToClipboard(Chat.status); - } - } - - private void chatDetails_grid_Tapped(object sender, TappedRoutedEventArgs e) - { - if (!IsDummy) - { - showProfile(); - } - } - - private void chatDetails_grid_RightTapped(object sender, RightTappedRoutedEventArgs e) - { - FrameworkElement senderElement = sender as FrameworkElement; - FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement); - flyoutBase.ShowAt(senderElement); - } - - private void omemo_tmfo_Click(object sender, RoutedEventArgs e) - { - Chat.omemoEnabled = omemo_tmfo.IsChecked; - showChat(Chat); - ChatTable cpy = Chat; - Task.Run(() => ChatDBManager.INSTANCE.setChatTableValue(nameof(cpy.id), cpy.id, nameof(cpy.omemoEnabled), cpy.omemoEnabled)); - } - - private void scrollDown_btn_Click(object sender, RoutedEventArgs e) - { - if (CHAT_MESSAGES.Count >= 1) - { - invertedListView_lstv.ScrollIntoView(CHAT_MESSAGES[CHAT_MESSAGES.Count - 1]); - } - } - - private void clipImgLib_btn_Click(object sender, RoutedEventArgs e) - { - // ToDo implement - } - - private void clipImgCam_btn_Click(object sender, RoutedEventArgs e) - { - // ToDo implement - } - - private void clipDraw_btn_Click(object sender, RoutedEventArgs e) - { - // ToDo implement - } - - private void clipFile_btn_Click(object sender, RoutedEventArgs e) - { - // ToDo implement - } - - private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (ChatTemp is null) - { - return; - } - - switch (e.PropertyName) - { - case nameof(ChatTemplate.chat): - showChat(ChatTemp.chat); - showMUCInfo(ChatTemp.mucInfo, ChatTemp.chat); - break; - - case nameof(ChatTemplate.client): - showClient(ChatTemp.client); - if (ChatTemp.client != null) - { - ChatTemp.client.NewChatState -= Client_NewChatState; - ChatTemp.client.NewChatState += Client_NewChatState; - } - break; - - case nameof(ChatTemplate.mucInfo): - showMUCInfo(ChatTemp.mucInfo, ChatTemp.chat); - break; - - default: - break; - } - } - #endregion - } -} diff --git a/UWP XMPP Client/Controls/Chat/ChatMasterControl.xaml b/UWP XMPP Client/Controls/Chat/ChatMasterControl.xaml deleted file mode 100644 index 3cfe7a291..000000000 --- a/UWP XMPP Client/Controls/Chat/ChatMasterControl.xaml +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Controls/Chat/SpeechBubbleMUCDirectInvitationControl.xaml.cs b/UWP XMPP Client/Controls/Chat/SpeechBubbleMUCDirectInvitationControl.xaml.cs deleted file mode 100644 index d05397785..000000000 --- a/UWP XMPP Client/Controls/Chat/SpeechBubbleMUCDirectInvitationControl.xaml.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using Data_Manager2.Classes.DBTables; -using System.Threading.Tasks; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Data_Manager2.Classes.DBManager; -using Data_Manager2.Classes; -using UWP_XMPP_Client.Dialogs; -using UWP_XMPP_Client.Classes; - -namespace UWP_XMPP_Client.Controls.Chat -{ - public sealed partial class SpeechBubbleMUCDirectInvitationControl : UserControl - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public ChatTable Chat - { - get { return (ChatTable)GetValue(ChatProperty); } - set { SetValue(ChatProperty, value); } - } - public static readonly DependencyProperty ChatProperty = DependencyProperty.Register(nameof(Chat), typeof(ChatTable), typeof(SpeechBubbleMUCDirectInvitationControl), null); - - public MUCDirectInvitationTable Invitation - { - get { return (MUCDirectInvitationTable)GetValue(InvitationProperty); } - set { SetValue(InvitationProperty, value); } - } - public static readonly DependencyProperty InvitationProperty = DependencyProperty.Register(nameof(Invitation), typeof(MUCDirectInvitationTable), typeof(SpeechBubbleMUCDirectInvitationControl), null); - - public ChatMessageTable ChatMessage - { - get { return (ChatMessageTable)GetValue(ChatMessageProperty); } - set - { - SetValue(ChatMessageProperty, value); - loadInvitation(); - } - } - public static readonly DependencyProperty ChatMessageProperty = DependencyProperty.Register(nameof(ChatMessage), typeof(ChatMessageTable), typeof(SpeechBubbleMUCDirectInvitationControl), null); - - private bool invitationLoaded; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 02/03/2018 Created [Fabian Sauter] - /// - public SpeechBubbleMUCDirectInvitationControl() - { - this.Invitation = null; - this.invitationLoaded = false; - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void loadInvitation() - { - if (!invitationLoaded && ChatMessage != null) - { - invitationLoaded = true; - main_expdr.Visibility = Visibility.Collapsed; - error_grid.Visibility = Visibility.Collapsed; - loading_grid.Visibility = Visibility.Visible; - - string chatMessageId = ChatMessage.id; - Task.Run(async () => - { - MUCDirectInvitationTable invitationTable = ChatDBManager.INSTANCE.getMUCDirectInvitation(chatMessageId); - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - Invitation = invitationTable; - showInvitation(); - }); - }); - } - } - - private void showInvitation() - { - - if (Invitation != null) - { - text_tbx.Text = ChatMessage.fromUser + " has send you an invite to join a MUC room."; - if (Invitation.roomJid is null) - { - error_tbx.Text = "No JID given!"; - loading_grid.Visibility = Visibility.Collapsed; - error_grid.Visibility = Visibility.Visible; - return; - } - - main_expdr.Header = "Invitation to room: " + Invitation.roomJid; - - switch (Invitation.state) - { - case MUCDirectInvitationState.REQUESTED: - main_expdr.IsExpanded = true; - result_tbx.Visibility = Visibility.Collapsed; - buttons_grid.Visibility = Visibility.Visible; - break; - - case MUCDirectInvitationState.ACCEPTED: - main_expdr.IsExpanded = false; - result_tbx.Text = "You have accepted the invitation."; - buttons_grid.Visibility = Visibility.Collapsed; - result_tbx.Visibility = Visibility.Visible; - break; - - case MUCDirectInvitationState.DECLINED: - result_tbx.Text = "You have declined the invitation."; - main_expdr.IsExpanded = false; - buttons_grid.Visibility = Visibility.Collapsed; - result_tbx.Visibility = Visibility.Visible; - break; - } - - loading_grid.Visibility = Visibility.Collapsed; - main_expdr.Visibility = Visibility.Visible; - } - else - { - error_tbx.Text = "Unable to load invitation from DB!"; - loading_grid.Visibility = Visibility.Collapsed; - error_grid.Visibility = Visibility.Visible; - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void decline_btn_Click(object sender, RoutedEventArgs e) - { - if (Invitation != null) - { - Invitation.state = MUCDirectInvitationState.DECLINED; - string chatMessageId = Invitation.chatMessageId; - Task.Run(() => ChatDBManager.INSTANCE.setMUCDirectInvitationState(chatMessageId, MUCDirectInvitationState.DECLINED)); - showInvitation(); - } - } - - private async void accept_btn_Click(object sender, RoutedEventArgs e) - { - if (Invitation != null && Chat != null) - { - AddMUCDialog dialog; - if (Invitation.roomPassword != null) - { - dialog = new AddMUCDialog(Invitation.roomJid, Invitation.roomPassword, Chat.userAccountId); - } - else - { - dialog = new AddMUCDialog(Invitation.roomJid); - } - await UiUtils.showDialogAsyncQueue(dialog); - if (dialog.cancled) - { - return; - } - - Invitation.state = MUCDirectInvitationState.ACCEPTED; - string chatMessageId = Invitation.chatMessageId; - await Task.Run(() => ChatDBManager.INSTANCE.setMUCDirectInvitationState(chatMessageId, MUCDirectInvitationState.ACCEPTED)).ConfigureAwait(false); - showInvitation(); - } - } - - #endregion - } -} diff --git a/UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml b/UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml deleted file mode 100644 index 17637bf27..000000000 --- a/UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Controls/IconButtonControl.xaml b/UWP XMPP Client/Controls/IconButtonControl.xaml index 3695a8a77..00e907c77 100644 --- a/UWP XMPP Client/Controls/IconButtonControl.xaml +++ b/UWP XMPP Client/Controls/IconButtonControl.xaml @@ -3,14 +3,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" HorizontalAlignment="Left" - VerticalAlignment="Top"> + VerticalAlignment="Top" + mc:Ignorable="d"> - - - - - - diff --git a/UWP XMPP Client/Dialogs/ChangeCertificateRequirementsDialog.xaml.cs b/UWP XMPP Client/Dialogs/ChangeCertificateRequirementsDialog.xaml.cs deleted file mode 100644 index bfb084a66..000000000 --- a/UWP XMPP Client/Dialogs/ChangeCertificateRequirementsDialog.xaml.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Collections.ObjectModel; -using UWP_XMPP_Client.DataTemplates; -using Windows.Security.Cryptography.Certificates; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes.Network; - -namespace UWP_XMPP_Client.Dialogs -{ - public sealed partial class ChangeCertificateRequirementsDialog : ContentDialog - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - private ObservableCollection certificateRequirements; - private XMPPAccount account; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 12/04/2018 Created [Fabian Sauter] - /// - public ChangeCertificateRequirementsDialog(XMPPAccount account) - { - this.account = account; - this.certificateRequirements = new ObservableCollection(); - loadCertificateRequirements(); - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void loadCertificateRequirements() - { - certificateRequirements.Add(new CertificateRequirementTemplate() - { - certificateError = ChainValidationResult.Expired, - description = "The certificate hasn't expired.", - required = true, - name = "Not expired" - }); - certificateRequirements.Add(new CertificateRequirementTemplate() - { - certificateError = ChainValidationResult.InvalidName, - description = "The certificate has a valid name.", - required = true, - name = "Valid name" - }); - certificateRequirements.Add(new CertificateRequirementTemplate() - { - certificateError = ChainValidationResult.Untrusted, - description = "The certificate isn't untrusted/self signed.", - required = true, - name = "Trusted" - }); - certificateRequirements.Add(new CertificateRequirementTemplate() - { - certificateError = ChainValidationResult.WrongUsage, - description = "The certificate is intended to get used for this.", - required = true, - name = "Right usage" - }); - - // Load ignored errors: - if (account != null) - { - foreach (ChainValidationResult item in account.connectionConfiguration.IGNORED_CERTIFICATE_ERRORS) - { - for (int i = 0; i < certificateRequirements.Count; i++) - { - if(certificateRequirements[i].certificateError == item) - { - certificateRequirements[i].required = false; - break; - } - } - } - } - } - - private void save() - { - if (account is null) - { - return; - } - - account.connectionConfiguration.IGNORED_CERTIFICATE_ERRORS.Clear(); - - foreach (CertificateRequirementTemplate c in certificateRequirements) - { - if (!c.required) - { - account.connectionConfiguration.IGNORED_CERTIFICATE_ERRORS.Add(c.certificateError); - } - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - save(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Dialogs/ClearCacheDialog.xaml.cs b/UWP XMPP Client/Dialogs/ClearCacheDialog.xaml.cs deleted file mode 100644 index 879cf3b06..000000000 --- a/UWP XMPP Client/Dialogs/ClearCacheDialog.xaml.cs +++ /dev/null @@ -1,219 +0,0 @@ -using Data_Manager2.Classes; -using Data_Manager2.Classes.DBTables; -using Data_Manager2.Classes.DBTables.Omemo; -using System; -using System.Threading.Tasks; -using Thread_Save_Components.Classes.SQLite; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace UWP_XMPP_Client.Dialogs -{ - public sealed partial class ClearCacheDialog : ContentDialog - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 20/01/2018 Created [Fabian Sauter] - /// - public ClearCacheDialog() - { - this.InitializeComponent(); - setSelectedNodes(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - private void setSelectedNodes() - { - tree_tv.SelectedNodes.Add(general_tvn); - tree_tv.SelectedNodes.Add(disco_tvn); - tree_tv.SelectedNodes.Add(muc_tvn); - tree_tv.SelectedNodes.Add(clients_tvn); - } - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private bool isChecked(Microsoft.UI.Xaml.Controls.TreeViewNode node) - { - return false; - } - - private void clearCache() - { - clear_btn.IsEnabled = false; - close_btn.IsEnabled = false; - tree_tv.IsEnabled = false; - clear_prgr.Visibility = Visibility.Visible; - - bool chatMessages = isChecked(chatMessages_tvn); - bool chat = isChecked(chats_tvn); - bool images = isChecked(images_tvn); - - bool discoFeatures = isChecked(discoFeatures_tvn); - bool discoIdentities = isChecked(discoIdentities_tvn); - bool discoItems = isChecked(discoItems_tvn); - - bool mucChatInfo = isChecked(mucChatInfo_tvn); - bool mucMembers = isChecked(mucMembers_tvn); - bool mucDirectInvites = isChecked(mucDirectInvites_tvn); - - bool accounts = isChecked(account_tvn); - bool passwordVault = isChecked(passwordVault_tvn); - bool ignoredCertificateErrors = isChecked(ignoredCertificateErrors_tvn); - bool connectionOptions = isChecked(connectionOptions_tvn); - - bool omemoDeviceListSubscriptions = isChecked(omemoDeviceListSubscriptions_tvn); - bool omemoDevices = isChecked(omemoDevices_tvn); - bool omemoIdentityKeys = isChecked(omemoIdentityKeys_tvn); - bool omemoPreKeys = isChecked(omemoPreKeys_tvn); - bool omemoSignedPreKeys = isChecked(omemoSignedPreKeys_tvn); - bool omemoSessions = isChecked(omemoSessions_tvn); - - bool reloadClients = isChecked(reloadClients_tvn); - - Task.Run(async () => - { - // General: - if (chatMessages) - { - AbstractDBManager.dB.RecreateTable(); - } - if (chat) - { - AbstractDBManager.dB.RecreateTable(); - } - if (images) - { - AbstractDBManager.dB.RecreateTable(); - } - - // Disco: - if (discoFeatures) - { - AbstractDBManager.dB.RecreateTable(); - } - if (discoIdentities) - { - AbstractDBManager.dB.RecreateTable(); - } - if (discoItems) - { - AbstractDBManager.dB.RecreateTable(); - } - - // MUC: - if (mucChatInfo) - { - AbstractDBManager.dB.RecreateTable(); - } - if (mucMembers) - { - AbstractDBManager.dB.RecreateTable(); - } - if (mucDirectInvites) - { - AbstractDBManager.dB.RecreateTable(); - } - - // Accounts: - if (accounts) - { - AbstractDBManager.dB.RecreateTable(); - } - if (passwordVault) - { - Vault.deleteAllVaults(); - } - if (ignoredCertificateErrors) - { - AbstractDBManager.dB.RecreateTable(); - } - if (connectionOptions) - { - AbstractDBManager.dB.RecreateTable(); - } - - // OMEMO: - if (omemoDeviceListSubscriptions) - { - AbstractDBManager.dB.RecreateTable(); - } - if (omemoDevices) - { - AbstractDBManager.dB.RecreateTable(); - } - if (omemoIdentityKeys) - { - AbstractDBManager.dB.RecreateTable(); - } - if (omemoPreKeys) - { - AbstractDBManager.dB.RecreateTable(); - } - if (omemoSignedPreKeys) - { - AbstractDBManager.dB.RecreateTable(); - } - if (omemoSessions) - { - AbstractDBManager.dB.RecreateTable(); - } - - // Clients: - if (reloadClients) - { - ConnectionHandler.INSTANCE.reloadClients(); - } - - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - clear_btn.IsEnabled = true; - close_btn.IsEnabled = true; - tree_tv.IsEnabled = true; - clear_prgr.Visibility = Visibility.Collapsed; - - // Show non found in app notification: - done_notification.Show("Done cleaning cache!", 0); - }); - }); - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void clear_btn_Click(object sender, RoutedEventArgs e) - { - done_notification.Dismiss(); - clearCache(); - } - - private void close_btn_Click(object sender, RoutedEventArgs e) - { - done_notification.Dismiss(); - Hide(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Dialogs/ColorPickerDialog.xaml b/UWP XMPP Client/Dialogs/ColorPickerDialog.xaml deleted file mode 100644 index 3606998e4..000000000 --- a/UWP XMPP Client/Dialogs/ColorPickerDialog.xaml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml b/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml deleted file mode 100644 index ec7f27dee..000000000 --- a/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml.cs b/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml.cs deleted file mode 100644 index d4ceb595c..000000000 --- a/UWP XMPP Client/Dialogs/ConnectionErrorDialog.xaml.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes.Network; - -namespace UWP_XMPP_Client.Dialogs -{ - public sealed partial class ConnectionErrorDialog : ContentDialog - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public ConnectionError LastConnectionError - { - get { return (ConnectionError)GetValue(LastConnectionErrorProperty); } - set - { - SetValue(LastConnectionErrorProperty, value); - showLastConnectionError(); - } - } - public static readonly DependencyProperty LastConnectionErrorProperty = DependencyProperty.Register("LastConnectionError", typeof(ConnectionError), typeof(ConnectionErrorDialog), null); - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 30/04/2018 Created [Fabian Sauter] - /// - public ConnectionErrorDialog(ConnectionError lastConnectionError) - { - this.InitializeComponent(); - this.LastConnectionError = lastConnectionError; - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void showLastConnectionError() - { - if (LastConnectionError != null) - { - errorCode_run.Text = (int)LastConnectionError.ERROR_CODE + " [" + LastConnectionError.ERROR_CODE + "]"; - if (LastConnectionError.ERROR_CODE == ConnectionErrorCode.SOCKET_ERROR) - { - socketErrorCode_run.Text = (int)LastConnectionError.SOCKET_ERROR + " [" + LastConnectionError.SOCKET_ERROR + "]"; - } - else - { - socketErrorCode_run.Text = "-"; - } - errorMessage_run.Text = LastConnectionError.ERROR_MESSAGE ?? "-"; - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - Hide(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml b/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml deleted file mode 100644 index 29dbada9e..000000000 --- a/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml.cs b/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml.cs deleted file mode 100644 index 31597d68a..000000000 --- a/UWP XMPP Client/Dialogs/ConnectionInfoDialog.xaml.cs +++ /dev/null @@ -1,361 +0,0 @@ -using libsignal; -using System; -using System.Collections.Generic; -using Windows.Security.Cryptography.Certificates; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes; -using XMPP_API.Classes.Network; -using XMPP_API.Classes.Network.XML; -using XMPP_API.Classes.Network.XML.Messages.XEP_0384; - -namespace UWP_XMPP_Client.Dialogs -{ - public sealed partial class ConnectionInfoDialog : ContentDialog - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - public Certificate Cert - { - get { return (Certificate)GetValue(CertProperty); } - set { SetValue(CertProperty, value); } - } - public static readonly DependencyProperty CertProperty = DependencyProperty.Register(nameof(Cert), typeof(Certificate), typeof(ConnectionInfoDialog), null); - - public ConnectionInformation ConnectionInfo - { - get { return (ConnectionInformation)GetValue(ConnectionInfoProperty); } - set - { - SetValue(ConnectionInfoProperty, value); - if (ConnectionInfo != null) - { - ConnectionInfo.PropertyChanged -= Value_PropertyChanged; - } - if (value != null) - { - value.PropertyChanged -= Value_PropertyChanged; - value.PropertyChanged += Value_PropertyChanged; - } - showConnectionInfo(); - } - } - - public static readonly DependencyProperty ConnectionInfoProperty = DependencyProperty.Register(nameof(ConnectionInfo), typeof(ConnectionInformation), typeof(ConnectionInfoDialog), null); - - public MessageParserStats ParserStats - { - get { return (MessageParserStats)GetValue(ParserStatsProperty); } - set { SetValue(ParserStatsProperty, value); } - } - public static readonly DependencyProperty ParserStatsProperty = DependencyProperty.Register(nameof(ParserStats), typeof(MessageParserStats), typeof(ConnectionInfoDialog), null); - - public OmemoHelperState OmemoState - { - get { return (OmemoHelperState)GetValue(OmemoStateProperty); } - set - { - SetValue(OmemoStateProperty, value); - showOmemoState(); - } - } - public static readonly DependencyProperty OmemoStateProperty = DependencyProperty.Register(nameof(OmemoState), typeof(OmemoHelperState), typeof(ConnectionInfoDialog), new PropertyMetadata(OmemoHelperState.DISABLED)); - - public XMPPClient Client - { - get { return (XMPPClient)GetValue(ClientProperty); } - set - { - SetValue(ClientProperty, value); - showClient(); - } - } - public static readonly DependencyProperty ClientProperty = DependencyProperty.Register(nameof(Client), typeof(XMPPClient), typeof(ConnectionInfoDialog), new PropertyMetadata(null)); - - private bool omemoPviLoaded; - private bool certsPviLoaded; - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 30/05/2018 Created [Fabian Sauter] - /// - public ConnectionInfoDialog() - { - this.omemoPviLoaded = false; - this.certsPviLoaded = false; - this.InitializeComponent(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void showConnectionInfo() - { - if (ConnectionInfo != null) - { - showTlsConnected(); - showMsgCarbonsState(); - showOmemoState(); - } - } - - private void showTlsConnected() - { - if (ConnectionInfo.tlsConnected) - { - tlsConnected_run.Text = "Connected"; - tlsDisconnected_run.Text = ""; - } - else - { - tlsConnected_run.Text = ""; - tlsDisconnected_run.Text = "Disconnected"; - } - } - - private void showOmemoState() - { - if (!omemoPviLoaded) - { - return; - } - - switch (OmemoState) - { - case OmemoHelperState.REQUESTING_DEVICE_LIST: - omemoDisabled_run.Text = ""; - omemoEnabled_run.Text = ""; - omemoWip_run.Text = "Requested device list"; - break; - - case OmemoHelperState.UPDATING_DEVICE_LIST: - omemoDisabled_run.Text = ""; - omemoEnabled_run.Text = ""; - omemoWip_run.Text = "Updating device list"; - break; - - case OmemoHelperState.ANNOUNCING_BUNDLE_INFO: - omemoDisabled_run.Text = ""; - omemoEnabled_run.Text = ""; - omemoWip_run.Text = "Announcing bundle info"; - break; - - case OmemoHelperState.ENABLED: - omemoDisabled_run.Text = ""; - omemoEnabled_run.Text = "Enabled"; - omemoWip_run.Text = ""; - break; - - case OmemoHelperState.DISABLED: - omemoDisabled_run.Text = "Disabled"; - omemoEnabled_run.Text = ""; - omemoWip_run.Text = ""; - break; - - default: - omemoDisabled_run.Text = "Error - view logs"; - omemoEnabled_run.Text = ""; - omemoWip_run.Text = ""; - break; - } - } - - private void showMsgCarbonsState() - { - switch (ConnectionInfo.msgCarbonsState) - { - case MessageCarbonsState.DISABLED: - carbonsDisabled_run.Text = "Disabled"; - carbonsEnabled_run.Text = ""; - carbonsReqested_run.Text = ""; - break; - - case MessageCarbonsState.UNAVAILABLE: - carbonsDisabled_run.Text = "Unavailable"; - carbonsEnabled_run.Text = ""; - carbonsReqested_run.Text = ""; - break; - - case MessageCarbonsState.REQUESTED: - carbonsDisabled_run.Text = ""; - carbonsEnabled_run.Text = ""; - carbonsReqested_run.Text = "Requested"; - break; - - case MessageCarbonsState.ENABLED: - carbonsDisabled_run.Text = ""; - carbonsEnabled_run.Text = "Enabled"; - carbonsReqested_run.Text = ""; - break; - - case MessageCarbonsState.ERROR: - carbonsDisabled_run.Text = "Error"; - carbonsEnabled_run.Text = ""; - carbonsReqested_run.Text = ""; - break; - - default: - throw new InvalidOperationException("Unexpected value for ConnectionInfo.msgCarbonsState: " + ConnectionInfo.msgCarbonsState); - } - } - - private void showClient() - { - if (!omemoPviLoaded) - { - return; - } - - omemoFingerprint_ofc.MyFingerprint = Client?.getXMPPAccount().getOmemoFingerprint(); - if (Client != null) - { - if (!Client.getXMPPAccount().checkOmemoKeys()) - { - omemoError_itbx.Text = "OMEMO keys are corrupted. Please remove and add your account again!"; - omemoError_itbx.Visibility = Visibility.Visible; - } - else - { - omemoError_itbx.Visibility = Visibility.Collapsed; - } - - OmemoDevices devices = Client.getOmemoHelper().DEVICES; - if (devices != null) - { - omemoDevices_odc.setDevices(devices.toSignalProtocolAddressList(Client.getXMPPAccount().getIdAndDomain())); - omemoDevicesInfo_tbx.Visibility = Visibility.Collapsed; - } - else - { - omemoDevices_odc.setDevices(new List()); - omemoDevicesInfo_tbx.Visibility = Visibility.Visible; - } - - uint deviceId = Client.getXMPPAccount().omemoDeviceId; - if (deviceId != 0) - { - omemoDeviceId_run.Text = deviceId.ToString(); - } - else - { - omemoDeviceId_run.Text = "-"; - } - } - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - Hide(); - } - - private async void Value_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - if (ConnectionInfo != null) - { - switch (e.PropertyName) - { - case "msgCarbonsState": - showMsgCarbonsState(); - break; - - case "tlsConnected": - showTlsConnected(); - break; - - default: - break; - } - } - }); - } - - private void resetOmemoDevices_btn_Click(object sender, RoutedEventArgs e) - { - if (Client != null) - { - resetOmemoDevices_btn.IsEnabled = false; - resetOmemoDevices_pgr.Visibility = Visibility.Visible; - Client.getOmemoHelper().resetDeviceListStateless(onResetDeviceListResult); - } - } - - private void refreshOmemoDevices_btn_Click(object sender, RoutedEventArgs e) - { - if (Client != null) - { - refreshOmemoDevices_btn.IsEnabled = false; - refreshOmemoDevices_pgr.Visibility = Visibility.Visible; - Client.getOmemoHelper().requestDeviceListStateless(onDeviceListResult); - } - } - - private async void onResetDeviceListResult(bool success) - { - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - resetOmemoDevices_pgr.Visibility = Visibility.Collapsed; - resetOmemoDevices_btn.IsEnabled = true; - }); - } - - private async void onDeviceListResult(bool success, OmemoDevices devices) - { - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - if (success) - { - omemoDevices_odc.setDevices(devices.toSignalProtocolAddressList(Client.getXMPPAccount().getIdAndDomain())); - refreshOmemoDevices_pgr.Visibility = Visibility.Collapsed; - refreshOmemoDevices_btn.IsEnabled = true; - } - }); - } - - private void ContentDialog_Unloaded(object sender, RoutedEventArgs e) - { - if (ConnectionInfo != null) - { - ConnectionInfo.PropertyChanged -= Value_PropertyChanged; - } - } - - private void cert_pvi_Loaded(object sender, RoutedEventArgs e) - { - certsPviLoaded = true; - } - - private void omemo_pvi_Loaded(object sender, RoutedEventArgs e) - { - omemoPviLoaded = true; - showOmemoState(); - showClient(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Dialogs/DeleteAccountDialog.xaml b/UWP XMPP Client/Dialogs/DeleteAccountDialog.xaml deleted file mode 100644 index dc951b2fc..000000000 --- a/UWP XMPP Client/Dialogs/DeleteAccountDialog.xaml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Dialogs/DeleteChatDialog.xaml b/UWP XMPP Client/Dialogs/DeleteChatDialog.xaml deleted file mode 100644 index ee1271a4f..000000000 --- a/UWP XMPP Client/Dialogs/DeleteChatDialog.xaml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Dialogs/WhatsNewDialog.xaml b/UWP XMPP Client/Dialogs/WhatsNewDialog.xaml deleted file mode 100644 index 453b6b40f..000000000 --- a/UWP XMPP Client/Dialogs/WhatsNewDialog.xaml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Package.xml b/UWP XMPP Client/Package.xml deleted file mode 100644 index 98fb29e03..000000000 --- a/UWP XMPP Client/Package.xml +++ /dev/null @@ -1,373 +0,0 @@ - - - CN=8AFEBA0F-E085-403B-A05B-71A8952F40A3 - Fabian Sauter - MSA - http://www.w3.org/2001/04/xmlenc#sha256 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 790FabianSauter.UWPXAlpha - - UWPX Alpha - - - - 790FabianSauter.TUM-Campus-App - 790FabianSauter.UWPX - 790FabianSauter.UWPXBeta - - - \ No newline at end of file diff --git a/UWP XMPP Client/Pages/AddAccountPage.xaml b/UWP XMPP Client/Pages/AddAccountPage.xaml deleted file mode 100644 index 6c57f8eef..000000000 --- a/UWP XMPP Client/Pages/AddAccountPage.xaml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/AddAccountPage.xaml.cs b/UWP XMPP Client/Pages/AddAccountPage.xaml.cs deleted file mode 100644 index 946d682ce..000000000 --- a/UWP XMPP Client/Pages/AddAccountPage.xaml.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Data_Manager2.Classes; -using Data_Manager2.Classes.DBManager; -using System; -using System.Threading.Tasks; -using UWP_XMPP_Client.Classes; -using UWP_XMPP_Client.Dialogs; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; -using XMPP_API.Classes.Network; - -namespace UWP_XMPP_Client.Pages -{ - public sealed partial class AddAccountPage : Page - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 25/08/2017 Created [Fabian Sauter] - /// - public AddAccountPage() - { - this.InitializeComponent(); - Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += AbstractBackRequestPage_BackRequested; - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private async Task acceptAsync() - { - accept_pgr.Visibility = Visibility.Visible; - IsEnabled = false; - if (await account_ac.isAccountVaildAsync()) - { - XMPPAccount account = account_ac.getAccount(); - Task t = Task.Run(async () => - { - if (account != null) - { - account.generateOmemoKeys(); - AccountDBManager.INSTANCE.setAccount(account, true); - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, moveOn); - } - else - { - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => - { - await showErrorDialogAsync(Localisation.getLocalizedString("invalid_jabber_id_text")); - accept_pgr.Visibility = Visibility.Collapsed; - IsEnabled = true; - }); - } - }); - } - else - { - accept_pgr.Visibility = Visibility.Collapsed; - IsEnabled = true; - } - } - - private async Task showErrorDialogAsync(string text) - { - TextDialog dialog = new TextDialog(text, Localisation.getLocalizedString("error_text")); - await UiUtils.showDialogAsyncQueue(dialog); - } - - private void moveOn() - { - Settings.setSetting(SettingsConsts.INITIALLY_STARTED, true); - if (Window.Current.Content is Frame rootFrame && rootFrame.CanGoBack) - { - rootFrame.GoBack(); - return; - } - (Window.Current.Content as Frame).Navigate(typeof(ChatPage), "AddAccountPage.xaml.cs"); - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void cancel_btn_Click_1(object sender, RoutedEventArgs e) - { - moveOn(); - } - - private async void accept_btn_Click_1(object sender, RoutedEventArgs e) - { - await acceptAsync(); - } - - private void AbstractBackRequestPage_BackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e) - { - if (!(Window.Current.Content is Frame rootFrame)) - { - return; - } - if (rootFrame.CanGoBack && e.Handled == false) - { - e.Handled = true; - rootFrame.GoBack(); - } - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - - } - - protected async override void OnNavigatedTo(NavigationEventArgs e) - { - if (e.NavigationMode == NavigationMode.New && e.Parameter is string && (e.Parameter as string).Equals("App.xaml.cs")) - { - await UiUtils.showInitialStartDialogAsync(); - } - } - - private async void account_ac_AccountAccepted(Controls.AccountControl sender, EventArgs args) - { - await acceptAsync(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Pages/BrowseMUCRoomsPage.xaml b/UWP XMPP Client/Pages/BrowseMUCRoomsPage.xaml index 12f45d825..2374b2282 100644 --- a/UWP XMPP Client/Pages/BrowseMUCRoomsPage.xaml +++ b/UWP XMPP Client/Pages/BrowseMUCRoomsPage.xaml @@ -47,11 +47,18 @@ Grid.Row="0" Visibility="Collapsed"> + + - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/SettingsPages/AccountSettingsPage.xaml.cs b/UWP XMPP Client/Pages/SettingsPages/AccountSettingsPage.xaml.cs deleted file mode 100644 index cc16a30de..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/AccountSettingsPage.xaml.cs +++ /dev/null @@ -1,159 +0,0 @@ -using Data_Manager2.Classes; -using Data_Manager2.Classes.DBManager; -using UWP_XMPP_Client.Controls; -using Windows.UI.Core; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using XMPP_API.Classes.Network; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Data_Manager2.Classes.Events; -using UWP_XMPP_Client.Dialogs; -using UWP_XMPP_Client.Classes; - -namespace UWP_XMPP_Client.Pages.SettingsPages -{ - public sealed partial class AccountSettingsPage : Page - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 04/09/2017 Created [Fabian Sauter] - /// - public AccountSettingsPage() - { - this.InitializeComponent(); - SystemNavigationManager.GetForCurrentView().BackRequested += AbstractBackRequestPage_BackRequested; - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void loadAccounts() - { - Task.Run(() => - { - IList list = AccountDBManager.INSTANCE.loadAllAccounts(); - - AccountDBManager.INSTANCE.AccountChanged -= INSTANCE_AccountChanged; - AccountDBManager.INSTANCE.AccountChanged += INSTANCE_AccountChanged; - - Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - accounts_stckp.Children.Clear(); - foreach (XMPPAccount account in list) - { - accounts_stckp.Children.Add(new AccountSettingsControl() { Account = account }); - } - - if (accounts_stckp.Children.Count > 0) - { - reloadAccounts_btn.Visibility = Visibility.Visible; - } - else - { - reloadAccounts_btn.Visibility = Visibility.Collapsed; - } - accounts_scrlv.Visibility = Visibility.Visible; - loading_grid.Visibility = Visibility.Collapsed; - reloadAccounts_prgr.Visibility = Visibility.Collapsed; - reloadAccounts_btn.IsEnabled = true; - }).AsTask(); - }); - } - - private void addAccount() - { - addAccount_prgr.Visibility = Visibility.Visible; - addAccount_btn.IsEnabled = false; - - Task t = Task.Run(() => - { - int accountCount = AccountDBManager.INSTANCE.getAccountCount(); - Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => - { - // https://docs.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordvault.add#Windows_Security_Credentials_PasswordVault_Add_Windows_Security_Credentials_PasswordCredential_ - if (accountCount >= 15) - { - TextDialog dialog = new TextDialog(Localisation.getLocalizedString("AccountSettingsPage_too_many_accounts_text"), "Error!"); - await UiUtils.showDialogAsyncQueue(dialog); - } - else - { - (Window.Current.Content as Frame).Navigate(typeof(AddAccountPage)); - } - addAccount_prgr.Visibility = Visibility.Visible; - addAccount_btn.IsEnabled = false; - }).AsTask(); - }); - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void AbstractBackRequestPage_BackRequested(object sender, BackRequestedEventArgs e) - { - if (!(Window.Current.Content is Frame rootFrame)) - { - return; - } - if (rootFrame.CanGoBack && e.Handled == false) - { - e.Handled = true; - rootFrame.GoBack(); - } - } - - private void addAccount_btn_Click(object sender, RoutedEventArgs e) - { - addAccount(); - } - - private void reloadAccounts_btn_Click(object sender, RoutedEventArgs e) - { - reloadAccounts_btn.IsEnabled = false; - reloadAccounts_prgr.Visibility = Visibility.Visible; - Task.Run(() => - { - ConnectionHandler.INSTANCE.reconnectAll(); - loadAccounts(); - }); - } - - private void INSTANCE_AccountChanged(AccountDBManager handler, AccountChangedEventArgs args) - { - loadAccounts(); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - loadAccounts(); - } - - #endregion - } -} diff --git a/UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml b/UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml deleted file mode 100644 index 3071b871d..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml b/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml deleted file mode 100644 index f287fc9f1..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml.cs b/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml.cs deleted file mode 100644 index 0a01ad15c..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/ChatSettingsPage.xaml.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Data_Manager2.Classes; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; - -namespace UWP_XMPP_Client.Pages.SettingsPages -{ - public sealed partial class ChatSettingsPage : Page - { - //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ - #region --Attributes-- - - - #endregion - //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ - #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 29/01/2017 Created [Fabian Sauter] - /// - public ChatSettingsPage() - { - this.InitializeComponent(); - Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += AbstractBackRequestPage_BackRequested; - loadSettings(); - } - - #endregion - //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ - #region --Set-, Get- Methods-- - - - #endregion - //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ - #region --Misc Methods (Public)-- - - - #endregion - - #region --Misc Methods (Private)-- - private void loadSettings() - { - enterToSend_tgls.IsOn = Settings.getSettingBoolean(SettingsConsts.ENTER_TO_SEND_MESSAGES); - sendChatState_tgls.IsOn = !Settings.getSettingBoolean(SettingsConsts.DONT_SEND_CHAT_STATE); - sendChatMessageReceivedMarkers_tgls.IsOn = !Settings.getSettingBoolean(SettingsConsts.DONT_SEND_CHAT_MESSAGE_RECEIVED_MARKERS); - storeImagesInLibary_tgls.IsOn = !Settings.getSettingBoolean(SettingsConsts.DISABLE_DOWNLOAD_IMAGES_TO_LIBARY); - autoJoinMUC_tgls.IsOn = !Settings.getSettingBoolean(SettingsConsts.DISABLE_AUTO_JOIN_MUC); - advancedChatMsgProcessing_tgls.IsOn = !Settings.getSettingBoolean(SettingsConsts.DISABLE_ADVANCED_CHAT_MESSAGE_PROCESSING); - } - - #endregion - - #region --Misc Methods (Protected)-- - - - #endregion - //--------------------------------------------------------Events:---------------------------------------------------------------------\\ - #region --Events-- - private void AbstractBackRequestPage_BackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e) - { - Frame rootFrame = Window.Current.Content as Frame; - if (rootFrame is null) - { - return; - } - if (rootFrame.CanGoBack && e.Handled == false) - { - e.Handled = true; - rootFrame.GoBack(); - } - } - - private void enterToSend_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.ENTER_TO_SEND_MESSAGES, enterToSend_tgls.IsOn); - } - - private void sendChatState_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.DONT_SEND_CHAT_STATE, !sendChatState_tgls.IsOn); - } - - private void storeImagesInLibary_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.DISABLE_DOWNLOAD_IMAGES_TO_LIBARY, !storeImagesInLibary_tgls.IsOn); - } - - private void clearCache_hlb_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) - { - (Window.Current.Content as Frame).Navigate(typeof(MiscSettingsPage)); - } - - private void autoJoinMUC_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.DISABLE_AUTO_JOIN_MUC, !autoJoinMUC_tgls.IsOn); - } - - private void sendChatMarkers_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.DONT_SEND_CHAT_MESSAGE_RECEIVED_MARKERS, !sendChatMessageReceivedMarkers_tgls.IsOn); - } - - private void AdvancedChatMsgProcessing_tgls_Toggled(object sender, RoutedEventArgs e) - { - Settings.setSetting(SettingsConsts.DISABLE_ADVANCED_CHAT_MESSAGE_PROCESSING, !advancedChatMsgProcessing_tgls.IsOn); - } - #endregion - } -} diff --git a/UWP XMPP Client/Pages/SettingsPages/DataSettingsPage.xaml b/UWP XMPP Client/Pages/SettingsPages/DataSettingsPage.xaml deleted file mode 100644 index eb5705eea..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/DataSettingsPage.xaml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml b/UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml deleted file mode 100644 index 6a0e041d8..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/UWP XMPP Client/Pages/SettingsPages/MiscSettingsPage.xaml b/UWP XMPP Client/Pages/SettingsPages/MiscSettingsPage.xaml deleted file mode 100644 index 90295f0ab..000000000 --- a/UWP XMPP Client/Pages/SettingsPages/MiscSettingsPage.xaml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Chat/ChatDetailsControl.xaml.cs b/UWPX_UI/Controls/Chat/ChatDetailsControl.xaml.cs new file mode 100644 index 000000000..bfc0e3960 --- /dev/null +++ b/UWPX_UI/Controls/Chat/ChatDetailsControl.xaml.cs @@ -0,0 +1,195 @@ +using UWP_XMPP_Client.Classes.Events; +using UWP_XMPP_Client.Pages; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Chat +{ + public sealed partial class ChatDetailsControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public ChatDataTemplate Chat + { + get { return (ChatDataTemplate)GetValue(ChatProperty); } + set { SetValue(ChatProperty, value); } + } + public static readonly DependencyProperty ChatProperty = DependencyProperty.Register(nameof(ChatDataTemplate), typeof(ChatDataTemplate), typeof(ChatDetailsControl), new PropertyMetadata(null, ChatPropertyChanged)); + + public bool IsDummy + { + get { return (bool)GetValue(IsDummyProperty); } + set { SetValue(IsDummyProperty, value); } + } + public static readonly DependencyProperty IsDummyProperty = DependencyProperty.Register(nameof(IsDummy), typeof(bool), typeof(ChatDetailsControl), new PropertyMetadata(false, OnIsDummyChanged)); + + public readonly ChatDetailsControlContext VIEW_MODEL = new ChatDetailsControlContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ChatDetailsControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public void UpdateView(DependencyPropertyChangedEventArgs args) + { + VIEW_MODEL.UpdateView(args); + } + + #endregion + + #region --Misc Methods (Private)-- + private void LoadDummyContent() + { + Chat = new ChatDataTemplate + { + Chat = new Data_Manager2.Classes.DBTables.ChatTable("dave@xmpp.uwpx.org", "alice@xmpp.uwpx.org") + { + presence = XMPP_API.Classes.Presence.Away, + status = "ʕノ•ᴥ•ʔノ ︵ ┻━┻", + omemoEnabled = true + } + }; + VIEW_MODEL.LoadDummyContent(Chat.Chat); + } + + private void UpdateIsDummy() + { + VIEW_MODEL.OnIsDummyChanged(IsDummy); + if (IsDummy) + { + LoadDummyContent(); + } + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private static void ChatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ChatDetailsControl detailsControl) + { + detailsControl.UpdateView(e); + } + } + + private void HeaderInfo_grid_RightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e) + { + if (sender is FrameworkElement element) + { + headerInfo_grid.ContextFlyout.ShowAt(element); + } + } + + private void CopyNameText_mfi_Click(object sender, RoutedEventArgs e) + { + UiUtils.SetClipboardText(VIEW_MODEL.MODEL.NameText); + } + + private void CopyChatStatus_mfi_Click(object sender, RoutedEventArgs e) + { + UiUtils.SetClipboardText(VIEW_MODEL.MODEL.StatusText); + } + + private void CopyAccountText_mfi_Click(object sender, RoutedEventArgs e) + { + UiUtils.SetClipboardText(VIEW_MODEL.MODEL.AccountText); + } + + private void Info_mfo_Click(object sender, RoutedEventArgs e) + { + if (!IsDummy) + { + if (Chat.Chat.chatType == Data_Manager2.Classes.ChatType.MUC) + { + UiUtils.NavigateToPage(typeof(MUCInfoPage), new NavigatedToMUCInfoEventArgs(Chat.Chat, Chat.Client, Chat.MucInfo)); + } + else + { + UiUtils.NavigateToPage(typeof(UserProfilePage), new NavigatedToUserProfileEventArgs(Chat.Chat, Chat.Client)); + } + } + } + + private async void Enter_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.EnterMucAsync(Chat); + } + + private async void Leave_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.LeaveMucAsync(Chat); + } + + private void Test_mfo_Click(object sender, RoutedEventArgs e) + { + if (!IsDummy) + { + + } + } + + private void ClipImgLib_btn_Click(object sender, RoutedEventArgs e) + { + + } + + private void ClipImgCam_btn_Click(object sender, RoutedEventArgs e) + { + + } + + private void ClipDraw_btn_Click(object sender, RoutedEventArgs e) + { + + } + + private void ClipFile_btn_Click(object sender, RoutedEventArgs e) + { + + } + + private async void Send_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.SendChatMessageAsync(Chat); + } + + private async void Message_tbx_KeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) + { + await VIEW_MODEL.OnChatMessageKeyDown(e, Chat); + } + + private async void ReadOnOmemo_link_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.OnReadOnOmemoClickedAsync(); + } + + private static void OnIsDummyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ChatDetailsControl chatDetailsControl) + { + chatDetailsControl.UpdateIsDummy(); + } + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Chat/ChatMasterControl.xaml b/UWPX_UI/Controls/Chat/ChatMasterControl.xaml new file mode 100644 index 000000000..4e906e21f --- /dev/null +++ b/UWPX_UI/Controls/Chat/ChatMasterControl.xaml @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Chat/ChatMasterControl.xaml.cs b/UWPX_UI/Controls/Chat/ChatMasterControl.xaml.cs new file mode 100644 index 000000000..0b521d917 --- /dev/null +++ b/UWPX_UI/Controls/Chat/ChatMasterControl.xaml.cs @@ -0,0 +1,219 @@ +using Data_Manager2.Classes; +using System.Threading.Tasks; +using UWP_XMPP_Client.Classes.Events; +using UWP_XMPP_Client.Pages; +using UWPX_UI.Controls.Toolkit.SlidableListItem; +using UWPX_UI.Dialogs; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls.Chat +{ + public sealed partial class ChatMasterControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public ChatDataTemplate Chat + { + get { return (ChatDataTemplate)GetValue(ChatProperty); } + set { SetValue(ChatProperty, value); } + } + public static readonly DependencyProperty ChatProperty = DependencyProperty.Register(nameof(ChatDataTemplate), typeof(ChatDataTemplate), typeof(ChatMasterControl), new PropertyMetadata(null, ChatPropertyChanged)); + + private readonly ChatMasterControlContext VIEW_MODEL; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ChatMasterControl() + { + this.InitializeComponent(); + this.VIEW_MODEL = new ChatMasterControlContext(Resources); + this.VIEW_MODEL.OnError += VIEW_MODEL_OnError; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public void UpdateView(DependencyPropertyChangedEventArgs args) + { + VIEW_MODEL.UpdateView(args); + } + + #endregion + + #region --Misc Methods (Private)-- + private async Task DeleteChatAsync() + { + DeleteChatConfirmDialog dialog = new DeleteChatConfirmDialog(); + await UiUtils.ShowDialogAsync(dialog); + await VIEW_MODEL.DeleteChatAsync(dialog.VIEW_MODEL.MODEL, Chat); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + #region --Presence-- + private async void RequestPresenceSubscription_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.RequestPresenceSubscriptionAsync(Chat); + } + + private async void CancelPresenceSubscription_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.CancelPresenceSubscriptionAsync(Chat); + } + + private async void RejectPresenceSubscription_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.RejectPresenceSubscriptionAsync(Chat); + } + + private async void ProbePresence_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.SendPresenceProbeAsync(Chat); + } + + #endregion + private void UserControl_RightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e) + { + if (Chat is null || Chat.Chat is null) + { + return; + } + switch (Chat.Chat.chatType) + { + case ChatType.CHAT: + chat_mfo.ShowAt(this, e.GetPosition(this)); + break; + case ChatType.MUC: + muc_mfo.ShowAt(this, e.GetPosition(this)); + break; + default: + break; + } + } + + private async void Mute_tmfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.SetChatMutedAsync(Chat, mute_tmfo.IsChecked); + } + + private async void RemoveFromRoster_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.SwitchChatInRosterAsync(Chat); + } + + private async void DeleteChat_mfo_Click(object sender, RoutedEventArgs e) + { + await DeleteChatAsync(); + } + + private void ShowInfo_mfo_Click(object sender, RoutedEventArgs e) + { + + } + + private async void Enter_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.EnterMucAsync(Chat); + } + + private async void Leave_mfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.LeaveMucAsync(Chat); + } + + private void Bookmark_tmfo_Click(object sender, RoutedEventArgs e) + { + VIEW_MODEL.SwitchChatBookmarked(Chat); + } + + private async void MuteMUC_tmfo_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.SetChatMutedAsync(Chat, muteMUC_tmfo.IsChecked); + } + + private async void DeleteMUC_mfo_Click(object sender, RoutedEventArgs e) + { + await DeleteChatAsync(); + } + + private async void SlideListItem_sli_SwipeStatusChanged(SlidableListItem sender, SwipeStatusChangedEventArgs args) + { + if (args.NewValue == SwipeStatus.Idle) + { + if (args.OldValue == SwipeStatus.SwipingPassedLeftThreshold) + { + await DeleteChatAsync(); + } + else if (args.OldValue == SwipeStatus.SwipingPassedRightThreshold) + { + if (Chat.Chat.chatType == ChatType.MUC) + { + VIEW_MODEL.SwitchChatBookmarked(Chat); + } + else + { + await VIEW_MODEL.SwitchChatInRosterAsync(Chat); + } + } + } + } + + private void ShowProfile_mfo_Click(object sender, RoutedEventArgs e) + { + if (Chat.Chat.chatType == ChatType.MUC) + { + UiUtils.NavigateToPage(typeof(MUCInfoPage), new NavigatedToMUCInfoEventArgs(Chat.Chat, Chat.Client, Chat.MucInfo)); + } + else + { + UiUtils.NavigateToPage(typeof(UserProfilePage), new NavigatedToUserProfileEventArgs(Chat.Chat, Chat.Client)); + } + } + + private static void ChatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ChatMasterControl masterControl) + { + masterControl.UpdateView(e); + } + } + + private void VIEW_MODEL_OnError(ChatMasterControlContext sender, UWPX_UI_Context.Classes.Events.OnErrorEventArgs args) + { + InfoDialog dialog = new InfoDialog(args.TITLE, args.MESSAGE) + { + Foreground = new SolidColorBrush(Colors.Red) + }; + } + + private async void AccountActionAccept_ibtn_Click(IconButtonControl sender, RoutedEventArgs args) + { + await VIEW_MODEL.AnswerPresenceSubscriptionRequestAsync(Chat, true); + } + + private async void AccountActionRefuse_ibtn_Click(IconButtonControl sender, RoutedEventArgs args) + { + await VIEW_MODEL.AnswerPresenceSubscriptionRequestAsync(Chat, false); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Chat/SpeechBubbles/Content/IShowFlyoutSpeechBubbleContent.cs b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/IShowFlyoutSpeechBubbleContent.cs new file mode 100644 index 000000000..cd32d539a --- /dev/null +++ b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/IShowFlyoutSpeechBubbleContent.cs @@ -0,0 +1,10 @@ +using Windows.Foundation; +using Windows.UI.Xaml; + +namespace UWPX_UI.Controls.Chat.SpeechBubbles.Content +{ + interface IShowFlyoutSpeechBubbleContent + { + void ShowFlyout(FrameworkElement sender, Point point); + } +} diff --git a/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml new file mode 100644 index 000000000..a6fd9c29d --- /dev/null +++ b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Controls/Chat/SpeechBubbleInfoControl.xaml.cs b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml.cs similarity index 63% rename from UWP XMPP Client/Controls/Chat/SpeechBubbleInfoControl.xaml.cs rename to UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml.cs index 887a8cd4e..04e72962e 100644 --- a/UWP XMPP Client/Controls/Chat/SpeechBubbleInfoControl.xaml.cs +++ b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleErrorStatusBarControl.xaml.cs @@ -1,30 +1,24 @@ -using Data_Manager2.Classes.DBTables; +using UWPX_UI_Context.Classes.DataContext.Controls; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Controls.Chat +namespace UWPX_UI.Controls.Chat.SpeechBubbles.Content { - public sealed partial class SpeechBubbleInfoControl : UserControl + public sealed partial class SpeechBubbleErrorStatusBarControl : UserControl { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public ChatMessageTable ChatMessage + public SpeechBubbleContentControlContext ViewModel { - get { return (ChatMessageTable)GetValue(ChatMessageProperty); } - set { SetValue(ChatMessageProperty, value); } + get { return (SpeechBubbleContentControlContext)GetValue(ViewModelProperty); } + set { SetValue(ViewModelProperty, value); } } - public static readonly DependencyProperty ChatMessageProperty = DependencyProperty.Register(nameof(ChatMessage), typeof(ChatMessageTable), typeof(SpeechBubbleInfoControl), null); + public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(SpeechBubbleContentControlContext), typeof(SpeechBubbleErrorStatusBarControl), new PropertyMetadata(null)); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 12/03/2018 Created [Fabian Sauter] - /// - public SpeechBubbleInfoControl() + public SpeechBubbleErrorStatusBarControl() { this.InitializeComponent(); } diff --git a/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleImageContentControl.xaml b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleImageContentControl.xaml new file mode 100644 index 000000000..76eec5e77 --- /dev/null +++ b/UWPX_UI/Controls/Chat/SpeechBubbles/Content/SpeechBubbleImageContentControl.xaml @@ -0,0 +1,209 @@ + + + + + + + ERROR + + + + + + + DONE + + + + + + + NOT_QUEUED + + + CANCELED + + + + + + + QUEUED + + + DOWNLOADING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/CustomSettingsTitleBarControl.xaml.cs b/UWPX_UI/Controls/CustomSettingsTitleBarControl.xaml.cs new file mode 100644 index 000000000..62c6222ca --- /dev/null +++ b/UWPX_UI/Controls/CustomSettingsTitleBarControl.xaml.cs @@ -0,0 +1,183 @@ +using System; +using UWPX_UI.Pages; +using UWPX_UI.Pages.Settings; +using UWPX_UI_Context.Classes; +using Windows.ApplicationModel.Core; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +namespace UWPX_UI.Controls +{ + public sealed partial class CustomSettingsTitleBarControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public Frame Frame + { + get { return (Frame)GetValue(FrameProperty); } + set { SetValue(FrameProperty, value); } + } + public static readonly DependencyProperty FrameProperty = DependencyProperty.Register(nameof(Frame), typeof(Frame), typeof(CustomSettingsTitleBarControl), new PropertyMetadata(null)); + + public string Glyph + { + get { return (string)GetValue(GlyphProperty); } + set { SetValue(GlyphProperty, value); } + } + public static readonly DependencyProperty GlyphProperty = DependencyProperty.Register(nameof(Glyph), typeof(string), typeof(CustomSettingsTitleBarControl), new PropertyMetadata("\uE9CE")); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(CustomSettingsTitleBarControl), new PropertyMetadata("Text")); + + public Visibility BackRequestButtonVisability + { + get { return (Visibility)GetValue(BackRequestButtonVisabilityProperty); } + set { SetValue(BackRequestButtonVisabilityProperty, value); } + } + public static readonly DependencyProperty BackRequestButtonVisabilityProperty = DependencyProperty.Register(nameof(BackRequestButtonVisability), typeof(Visibility), typeof(CustomSettingsTitleBarControl), new PropertyMetadata(Visibility.Visible)); + + public Type NavigationFallbackPage + { + get { return (Type)GetValue(NavigationFallbackPageProperty); } + set { SetValue(NavigationFallbackPageProperty, value); } + } + public static readonly DependencyProperty NavigationFallbackPageProperty = DependencyProperty.Register(nameof(NavigationFallbackPage), typeof(Type), typeof(CustomSettingsTitleBarControl), new PropertyMetadata(typeof(ChatPage))); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public CustomSettingsTitleBarControl() + { + this.InitializeComponent(); + SetupTitleBar(); + SetupKeyboardAccelerators(); + SystemNavigationManager.GetForCurrentView().BackRequested += CustomTitleBarControl_BackRequested; + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void SetupTitleBar() + { + if (UiUtils.IsApplicationViewApiAvailable()) + { + if (!DeviceFamilyHelper.ShouldShowBackButton()) + { + BackRequestButtonVisability = Visibility.Collapsed; + } + + UpdateTitleBarLayout(); + } + } + + private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args) + { + UpdateTitleBarLayout(sender); + } + + private void UpdateTitleBarLayout() + { + if (DeviceFamilyHelper.IsRunningOnDesktopDevice()) + { + // Set XAML element as a draggable region. + CoreApplicationViewTitleBar titleBar = CoreApplication.GetCurrentView().TitleBar; + UpdateTitleBarLayout(titleBar); + Window.Current.SetTitleBar(titleBar_grid); + titleBar.LayoutMetricsChanged += TitleBar_LayoutMetricsChanged; + } + } + + private void UpdateTitleBarLayout(CoreApplicationViewTitleBar titleBar) + { + // Get the size of the caption controls area and back button + // (returned in logical pixels), and move your content around as necessary. + leftPaddingColumn.Width = new GridLength(titleBar.SystemOverlayLeftInset); + rightPaddingColumn.Width = new GridLength(titleBar.SystemOverlayRightInset); + titleBar_grid.Margin = new Thickness(0, -1, 0, 0); + + // Update title bar control size as needed to account for system size changes. + titleBar_grid.Height = titleBar.Height; + } + + private void SetupKeyboardAccelerators() + { + if (UiUtils.IsKeyboardAcceleratorApiAvailable()) + { + foreach (KeyboardAccelerator accelerator in UiUtils.GetGoBackKeyboardAccelerators()) + { + accelerator.Invoked += Accelerator_Invoked; + KeyboardAccelerators.Add(accelerator); + } + } + } + + private void Accelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) + { + if (!args.Handled) + { + args.Handled = true; + OnGoBackRequested(); + } + } + + private bool OnGoBackRequested() + { + if (!UiUtils.OnGoBackRequested(Frame) && !(NavigationFallbackPage is null)) + { + bool b = UiUtils.NavigateToPage(NavigationFallbackPage); + UiUtils.RemoveLastBackStackEntry(); + return b; + } + return false; + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void BackRequest_btn_Click(object sender, RoutedEventArgs e) + { + OnGoBackRequested(); + } + + private void CustomTitleBarControl_BackRequested(object sender, BackRequestedEventArgs e) + { + if (!e.Handled) + { + e.Handled = OnGoBackRequested(); + } + } + + private void GoToOverview_btn_Click(object sender, RoutedEventArgs e) + { + UiUtils.NavigateToPage(typeof(SettingsPage)); + } + + private void UserControl_GettingFocus(UIElement sender, GettingFocusEventArgs args) + { + UpdateTitleBarLayout(); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/CustomTitleBarControl.xaml b/UWPX_UI/Controls/CustomTitleBarControl.xaml new file mode 100644 index 000000000..92c800c6c --- /dev/null +++ b/UWPX_UI/Controls/CustomTitleBarControl.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/IconButtonControl.xaml.cs b/UWPX_UI/Controls/IconButtonControl.xaml.cs new file mode 100644 index 000000000..c932b1ac2 --- /dev/null +++ b/UWPX_UI/Controls/IconButtonControl.xaml.cs @@ -0,0 +1,81 @@ +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls +{ + public sealed partial class IconButtonControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string Glyph + { + get { return (string)GetValue(GlyphProperty); } + set { SetValue(GlyphProperty, value); } + } + public static readonly DependencyProperty GlyphProperty = DependencyProperty.Register(nameof(Glyph), typeof(string), typeof(IconButtonControl), new PropertyMetadata("")); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(IconButtonControl), new PropertyMetadata("")); + + public Brush GlyphForeground + { + get { return (Brush)GetValue(GlyphForegroundProperty); } + set { SetValue(GlyphForegroundProperty, value); } + } + public static readonly DependencyProperty GlyphForegroundProperty = DependencyProperty.Register(nameof(GlyphForeground), typeof(Brush), typeof(IconButtonControl), new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["SystemAccentColor"]))); + + public Thickness GlyphMargin + { + get { return (Thickness)GetValue(GlyphMarginProperty); } + set { SetValue(GlyphMarginProperty, value); } + } + public static readonly DependencyProperty GlyphMarginProperty = DependencyProperty.Register(nameof(GlyphMargin), typeof(Thickness), typeof(IconButtonControl), new PropertyMetadata(new Thickness(0))); + + public delegate void ClickHandler(IconButtonControl sender, RoutedEventArgs args); + public event ClickHandler Click; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public IconButtonControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Button_Click(object sender, RoutedEventArgs e) + { + Click?.Invoke(this, e); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/IconProgressButtonControl.xaml b/UWPX_UI/Controls/IconProgressButtonControl.xaml new file mode 100644 index 000000000..ff28f732d --- /dev/null +++ b/UWPX_UI/Controls/IconProgressButtonControl.xaml @@ -0,0 +1,39 @@ + + + + diff --git a/UWPX_UI/Controls/IconProgressButtonControl.xaml.cs b/UWPX_UI/Controls/IconProgressButtonControl.xaml.cs new file mode 100644 index 000000000..2f1684f9d --- /dev/null +++ b/UWPX_UI/Controls/IconProgressButtonControl.xaml.cs @@ -0,0 +1,88 @@ +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls +{ + public sealed partial class IconProgressButtonControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string Glyph + { + get { return (string)GetValue(GlyphProperty); } + set { SetValue(GlyphProperty, value); } + } + public static readonly DependencyProperty GlyphProperty = DependencyProperty.Register(nameof(Glyph), typeof(string), typeof(IconProgressButtonControl), new PropertyMetadata("")); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(IconProgressButtonControl), new PropertyMetadata("")); + + public Brush GlyphForeground + { + get { return (Brush)GetValue(GlyphForegroundProperty); } + set { SetValue(GlyphForegroundProperty, value); } + } + public static readonly DependencyProperty GlyphForegroundProperty = DependencyProperty.Register(nameof(GlyphForeground), typeof(Brush), typeof(IconProgressButtonControl), new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["SystemAccentColor"]))); + + public Thickness GlyphMargin + { + get { return (Thickness)GetValue(GlyphMarginProperty); } + set { SetValue(GlyphMarginProperty, value); } + } + public static readonly DependencyProperty GlyphMarginProperty = DependencyProperty.Register(nameof(GlyphMargin), typeof(Thickness), typeof(IconProgressButtonControl), new PropertyMetadata(new Thickness(0))); + + public Visibility ProgressRingVisibility + { + get { return (Visibility)GetValue(ProgressRingVisibilityProperty); } + set { SetValue(ProgressRingVisibilityProperty, value); } + } + public static readonly DependencyProperty ProgressRingVisibilityProperty = DependencyProperty.Register(nameof(ProgressRingVisibility), typeof(Visibility), typeof(IconProgressButtonControl), new PropertyMetadata(Visibility.Collapsed)); + + public delegate void ClickHandler(IconProgressButtonControl sender, RoutedEventArgs args); + public event ClickHandler Click; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public IconProgressButtonControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Button_Click(object sender, RoutedEventArgs e) + { + Click?.Invoke(this, e); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/IconTextBlockControl.xaml b/UWPX_UI/Controls/IconTextBlockControl.xaml new file mode 100644 index 000000000..ef664c07d --- /dev/null +++ b/UWPX_UI/Controls/IconTextBlockControl.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/UWPX_UI/Controls/IconTextBlockControl.xaml.cs b/UWPX_UI/Controls/IconTextBlockControl.xaml.cs new file mode 100644 index 000000000..79a66d5bb --- /dev/null +++ b/UWPX_UI/Controls/IconTextBlockControl.xaml.cs @@ -0,0 +1,75 @@ +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls +{ + public sealed partial class IconTextBlockControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string Glyph + { + get { return (string)GetValue(GlyphProperty); } + set { SetValue(GlyphProperty, value); } + } + public static readonly DependencyProperty GlyphProperty = DependencyProperty.Register(nameof(Glyph), typeof(string), typeof(IconTextBlockControl), new PropertyMetadata("")); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(IconTextBlockControl), new PropertyMetadata("")); + + public Brush GlyphForeground + { + get { return (Brush)GetValue(GlyphForegroundProperty); } + set { SetValue(GlyphForegroundProperty, value); } + } + public static readonly DependencyProperty GlyphForegroundProperty = DependencyProperty.Register(nameof(GlyphForeground), typeof(Brush), typeof(IconButtonControl), new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["SystemAccentColor"]))); + + public Style TextBlockStyle + { + get { return (Style)GetValue(TextBlockStyleProperty); } + set { SetValue(TextBlockStyleProperty, value); } + } + public static readonly DependencyProperty TextBlockStyleProperty = DependencyProperty.Register(nameof(TextBlockStyle), typeof(Style), typeof(IconButtonControl), new PropertyMetadata(null)); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public IconTextBlockControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/UWPX_UI/Controls/LoadingContentControl.xaml b/UWPX_UI/Controls/LoadingContentControl.xaml new file mode 100644 index 000000000..5d593ea7d --- /dev/null +++ b/UWPX_UI/Controls/LoadingContentControl.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/UWPX_UI/Controls/LoadingContentControl.xaml.cs b/UWPX_UI/Controls/LoadingContentControl.xaml.cs new file mode 100644 index 000000000..6b39b4265 --- /dev/null +++ b/UWPX_UI/Controls/LoadingContentControl.xaml.cs @@ -0,0 +1,52 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls +{ + public sealed partial class LoadingContentControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(LoadingContentControl), new PropertyMetadata("Loading...")); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public LoadingContentControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml b/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml new file mode 100644 index 000000000..b741ed86b --- /dev/null +++ b/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml.cs b/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml.cs new file mode 100644 index 000000000..f76b8dc84 --- /dev/null +++ b/UWPX_UI/Controls/OMEMO/OmemoDeviceListControl.xaml.cs @@ -0,0 +1,75 @@ +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.OMEMO +{ + public sealed partial class OmemoDeviceListControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public AccountDataTemplate Account + { + get { return (AccountDataTemplate)GetValue(AccountProperty); } + set { SetValue(AccountProperty, value); } + } + public static readonly DependencyProperty AccountProperty = DependencyProperty.Register(nameof(Account), typeof(AccountDataTemplate), typeof(OmemoDeviceListControl), new PropertyMetadata(null, OnAccountChanged)); + + public readonly OmemoDeviceListControlContext VIEW_MODEL = new OmemoDeviceListControlContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public OmemoDeviceListControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void UpdateView(DependencyPropertyChangedEventArgs e) + { + VIEW_MODEL.UpdateView(e); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Reset_ibtn_Click(object sender, RoutedEventArgs args) + { + VIEW_MODEL.ResetOmemoDevices(Account.Client); + } + + private void Refresh_ibtn_Click(object sender, RoutedEventArgs args) + { + VIEW_MODEL.RefreshOmemoDevices(Account.Client); + } + + private static void OnAccountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is OmemoDeviceListControl omemoDeviceListControl) + { + omemoDeviceListControl.UpdateView(e); + } + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/OMEMO/OmemoFingerprintControl.xaml b/UWPX_UI/Controls/OMEMO/OmemoFingerprintControl.xaml new file mode 100644 index 000000000..abd943d90 --- /dev/null +++ b/UWPX_UI/Controls/OMEMO/OmemoFingerprintControl.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/AccountInfoCertificateGeneralControl.xaml.cs b/UWPX_UI/Controls/Settings/AccountInfoCertificateGeneralControl.xaml.cs new file mode 100644 index 000000000..6f4e4215f --- /dev/null +++ b/UWPX_UI/Controls/Settings/AccountInfoCertificateGeneralControl.xaml.cs @@ -0,0 +1,70 @@ +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class AccountInfoCertificateGeneralControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public AccountDataTemplate Account + { + get { return (AccountDataTemplate)GetValue(AccountProperty); } + set { SetValue(AccountProperty, value); } + } + public static readonly DependencyProperty AccountProperty = DependencyProperty.Register(nameof(Account), typeof(AccountDataTemplate), typeof(AccountInfoCertificateGeneralControl), new PropertyMetadata(null, OnAccountChanged)); + + public readonly AccountInfoCertificateGeneralControlContext VIEW_MODEL = new AccountInfoCertificateGeneralControlContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public AccountInfoCertificateGeneralControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void UpdateViewModel(DependencyPropertyChangedEventArgs e) + { + VIEW_MODEL.UpdateViewModel(e); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private static void OnAccountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is AccountInfoCertificateGeneralControl control) + { + control.UpdateViewModel(e); + } + } + + private async void ExportCert_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ExportCertificateAsync(); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml b/UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml new file mode 100644 index 000000000..67484d5b5 --- /dev/null +++ b/UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml.cs b/UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml.cs similarity index 53% rename from UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml.cs rename to UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml.cs index 682acb498..8e2a00a54 100644 --- a/UWP XMPP Client/Controls/Chat/SpeechBubbleTopControl.xaml.cs +++ b/UWPX_UI/Controls/Settings/AccountInfoGeneralControl.xaml.cs @@ -1,37 +1,27 @@ -using Data_Manager2.Classes.DBTables; +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Controls.Chat +namespace UWPX_UI.Controls.Settings { - public sealed partial class SpeechBubbleTopControl : UserControl + public sealed partial class AccountInfoGeneralControl : UserControl { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public ChatMessageTable ChatMessage + public AccountDataTemplate Account { - get { return (ChatMessageTable)GetValue(ChatMessageProperty); } - set { SetValue(ChatMessageProperty, value); } + get { return (AccountDataTemplate)GetValue(AccountProperty); } + set { SetValue(AccountProperty, value); } } - public static readonly DependencyProperty ChatMessageProperty = DependencyProperty.Register(nameof(ChatMessage), typeof(ChatMessageTable), typeof(SpeechBubbleTopControl), null); + public static readonly DependencyProperty AccountProperty = DependencyProperty.Register(nameof(Account), typeof(AccountDataTemplate), typeof(AccountInfoGeneralControl), new PropertyMetadata(null, OnAccountChanged)); - public ChatTable Chat - { - get { return (ChatTable)GetValue(ChatProperty); } - set { SetValue(ChatProperty, value); } - } - public static readonly DependencyProperty ChatProperty = DependencyProperty.Register(nameof(Chat), typeof(ChatTable), typeof(SpeechBubbleTopControl), null); + public readonly AccountInfoGeneralControlContext VIEW_MODEL = new AccountInfoGeneralControlContext(); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 29/08/2017 Created [Fabian Sauter] - /// - public SpeechBubbleTopControl() + public AccountInfoGeneralControl() { this.InitializeComponent(); } @@ -49,7 +39,10 @@ public SpeechBubbleTopControl() #endregion #region --Misc Methods (Private)-- - + private void UpdateViewModel(DependencyPropertyChangedEventArgs e) + { + VIEW_MODEL.UpdateViewModel(e); + } #endregion @@ -59,7 +52,13 @@ public SpeechBubbleTopControl() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - + private static void OnAccountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is AccountInfoGeneralControl control) + { + control.UpdateViewModel(e); + } + } #endregion } diff --git a/UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml b/UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml new file mode 100644 index 000000000..6b3b9c019 --- /dev/null +++ b/UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml.cs b/UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml.cs similarity index 54% rename from UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml.cs rename to UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml.cs index 9d732fba9..f799fe5a1 100644 --- a/UWP XMPP Client/Pages/SettingsPages/DonateSettingsPage.xaml.cs +++ b/UWPX_UI/Controls/Settings/AccountOmemoInfoControl.xaml.cs @@ -1,30 +1,28 @@ -using System; -using System.Collections.ObjectModel; -using UWP_XMPP_Client.Classes; -using Windows.Services.Store; +using UWPX_UI_Context.Classes.DataContext.Controls; +using UWPX_UI_Context.Classes.DataTemplates; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Pages.SettingsPages +namespace UWPX_UI.Controls.Settings { - public sealed partial class DonateSettingsPage : Page + public sealed partial class AccountOmemoInfoControl : UserControl { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - private readonly ObservableCollection PRODUCTS; + public AccountDataTemplate Account + { + get { return (AccountDataTemplate)GetValue(AccountProperty); } + set { SetValue(AccountProperty, value); } + } + public static readonly DependencyProperty AccountProperty = DependencyProperty.Register(nameof(Account), typeof(AccountDataTemplate), typeof(AccountOmemoInfoControl), new PropertyMetadata(null, OnAccountChanged)); + + public readonly AccountOmemoInfoControlContext VIEW_MODEL = new AccountOmemoInfoControlContext(); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 15/04/2018 Created [Fabian Sauter] - /// - public DonateSettingsPage() + public AccountOmemoInfoControl() { - this.PRODUCTS = new ObservableCollection(); this.InitializeComponent(); } @@ -41,7 +39,10 @@ public DonateSettingsPage() #endregion #region --Misc Methods (Private)-- - + private void UpdateView(DependencyPropertyChangedEventArgs e) + { + VIEW_MODEL.UpdateView(e); + } #endregion @@ -51,19 +52,12 @@ public DonateSettingsPage() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private async void donateLP_btn_Click(object sender, RoutedEventArgs e) - { - await UiUtils.launchUriAsync(new Uri("http://liberapay.uwpx.org")); - } - - private async void donatePP_btn_Click(object sender, RoutedEventArgs e) - { - await UiUtils.launchUriAsync(new Uri("http://paypal.uwpx.org")); - } - - private async void sendMail_link_Click(object sender, RoutedEventArgs e) + private static void OnAccountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - await UiUtils.launchUriAsync(new Uri("mailto:support@uwpx.org?subject=[Donation] Bank detail")); + if (d is AccountOmemoInfoControl omemoInfoControl) + { + omemoInfoControl.UpdateView(e); + } } #endregion diff --git a/UWPX_UI/Controls/Settings/AccountsListControl.xaml b/UWPX_UI/Controls/Settings/AccountsListControl.xaml new file mode 100644 index 000000000..bb485a70b --- /dev/null +++ b/UWPX_UI/Controls/Settings/AccountsListControl.xaml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/AccountsListControl.xaml.cs b/UWPX_UI/Controls/Settings/AccountsListControl.xaml.cs new file mode 100644 index 000000000..f62f9f948 --- /dev/null +++ b/UWPX_UI/Controls/Settings/AccountsListControl.xaml.cs @@ -0,0 +1,50 @@ +using UWPX_UI_Context.Classes.DataContext.Controls; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class AccountsListControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly AccountsListControlContext VIEW_MODEL = new AccountsListControlContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public AccountsListControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void AccountLimit_hlbtn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + accountLimit_hlbtn.ContextFlyout.ShowAt(accountLimit_hlbtn); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Settings/FolderSizeControl.xaml b/UWPX_UI/Controls/Settings/FolderSizeControl.xaml new file mode 100644 index 000000000..0dcf335e6 --- /dev/null +++ b/UWPX_UI/Controls/Settings/FolderSizeControl.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/UWP XMPP Client/Controls/SettingsHeaderControl.xaml.cs b/UWPX_UI/Controls/Settings/FolderSizeControl.xaml.cs similarity index 52% rename from UWP XMPP Client/Controls/SettingsHeaderControl.xaml.cs rename to UWPX_UI/Controls/Settings/FolderSizeControl.xaml.cs index dba4ee066..d302eeaa1 100644 --- a/UWP XMPP Client/Controls/SettingsHeaderControl.xaml.cs +++ b/UWPX_UI/Controls/Settings/FolderSizeControl.xaml.cs @@ -1,40 +1,27 @@ -using UWP_XMPP_Client.Pages; +using System.Threading.Tasks; +using UWPX_UI_Context.Classes.DataContext.Controls; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Input; -namespace UWP_XMPP_Client.Controls +namespace UWPX_UI.Controls.Settings { - public sealed partial class SettingsHeaderControl : UserControl + public sealed partial class FolderSizeControl : UserControl { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public string Icon + public string FolderPath { - get { return (string)GetValue(IconProperty); } - set { SetValue(IconProperty, value); } + get { return (string)GetValue(FolderPathProperty); } + set { SetValue(FolderPathProperty, value); } } - public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(string), typeof(SettingsHeaderControl), new PropertyMetadata(0)); - - public string Text - { - get { return (string)GetValue(TextProperty); } - set { SetValue(TextProperty, value); } - } - public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(SettingsHeaderControl), new PropertyMetadata(0)); - + public static readonly DependencyProperty FolderPathProperty = DependencyProperty.Register(nameof(FolderPath), typeof(string), typeof(FolderSizeControl), new PropertyMetadata(null, OnFolderPathChanged)); + private readonly FolderSizeControlContext VIEW_MODEL = new FolderSizeControlContext(); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 03/09/2017 Created [Fabian Sauter] - /// - public SettingsHeaderControl() + public FolderSizeControl() { this.InitializeComponent(); } @@ -47,12 +34,18 @@ public SettingsHeaderControl() #endregion //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ #region --Misc Methods (Public)-- - + public async Task RecalculateFolderSizeAsync() + { + await VIEW_MODEL.RecalculateFolderSizeAsync(FolderPath); + } #endregion #region --Misc Methods (Private)-- - + private async Task UpdateViewAsync(DependencyPropertyChangedEventArgs e) + { + await VIEW_MODEL.UpdateViewAsync(e); + } #endregion @@ -62,9 +55,12 @@ public SettingsHeaderControl() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private void Grid_Tapped(object sender, TappedRoutedEventArgs e) + private static async void OnFolderPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - (Window.Current.Content as Frame).Navigate(typeof(SettingsPage)); + if (d is FolderSizeControl folderSizeControl) + { + await folderSizeControl.UpdateViewAsync(e); + } } #endregion diff --git a/UWPX_UI/Controls/Settings/LogLevelControl.xaml b/UWPX_UI/Controls/Settings/LogLevelControl.xaml new file mode 100644 index 000000000..5cb52fe20 --- /dev/null +++ b/UWPX_UI/Controls/Settings/LogLevelControl.xaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/LogLevelControl.xaml.cs b/UWPX_UI/Controls/Settings/LogLevelControl.xaml.cs new file mode 100644 index 000000000..a422f1ebe --- /dev/null +++ b/UWPX_UI/Controls/Settings/LogLevelControl.xaml.cs @@ -0,0 +1,51 @@ +using UWPX_UI_Context.Classes.DataContext.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class LogLevelControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly LogLevelControlContext VIEW_MODEL = new LogLevelControlContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public LogLevelControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void LogLevelDebug_btn_Click(object sender, RoutedEventArgs e) + { + + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Settings/SettingsContentControl.xaml b/UWPX_UI/Controls/Settings/SettingsContentControl.xaml new file mode 100644 index 000000000..64af72d05 --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsContentControl.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/SettingsContentControl.xaml.cs b/UWPX_UI/Controls/Settings/SettingsContentControl.xaml.cs new file mode 100644 index 000000000..d92138a22 --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsContentControl.xaml.cs @@ -0,0 +1,59 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class SettingsContentControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string Header + { + get { return (string)GetValue(HeaderProperty); } + set { SetValue(HeaderProperty, value); } + } + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(nameof(Header), typeof(string), typeof(SettingsContentControl), new PropertyMetadata("")); + + public object MainContent + { + get { return GetValue(MainContentProperty); } + set { SetValue(MainContentProperty, value); } + } + public static readonly DependencyProperty MainContentProperty = DependencyProperty.Register(nameof(MainContent), typeof(object), typeof(SettingsContentControl), new PropertyMetadata(null)); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public SettingsContentControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml b/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml new file mode 100644 index 000000000..85a29ae35 --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml.cs b/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml.cs new file mode 100644 index 000000000..c6ebe253e --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsSelectionLargeControl.xaml.cs @@ -0,0 +1,63 @@ +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class SettingsSelectionLargeControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public SettingsPageButtonDataTemplate Model + { + get { return (SettingsPageButtonDataTemplate)GetValue(ModelProperty); } + set { SetValue(ModelProperty, value); } + } + public static readonly DependencyProperty ModelProperty = DependencyProperty.Register(nameof(Model), typeof(SettingsPageButtonDataTemplate), typeof(SettingsSelectionLargeControl), new PropertyMetadata(new SettingsPageButtonDataTemplate() + { + Description = "Description", + Glyph = "\uE9CE", + Name = "Name", + NavTarget = null + })); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public SettingsSelectionLargeControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Button_Click(object sender, RoutedEventArgs e) + { + UiUtils.NavigateToPage(Model?.NavTarget); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml b/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml new file mode 100644 index 000000000..9f9f85519 --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml @@ -0,0 +1,52 @@ + + + + + + + + diff --git a/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml.cs b/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml.cs new file mode 100644 index 000000000..8ece44cc7 --- /dev/null +++ b/UWPX_UI/Controls/Settings/SettingsSelectionSmallControl.xaml.cs @@ -0,0 +1,63 @@ +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Settings +{ + public sealed partial class SettingsSelectionSmallControl : UserControl + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public SettingsPageButtonDataTemplate Model + { + get { return (SettingsPageButtonDataTemplate)GetValue(ModelProperty); } + set { SetValue(ModelProperty, value); } + } + public static readonly DependencyProperty ModelProperty = DependencyProperty.Register(nameof(Model), typeof(SettingsPageButtonDataTemplate), typeof(SettingsSelectionSmallControl), new PropertyMetadata(new SettingsPageButtonDataTemplate() + { + Description = "Description", + Glyph = "\uE9CE", + Name = "Name", + NavTarget = null + })); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public SettingsSelectionSmallControl() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Button_Click(object sender, RoutedEventArgs e) + { + UiUtils.NavigateToPage(Model?.NavTarget); + } + + #endregion + } +} diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/BackButtonBehavior.cs b/UWPX_UI/Controls/Toolkit/MasterDetailsView/BackButtonBehavior.cs new file mode 100644 index 000000000..301037b68 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/BackButtonBehavior.cs @@ -0,0 +1,41 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MasterDetailsView/BackButtonBehavior.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace UWPX_UI.Controls.Toolkit.MasterDetailsView +{ + /// + /// The behavior to use for navigating between the master and details views + /// + public enum BackButtonBehavior + { + /// + /// Automatically determine the best approach to use. + /// + /// + /// If the back button controlled by is already visible, the will hook into that button. + /// If the new NavigationView provided by the Windows UI NuGet package is used, the will enable and show that button. + /// Otherwise the inline button is used. + /// + Automatic, + + /// + /// Use a back button built into the + /// + Inline, + + /// + /// Use the system back button controlled by the . + /// + System, + + /// + /// Do not enable any back buttons. Use this if you plan to handle all navigation or have your own back button in the application. + /// + Manual, + } +} diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Events.cs b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Events.cs new file mode 100644 index 000000000..3e10df8a7 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Events.cs @@ -0,0 +1,35 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MasterDetailsView/MasterDetailsView.Events.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Controls.Toolkit.MasterDetailsView +{ + /// + /// Panel that allows for a Master/Details pattern. + /// + /// + public partial class MasterDetailsView + { + /// + /// Occurs when the currently selected item changes. + /// + public event SelectionChangedEventHandler SelectionChanged; + + /// + /// Occurs when the view state changes + /// + public event EventHandler ViewStateChanged; + + private void OnSelectionChanged(SelectionChangedEventArgs e) + { + SelectionChanged?.Invoke(this, e); + } + } +} diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Properties.cs b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Properties.cs new file mode 100644 index 000000000..35d6b7e35 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.Properties.cs @@ -0,0 +1,334 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MasterDetailsView/MasterDetailsView.Properties.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls.Toolkit.MasterDetailsView +{ + /// + /// Panel that allows for a Master/Details pattern. + /// + /// + public partial class MasterDetailsView + { + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( + nameof(SelectedItem), + typeof(object), + typeof(MasterDetailsView), + new PropertyMetadata(null, OnSelectedItemChanged)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsTemplateProperty = DependencyProperty.Register( + nameof(DetailsTemplate), + typeof(DataTemplate), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty MasterPaneBackgroundProperty = DependencyProperty.Register( + nameof(MasterPaneBackground), + typeof(Brush), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty MasterHeaderProperty = DependencyProperty.Register( + nameof(MasterHeader), + typeof(object), + typeof(MasterDetailsView), + new PropertyMetadata(null, OnMasterHeaderChanged)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty MasterHeaderTemplateProperty = DependencyProperty.Register( + nameof(MasterHeaderTemplate), + typeof(DataTemplate), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsHeaderProperty = DependencyProperty.Register( + nameof(DetailsHeader), + typeof(object), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsHeaderTemplateProperty = DependencyProperty.Register( + nameof(DetailsHeaderTemplate), + typeof(DataTemplate), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty MasterPaneWidthProperty = DependencyProperty.Register( + nameof(MasterPaneWidth), + typeof(double), + typeof(MasterDetailsView), + new PropertyMetadata(321d)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty NoSelectionContentProperty = DependencyProperty.Register( + nameof(NoSelectionContent), + typeof(object), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty NoSelectionContentTemplateProperty = DependencyProperty.Register( + nameof(NoSelectionContentTemplate), + typeof(DataTemplate), + typeof(MasterDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty ViewStateProperty = DependencyProperty.Register( + nameof(ViewState), + typeof(MasterDetailsViewState), + typeof(MasterDetailsView), + new PropertyMetadata(default(MasterDetailsViewState))); + + /// + /// Identifies the dependency property + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty MasterCommandBarProperty = DependencyProperty.Register( + nameof(MasterCommandBar), + typeof(CommandBar), + typeof(MasterDetailsView), + new PropertyMetadata(null, OnMasterCommandBarChanged)); + + /// + /// Identifies the dependency property + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsCommandBarProperty = DependencyProperty.Register( + nameof(DetailsCommandBar), + typeof(CommandBar), + typeof(MasterDetailsView), + new PropertyMetadata(null, OnDetailsCommandBarChanged)); + + /// + /// Identifies the dependency property + /// + public static readonly DependencyProperty CompactModeThresholdWidthProperty = DependencyProperty.Register( + nameof(CompactModeThresholdWidth), + typeof(double), + typeof(MasterDetailsView), + new PropertyMetadata(640d, OnCompactModeThresholdWidthChanged)); + + /// + /// Identifies the dependency property + /// + public static readonly DependencyProperty BackButtonBehaviorProperty = DependencyProperty.Register( + nameof(BackButtonBehavior), + typeof(BackButtonBehavior), + typeof(MasterDetailsView), + new PropertyMetadata(BackButtonBehavior.System, OnBackButtonBehaviorChanged)); + + /// + /// Gets or sets the selected item. + /// + /// The selected item. The default is null. + public object SelectedItem + { + get { return GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + + /// + /// Gets or sets the DataTemplate used to display the details. + /// + public DataTemplate DetailsTemplate + { + get { return (DataTemplate)GetValue(DetailsTemplateProperty); } + set { SetValue(DetailsTemplateProperty, value); } + } + + /// + /// Gets or sets the Brush to apply to the background of the list area of the control. + /// + /// The Brush to apply to the background of the list area of the control. + public Brush MasterPaneBackground + { + get { return (Brush)GetValue(MasterPaneBackgroundProperty); } + set { SetValue(MasterPaneBackgroundProperty, value); } + } + + /// + /// Gets or sets the content for the master pane's header + /// + /// + /// The content of the master pane's header. The default is null. + /// + public object MasterHeader + { + get { return GetValue(MasterHeaderProperty); } + set { SetValue(MasterHeaderProperty, value); } + } + + /// + /// Gets or sets the DataTemplate used to display the content of the master pane's header. + /// + /// + /// The template that specifies the visualization of the master pane header object. The default is null. + /// + public DataTemplate MasterHeaderTemplate + { + get { return (DataTemplate)GetValue(MasterHeaderTemplateProperty); } + set { SetValue(MasterHeaderTemplateProperty, value); } + } + + /// + /// Gets or sets the content for the details pane's header + /// + /// + /// The content of the details pane's header. The default is null. + /// + public object DetailsHeader + { + get { return GetValue(DetailsHeaderProperty); } + set { SetValue(DetailsHeaderProperty, value); } + } + + /// + /// Gets or sets the DataTemplate used to display the content of the details pane's header. + /// + /// + /// The template that specifies the visualization of the details pane header object. The default is null. + /// + public DataTemplate DetailsHeaderTemplate + { + get { return (DataTemplate)GetValue(DetailsHeaderTemplateProperty); } + set { SetValue(DetailsHeaderTemplateProperty, value); } + } + + /// + /// Gets or sets the width of the master pane when the view is expanded. + /// + /// + /// The width of the SplitView pane when it's fully expanded. The default is 320 + /// device-independent pixel (DIP). + /// + public double MasterPaneWidth + { + get { return (double)GetValue(MasterPaneWidthProperty); } + set { SetValue(MasterPaneWidthProperty, value); } + } + + /// + /// Gets or sets the content to display when there is no item selected in the master list. + /// + public object NoSelectionContent + { + get { return GetValue(NoSelectionContentProperty); } + set { SetValue(NoSelectionContentProperty, value); } + } + + /// + /// Gets or sets the DataTemplate used to display the content when there is no selection. + /// + /// + /// The template that specifies the visualization of the content when there is no + /// selection. The default is null. + /// + public DataTemplate NoSelectionContentTemplate + { + get { return (DataTemplate)GetValue(NoSelectionContentTemplateProperty); } + set { SetValue(NoSelectionContentTemplateProperty, value); } + } + + /// + /// Gets the current visual state of the control + /// + public MasterDetailsViewState ViewState + { + get { return (MasterDetailsViewState)GetValue(ViewStateProperty); } + private set { SetValue(ViewStateProperty, value); } + } + + /// + /// Gets or sets the for the master section. + /// + public CommandBar MasterCommandBar + { + get { return (CommandBar)GetValue(MasterCommandBarProperty); } + set { SetValue(MasterCommandBarProperty, value); } + } + + /// + /// Gets or sets the for the details section. + /// + public CommandBar DetailsCommandBar + { + get { return (CommandBar)GetValue(DetailsCommandBarProperty); } + set { SetValue(DetailsCommandBarProperty, value); } + } + + /// + /// Gets or sets the Threshold width that will trigger the control to go into compact mode + /// + public double CompactModeThresholdWidth + { + get { return (double)GetValue(CompactModeThresholdWidthProperty); } + set { SetValue(CompactModeThresholdWidthProperty, value); } + } + + /// + /// Gets or sets the behavior to use for the back button + /// + /// The current BackButtonBehavior. The default is System. + public BackButtonBehavior BackButtonBehavior + { + get { return (BackButtonBehavior)GetValue(BackButtonBehaviorProperty); } + set { SetValue(BackButtonBehaviorProperty, value); } + } + + /// + /// Gets or sets a function for mapping the selected item to a different model. + /// This new model will be the DataContext of the Details area. + /// + public Func MapDetails { get; set; } + } +} diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.cs b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.cs new file mode 100644 index 000000000..9001714c4 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.cs @@ -0,0 +1,557 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MasterDetailsView/MasterDetailsView.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Windows.ApplicationModel; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Navigation; + +namespace UWPX_UI.Controls.Toolkit.MasterDetailsView +{ + /// + /// Panel that allows for a Master/Details pattern. + /// + [TemplatePart(Name = PartDetailsPresenter, Type = typeof(ContentPresenter))] + [TemplatePart(Name = PartDetailsPanel, Type = typeof(FrameworkElement))] + [TemplateVisualState(Name = NoSelectionNarrowState, GroupName = SelectionStates)] + [TemplateVisualState(Name = NoSelectionWideState, GroupName = SelectionStates)] + [TemplateVisualState(Name = HasSelectionWideState, GroupName = SelectionStates)] + [TemplateVisualState(Name = HasSelectionNarrowState, GroupName = SelectionStates)] + [TemplateVisualState(Name = NarrowState, GroupName = WidthStates)] + [TemplateVisualState(Name = WideState, GroupName = WidthStates)] + public partial class MasterDetailsView : ItemsControl + { + private const string PartDetailsPresenter = "DetailsPresenter"; + private const string PartDetailsPanel = "DetailsPanel"; + private const string PartBackButton = "MasterDetailsBackButton"; + private const string PartHeaderContentPresenter = "HeaderContentPresenter"; + private const string NarrowState = "NarrowState"; + private const string WideState = "WideState"; + private const string WidthStates = "WidthStates"; + private const string SelectionStates = "SelectionStates"; + private const string HasSelectionNarrowState = "HasSelectionNarrow"; + private const string HasSelectionWideState = "HasSelectionWide"; + private const string NoSelectionNarrowState = "NoSelectionNarrow"; + private const string NoSelectionWideState = "NoSelectionWide"; + + private AppViewBackButtonVisibility? _previousSystemBackButtonVisibility; + private bool _previousNavigationViewBackEnabled; + + // Int used because the underlying type is an enum, but we don't have access to the enum + private int _previousNavigationViewBackVisibilty; + private ContentPresenter _detailsPresenter; + private VisualStateGroup _selectionStateGroup; + private Button _inlineBackButton; + private object _navigationView; + private Frame _frame; + private bool ignoreClearSelectedItem; + + /// + /// Initializes a new instance of the class. + /// + public MasterDetailsView() + { + DefaultStyleKey = typeof(MasterDetailsView); + + Loaded += OnLoaded; + Unloaded += OnUnloaded; + } + + /// + /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call + /// ApplyTemplate. In simplest terms, this means the method is called just before a UI element displays + /// in your app. Override this method to influence the default post-template logic of a class. + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (_inlineBackButton != null) + { + _inlineBackButton.Click -= OnInlineBackButtonClicked; + } + + _inlineBackButton = (Button)GetTemplateChild(PartBackButton); + if (_inlineBackButton != null) + { + _inlineBackButton.Click += OnInlineBackButtonClicked; + } + + _detailsPresenter = (ContentPresenter)GetTemplateChild(PartDetailsPresenter); + SetDetailsContent(); + + SetMasterHeaderVisibility(); + OnDetailsCommandBarChanged(); + OnMasterCommandBarChanged(); + + SizeChanged -= MasterDetailsView_SizeChanged; + SizeChanged += MasterDetailsView_SizeChanged; + + UpdateView(true); + } + + /// + /// Fired when the SelectedItem changes. + /// + /// The sender + /// The event args + /// + /// Sets up animations for the DetailsPresenter for animating in/out. + /// + private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (MasterDetailsView)d; + + // Prevent setting the SelectedItem to null if only the order changed (=> collection reset got triggered). + if (!view.ignoreClearSelectedItem && !(e.OldValue is null) && e.NewValue is null && view.Items.Contains(e.OldValue)) + { + view.SelectedItem = e.OldValue; + return; + } + + view.OnSelectionChanged(new SelectionChangedEventArgs(new List { e.OldValue }, new List { e.NewValue })); + + view.UpdateView(true); + + // If there is no selection, do not remove the DetailsPresenter content but let it animate out. + if (view.SelectedItem != null) + { + view.SetDetailsContent(); + } + } + + /// + /// Fired when the is changed. + /// + /// The sender + /// The event args + private static void OnMasterHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (MasterDetailsView)d; + view.SetMasterHeaderVisibility(); + } + + /// + /// Fired when the DetailsCommandBar changes. + /// + /// The sender + /// The event args + private static void OnDetailsCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (MasterDetailsView)d; + view.OnDetailsCommandBarChanged(); + } + + /// + /// Fired when CompactModeThresholdWIdthChanged + /// + /// The sender + /// The event args + private static void OnCompactModeThresholdWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((MasterDetailsView)d).HandleStateChanges(); + } + + private static void OnBackButtonBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (MasterDetailsView)d; + view.SetBackButtonVisibility(); + } + + /// + /// Fired when the MasterCommandBar changes. + /// + /// The sender + /// The event args + private static void OnMasterCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (MasterDetailsView)d; + view.OnMasterCommandBarChanged(); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + if (DesignMode.DesignModeEnabled == false) + { + SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; + if (_frame != null) + { + _frame.Navigating -= OnFrameNavigating; + } + + _navigationView = this.FindAscendants().FirstOrDefault(p => p.GetType().FullName == "Microsoft.UI.Xaml.Controls.NavigationView"); + _frame = this.FindAscendant(); + if (_frame != null) + { + _frame.Navigating += OnFrameNavigating; + } + + _selectionStateGroup = (VisualStateGroup)GetTemplateChild(SelectionStates); + if (_selectionStateGroup != null) + { + _selectionStateGroup.CurrentStateChanged += OnSelectionStateChanged; + } + + UpdateView(true); + } + } + + public void ClearSelectedItem() + { + ignoreClearSelectedItem = true; + SelectedItem = null; + ignoreClearSelectedItem = false; + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + if (DesignMode.DesignModeEnabled == false) + { + SystemNavigationManager.GetForCurrentView().BackRequested -= OnBackRequested; + if (_frame != null) + { + _frame.Navigating -= OnFrameNavigating; + } + + _selectionStateGroup = (VisualStateGroup)GetTemplateChild(SelectionStates); + if (_selectionStateGroup != null) + { + _selectionStateGroup.CurrentStateChanged -= OnSelectionStateChanged; + _selectionStateGroup = null; + } + } + } + + private void MasterDetailsView_SizeChanged(object sender, SizeChangedEventArgs e) + { + // if size is changing + if ((e.PreviousSize.Width < CompactModeThresholdWidth && e.NewSize.Width >= CompactModeThresholdWidth) || + (e.PreviousSize.Width >= CompactModeThresholdWidth && e.NewSize.Width < CompactModeThresholdWidth)) + { + HandleStateChanges(); + } + } + + private void OnInlineBackButtonClicked(object sender, RoutedEventArgs e) + { + SelectedItem = null; + } + + private void HandleStateChanges() + { + UpdateView(true); + SetListSelectionWithKeyboardFocusOnVisualStateChanged(ViewState); + } + + /// + /// Closes the details pane if we are in narrow state + /// + /// The sender + /// The event args + private void OnFrameNavigating(object sender, NavigatingCancelEventArgs args) + { + if ((args.NavigationMode == NavigationMode.Back) && (ViewState == MasterDetailsViewState.Details)) + { + SelectedItem = null; + args.Cancel = true; + } + } + + /// + /// Closes the details pane if we are in narrow state + /// + /// The sender + /// The event args + private void OnBackRequested(object sender, BackRequestedEventArgs args) + { + if (ViewState == MasterDetailsViewState.Details) + { + // let the OnFrameNavigating method handle it if + if (_frame == null || !_frame.CanGoBack) + { + SelectedItem = null; + } + + args.Handled = true; + } + } + + private void SetMasterHeaderVisibility() + { + if (GetTemplateChild(PartHeaderContentPresenter) is FrameworkElement headerPresenter) + { + headerPresenter.Visibility = MasterHeader != null + ? Visibility.Visible + : Visibility.Collapsed; + } + } + + private void UpdateView(bool animate) + { + UpdateViewState(); + SetVisualState(animate); + } + + /// + /// Sets the back button visibility based on the current visual state and selected item + /// + private void SetBackButtonVisibility(MasterDetailsViewState? previousState = null) + { + const int backButtonVisible = 1; + + if (DesignMode.DesignModeEnabled) + { + return; + } + + if (ViewState == MasterDetailsViewState.Details) + { + if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + { + _inlineBackButton.Visibility = Visibility.Visible; + } + else if (BackButtonBehavior == BackButtonBehavior.Automatic) + { + // Continue to support the system back button if it is being used + var navigationManager = SystemNavigationManager.GetForCurrentView(); + if (navigationManager.AppViewBackButtonVisibility == AppViewBackButtonVisibility.Visible) + { + // Setting this indicates that the system back button is being used + _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; + } + else if ((_navigationView == null) || (_frame == null)) + { + // We can only use the new NavigationView if we also have a Frame + // If there is no frame we have to use the inline button + _inlineBackButton.Visibility = Visibility.Visible; + } + else + { + SetNavigationViewBackButtonState(backButtonVisible, true); + } + } + else if (BackButtonBehavior != BackButtonBehavior.Manual) + { + var navigationManager = SystemNavigationManager.GetForCurrentView(); + _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; + + navigationManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; + } + } + else if (previousState == MasterDetailsViewState.Details) + { + if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + { + _inlineBackButton.Visibility = Visibility.Collapsed; + } + else if (BackButtonBehavior == BackButtonBehavior.Automatic) + { + if (_previousSystemBackButtonVisibility.HasValue == false) + { + if ((_navigationView == null) || (_frame == null)) + { + _inlineBackButton.Visibility = Visibility.Collapsed; + } + else + { + SetNavigationViewBackButtonState(_previousNavigationViewBackVisibilty, _previousNavigationViewBackEnabled); + } + } + } + + if (_previousSystemBackButtonVisibility.HasValue) + { + // Make sure we show the back button if the stack can navigate back + SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = _previousSystemBackButtonVisibility.Value; + _previousSystemBackButtonVisibility = null; + } + } + } + + private void UpdateViewState() + { + var previousState = ViewState; + + if (ActualWidth < CompactModeThresholdWidth) + { + ViewState = SelectedItem == null ? MasterDetailsViewState.Master : MasterDetailsViewState.Details; + } + else + { + ViewState = MasterDetailsViewState.Both; + } + + if (previousState != ViewState) + { + ViewStateChanged?.Invoke(this, ViewState); + SetBackButtonVisibility(previousState); + } + } + + private void SetVisualState(bool animate) + { + string state; + string noSelectionState; + string hasSelectionState; + if (ActualWidth < CompactModeThresholdWidth) + { + state = NarrowState; + noSelectionState = NoSelectionNarrowState; + hasSelectionState = HasSelectionNarrowState; + } + else + { + state = WideState; + noSelectionState = NoSelectionWideState; + hasSelectionState = HasSelectionWideState; + } + + VisualStateManager.GoToState(this, SelectedItem == null ? noSelectionState : hasSelectionState, animate); + VisualStateManager.GoToState(this, state, animate); + } + + private void SetNavigationViewBackButtonState(int visible, bool enabled) + { + var navType = _navigationView.GetType(); + var visibleProperty = navType.GetProperty("IsBackButtonVisible"); + if (visibleProperty != null) + { + _previousNavigationViewBackVisibilty = (int)visibleProperty.GetValue(_navigationView); + visibleProperty.SetValue(_navigationView, visible); + } + + var enabledProperty = navType.GetProperty("IsBackEnabled"); + if (enabledProperty != null) + { + _previousNavigationViewBackEnabled = (bool)enabledProperty.GetValue(_navigationView); + enabledProperty.SetValue(_navigationView, enabled); + } + } + + private void SetDetailsContent() + { + if (_detailsPresenter != null) + { + _detailsPresenter.Content = MapDetails == null + ? SelectedItem + : SelectedItem != null ? MapDetails(SelectedItem) : null; + } + } + + private void OnMasterCommandBarChanged() + { + OnCommandBarChanged("MasterCommandBarPanel", MasterCommandBar); + } + + private void OnDetailsCommandBarChanged() + { + OnCommandBarChanged("DetailsCommandBarPanel", DetailsCommandBar); + } + + private void OnCommandBarChanged(string panelName, CommandBar commandbar) + { + var panel = GetTemplateChild(panelName) as Panel; + if (panel == null) + { + return; + } + + panel.Children.Clear(); + if (commandbar != null) + { + panel.Children.Add(commandbar); + } + } + + /// + /// Sets whether the selected item should change when focused with the keyboard based on the view state + /// + /// the view state + private void SetListSelectionWithKeyboardFocusOnVisualStateChanged(MasterDetailsViewState viewState) + { + if (viewState == MasterDetailsViewState.Both) + { + SetListSelectionWithKeyboardFocus(true); + } + else + { + SetListSelectionWithKeyboardFocus(false); + } + } + + /// + /// Sets whether the selected item should change when focused with the keyboard + /// + private void SetListSelectionWithKeyboardFocus(bool singleSelectionFollowsFocus) + { + if (GetTemplateChild("MasterList") is Windows.UI.Xaml.Controls.ListViewBase masterList) + { + masterList.SingleSelectionFollowsFocus = singleSelectionFollowsFocus; + } + } + + /// + /// Fires when the selection state of the control changes + /// + /// the sender + /// the event args + /// + /// Sets focus to the item list when the viewState is not Details. + /// Sets whether the selected item should change when focused with the keyboard. + /// + private void OnSelectionStateChanged(object sender, VisualStateChangedEventArgs e) + { + SetFocus(ViewState); + SetListSelectionWithKeyboardFocusOnVisualStateChanged(ViewState); + } + + /// + /// Sets focus to the relevant control based on the viewState. + /// + /// the view state + private void SetFocus(MasterDetailsViewState viewState) + { + if (viewState != MasterDetailsViewState.Details) + { + FocusItemList(); + } + else + { + FocusFirstFocusableElementInDetails(); + } + } + + /// + /// Sets focus to the first focusable element in the details template + /// + private void FocusFirstFocusableElementInDetails() + { + if (GetTemplateChild(PartDetailsPanel) is DependencyObject details) + { + var focusableElement = FocusManager.FindFirstFocusableElement(details); + (focusableElement as Control)?.Focus(FocusState.Programmatic); + } + } + + /// + /// Sets focus to the item list + /// + private void FocusItemList() + { + if (GetTemplateChild("MasterList") is Control masterList) + { + masterList.Focus(FocusState.Programmatic); + } + } + } +} diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.xaml b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.xaml new file mode 100644 index 000000000..8ace49f37 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsView.xaml @@ -0,0 +1,211 @@ + + + + + \ No newline at end of file diff --git a/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsViewState.cs b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsViewState.cs new file mode 100644 index 000000000..65c890915 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/MasterDetailsView/MasterDetailsViewState.cs @@ -0,0 +1,31 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MasterDetailsView/MasterDetailsViewState.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace UWPX_UI.Controls.Toolkit.MasterDetailsView +{ + /// + /// The state. + /// + public enum MasterDetailsViewState + { + /// + /// Only the Master view is shown + /// + Master, + + /// + /// Only the Details view is shown + /// + Details, + + /// + /// Both the Master and Details views are shown + /// + Both + } +} diff --git a/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.cs b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.cs new file mode 100644 index 000000000..6140390d9 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.cs @@ -0,0 +1,955 @@ +// Backport of the SlidableListItem from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/v4.0.0/Microsoft.Toolkit.Uwp.UI.Controls/SlidableListItem/SlidableListItem.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Toolkit.Uwp.UI.Animations; +using System; +using System.Windows.Input; +using Windows.Devices.Input; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Animation; + +namespace UWPX_UI.Controls.Toolkit.SlidableListItem +{ + /// + /// ContentControl providing functionality for sliding left or right to expose functions + /// + [TemplatePart(Name = PartContentGrid, Type = typeof(Grid))] + [TemplatePart(Name = PartCommandContainer, Type = typeof(Grid))] + [TemplatePart(Name = PartLeftCommandPanel, Type = typeof(StackPanel))] + [TemplatePart(Name = PartRightCommandPanel, Type = typeof(StackPanel))] + public class SlidableListItem : ContentControl + { + /// + /// Identifies the property + /// + public static readonly DependencyProperty ExtraSwipeThresholdProperty = + DependencyProperty.Register(nameof(ExtraSwipeThreshold), typeof(int), typeof(SlidableListItem), new PropertyMetadata(default(int))); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty IsOffsetLimitedProperty = + DependencyProperty.Register(nameof(IsOffsetLimited), typeof(bool), typeof(SlidableListItem), new PropertyMetadata(true)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty IsRightCommandEnabledProperty = + DependencyProperty.Register(nameof(IsRightCommandEnabled), typeof(bool), typeof(SlidableListItem), new PropertyMetadata(true, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty IsLeftCommandEnabledProperty = + DependencyProperty.Register(nameof(IsLeftCommandEnabled), typeof(bool), typeof(SlidableListItem), new PropertyMetadata(true, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty ActivationWidthProperty = + DependencyProperty.Register(nameof(ActivationWidth), typeof(double), typeof(SlidableListItem), new PropertyMetadata(80)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftIconProperty = + DependencyProperty.Register(nameof(LeftIcon), typeof(Symbol), typeof(SlidableListItem), new PropertyMetadata(Symbol.Favorite, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightIconProperty = + DependencyProperty.Register(nameof(RightIcon), typeof(Symbol), typeof(SlidableListItem), new PropertyMetadata(Symbol.Delete, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftLabelProperty = + DependencyProperty.Register(nameof(LeftLabel), typeof(string), typeof(SlidableListItem), new PropertyMetadata(string.Empty, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightLabelProperty = + DependencyProperty.Register(nameof(RightLabel), typeof(string), typeof(SlidableListItem), new PropertyMetadata(string.Empty, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftForegroundProperty = + DependencyProperty.Register(nameof(LeftForeground), typeof(Brush), typeof(SlidableListItem), new PropertyMetadata(new SolidColorBrush(Colors.White), OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightForegroundProperty = + DependencyProperty.Register(nameof(RightForeground), typeof(Brush), typeof(SlidableListItem), new PropertyMetadata(new SolidColorBrush(Colors.White), OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftBackgroundProperty = + DependencyProperty.Register(nameof(LeftBackground), typeof(Brush), typeof(SlidableListItem), new PropertyMetadata(new SolidColorBrush(Colors.LightGray), OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightBackgroundProperty = + DependencyProperty.Register(nameof(RightBackground), typeof(Brush), typeof(SlidableListItem), new PropertyMetadata(new SolidColorBrush(Colors.DarkGray), OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty MouseSlidingEnabledProperty = + DependencyProperty.Register(nameof(MouseSlidingEnabled), typeof(bool), typeof(SlidableListItem), new PropertyMetadata(false)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftCommandProperty = + DependencyProperty.Register(nameof(LeftCommand), typeof(ICommand), typeof(SlidableListItem), new PropertyMetadata(null, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightCommandProperty = + DependencyProperty.Register(nameof(RightCommand), typeof(ICommand), typeof(SlidableListItem), new PropertyMetadata(null, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty LeftCommandParameterProperty = + DependencyProperty.Register(nameof(LeftCommandParameter), typeof(object), typeof(SlidableListItem), new PropertyMetadata(null, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty RightCommandParameterProperty = + DependencyProperty.Register(nameof(RightCommandParameter), typeof(object), typeof(SlidableListItem), new PropertyMetadata(null, OnSwipeControlValueChanged)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty SwipeStatusProperty = + DependencyProperty.Register(nameof(SwipeStatus), typeof(object), typeof(SlidableListItem), new PropertyMetadata(SwipeStatus.Idle)); + + /// + /// Identifies the property + /// + public static readonly DependencyProperty IsPointerReleasedOnSwipingHandledProperty = + DependencyProperty.Register("IsPointerReleasedOnSwipingHandled", typeof(bool), typeof(SlidableListItem), new PropertyMetadata(false)); + + /// + /// Identifies the dependency property + /// + public static readonly DependencyProperty UseSwipeControlWhenPossibleProperty = + DependencyProperty.Register("UseSwipeControlWhenPossible", typeof(bool), typeof(SlidableListItem), new PropertyMetadata(false, OnUseSwipeControlWhenPossibleChanged)); + + /// + /// Occurs when SwipeStatus has changed + /// + public event TypedEventHandler SwipeStatusChanged; + + private const string PartContentGrid = "ContentGrid"; + private const string PartCommandContainer = "CommandContainer"; + private const string PartLeftCommandPanel = "LeftCommandPanel"; + private const string PartRightCommandPanel = "RightCommandPanel"; + private const int FinishAnimationDuration = 150; + private const int SnappedCommandMargin = 20; + private const int AnimationSetDuration = 200; + + private Grid _contentGrid; + private CompositeTransform _transform; + private Grid _commandContainer; + private CompositeTransform _commandContainerTransform; + private DoubleAnimation _commandContainerClipTranslateAnimation; + private StackPanel _leftCommandPanel; + private CompositeTransform _leftCommandTransform; + private StackPanel _rightCommandPanel; + private CompositeTransform _rightCommandTransform; + private DoubleAnimation _contentAnimation; + private Storyboard _contentStoryboard; + private AnimationSet _leftCommandAnimationSet; + private AnimationSet _rightCommandAnimationSet; + + private ControlTemplate _previousTemplateUsed; + private object _swipeControl; + private object _leftSwipeItems; + private object _leftSwipeItem; + private object _rightSwipeItems; + private object _rightSwipeItem; + + private bool UsingSwipeControl => UseSwipeControlWhenPossible && IsSwipeControlSupported; + + /// + /// Gets a value indicating whether is supported + /// + public static bool IsSwipeControlSupported { get; } = ApiInformation.IsTypePresent("Windows.UI.Xaml.Controls.SwipeControl"); + + /// + /// Initializes a new instance of the class. + /// Creates a new instance of + /// + public SlidableListItem() + { + DefaultStyleKey = typeof(SlidableListItem); + } + + /// + /// Occurs when the user swipes to the left to activate the right action + /// + public event EventHandler RightCommandRequested; + + /// + /// Occurs when the user swipes to the right to activate the left action + /// + public event EventHandler LeftCommandRequested; + + /// + /// Invoked whenever application code or internal processes (such as a rebuilding + /// layout pass) call . In simplest terms, this means the method + /// is called just before a UI element displays in an application. Override this + /// method to influence the default post-template logic of a class. + /// + protected override void OnApplyTemplate() + { + if (_contentGrid != null) + { + _contentGrid.PointerReleased -= ContentGrid_PointerReleased; + _contentGrid.ManipulationStarted -= ContentGrid_ManipulationStarted; + _contentGrid.ManipulationDelta -= ContentGrid_ManipulationDelta; + _contentGrid.ManipulationCompleted -= ContentGrid_ManipulationCompleted; + } + + _contentGrid = GetTemplateChild(PartContentGrid) as Grid; + + if (_contentGrid != null) + { + _contentGrid.PointerReleased += ContentGrid_PointerReleased; + + _transform = _contentGrid.RenderTransform as CompositeTransform; + _contentGrid.ManipulationStarted += ContentGrid_ManipulationStarted; + _contentGrid.ManipulationDelta += ContentGrid_ManipulationDelta; + _contentGrid.ManipulationCompleted += ContentGrid_ManipulationCompleted; + } + + if (UsingSwipeControl) + { + OnApplyTemplateSwipeControl(); + } + + Loaded += SlidableListItem_Loaded; + Unloaded += SlidableListItem_Unloaded; + + base.OnApplyTemplate(); + } + + private static void OnUseSwipeControlWhenPossibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var slider = d as SlidableListItem; + if (slider == null) + { + return; + } + + if (slider.UseSwipeControlWhenPossible && SlidableListItem.IsSwipeControlSupported) + { + ResourceDictionary dict = new ResourceDictionary(); + dict.Source = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/SlidableListItem/SlidableListItemSwipeControlTemplate.xaml"); + slider._previousTemplateUsed = slider.Template; + slider.Template = dict["SlidableListItemSwipeControlStyle"] as ControlTemplate; + } + else if (!slider.UseSwipeControlWhenPossible && + e.OldValue is bool oldValue && + oldValue) + { + if (slider._previousTemplateUsed != null) + { + slider.Template = slider._previousTemplateUsed; + } + else + { + ResourceDictionary dict = new ResourceDictionary(); + dict.Source = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/SlidableListItem/SlidableListItem.xaml"); + slider.Template = dict["SlidableListItemDefaultTemplate"] as ControlTemplate; + } + } + } + + private static void OnSwipeControlValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is SlidableListItem control && control.UsingSwipeControl) + { + control.UpdateSwipeControlItems(); + } + } + + private void OnApplyTemplateSwipeControl() + { + if (GetTemplateChild("SwipeControl") is SwipeControl swipeControl) + { + _swipeControl = swipeControl; + UpdateSwipeControlItems(); + } + } + + private void UpdateSwipeControlItems() + { + var swipeControl = _swipeControl as SwipeControl; + if (swipeControl == null) + { + return; + } + + if (IsLeftCommandEnabled) + { + var leftItem = _leftSwipeItem as SwipeItem; + var leftItems = _leftSwipeItems as SwipeItems; + + if (leftItem == null) + { + leftItem = new SwipeItem(); + leftItem.IconSource = new SymbolIconSource(); + leftItem.Invoked += LeftControl_Invoked; + + leftItems = new SwipeItems() + { + leftItem + }; + leftItems.Mode = SwipeMode.Execute; + + _leftSwipeItems = leftItems; + _leftSwipeItem = leftItem; + } + + leftItem.BehaviorOnInvoked = SwipeBehaviorOnInvoked.Close; + leftItem.Background = LeftBackground; + leftItem.Text = LeftLabel; + leftItem.Foreground = LeftForeground; + leftItem.Command = LeftCommand; + leftItem.CommandParameter = LeftCommandParameter; + leftItem.IconSource.Foreground = LeftForeground; + ((SymbolIconSource)leftItem.IconSource).Symbol = LeftIcon; + + swipeControl.LeftItems = leftItems; + } + else + { + swipeControl.LeftItems = null; + } + + if (IsRightCommandEnabled) + { + var rightItem = _rightSwipeItem as SwipeItem; + var rightItems = _rightSwipeItems as SwipeItems; + + if (rightItem == null) + { + rightItem = new SwipeItem(); + rightItem.IconSource = new SymbolIconSource(); + rightItem.Invoked += RightControl_Invoked; + + rightItems = new SwipeItems() + { + rightItem + }; + rightItems.Mode = SwipeMode.Execute; + + _rightSwipeItems = rightItems; + _rightSwipeItem = rightItem; + } + + rightItem.BehaviorOnInvoked = SwipeBehaviorOnInvoked.Close; + rightItem.Background = RightBackground; + rightItem.Text = RightLabel; + rightItem.Foreground = RightForeground; + rightItem.Command = RightCommand; + rightItem.CommandParameter = RightCommandParameter; + rightItem.IconSource.Foreground = RightForeground; + ((SymbolIconSource)rightItem.IconSource).Symbol = RightIcon; + + swipeControl.RightItems = rightItems; + } + else + { + swipeControl.RightItems = null; + } + } + + private void LeftControl_Invoked(SwipeItem sender, SwipeItemInvokedEventArgs args) + { + LeftCommandRequested?.Invoke(this, EventArgs.Empty); + } + + private void RightControl_Invoked(SwipeItem sender, SwipeItemInvokedEventArgs args) + { + RightCommandRequested?.Invoke(this, EventArgs.Empty); + } + + private void SlidableListItem_Loaded(object sender, RoutedEventArgs e) + { + if (_contentStoryboard != null) + { + _contentStoryboard.Completed += ContentStoryboard_Completed; + } + } + + private void SlidableListItem_Unloaded(object sender, RoutedEventArgs e) + { + if (_contentStoryboard != null) + { + _contentStoryboard.Completed -= ContentStoryboard_Completed; + } + } + + private void ContentGrid_PointerReleased(object sender, PointerRoutedEventArgs e) + { + if (SwipeStatus != SwipeStatus.Idle && IsPointerReleasedOnSwipingHandled) + { + e.Handled = true; + } + } + + private void ContentGrid_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + if ((!MouseSlidingEnabled && e.PointerDeviceType == PointerDeviceType.Mouse) || (!IsLeftCommandEnabled && !IsRightCommandEnabled)) + { + return; + } + + if (_contentStoryboard == null) + { + _contentAnimation = new DoubleAnimation(); + Storyboard.SetTarget(_contentAnimation, _transform); + Storyboard.SetTargetProperty(_contentAnimation, "TranslateX"); + _contentAnimation.To = 0; + _contentAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(FinishAnimationDuration)); + + _contentStoryboard = new Storyboard(); + _contentStoryboard.Children.Add(_contentAnimation); + + _contentStoryboard.Completed += ContentStoryboard_Completed; + } + + if (_commandContainer == null) + { + _commandContainer = GetTemplateChild(PartCommandContainer) as Grid; + if (_commandContainer != null) + { + _commandContainer.Background = LeftBackground as Brush; + _commandContainer.Clip = new RectangleGeometry(); + _commandContainerTransform = new CompositeTransform(); + _commandContainer.Clip.Transform = _commandContainerTransform; + + _commandContainerClipTranslateAnimation = new DoubleAnimation(); + Storyboard.SetTarget(_commandContainerClipTranslateAnimation, _commandContainerTransform); + Storyboard.SetTargetProperty(_commandContainerClipTranslateAnimation, "TranslateX"); + _commandContainerClipTranslateAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(FinishAnimationDuration)); + _contentStoryboard.Children.Add(_commandContainerClipTranslateAnimation); + } + } + + if (_leftCommandPanel == null) + { + _leftCommandPanel = GetTemplateChild(PartLeftCommandPanel) as StackPanel; + if (_leftCommandPanel != null) + { + _leftCommandTransform = _leftCommandPanel.RenderTransform as CompositeTransform; + } + } + + if (_rightCommandPanel == null) + { + _rightCommandPanel = GetTemplateChild(PartRightCommandPanel) as StackPanel; + if (_rightCommandPanel != null) + { + _rightCommandTransform = _rightCommandPanel.RenderTransform as CompositeTransform; + } + } + + _contentStoryboard.Stop(); + _commandContainer.Opacity = 0; + _commandContainerTransform.TranslateX = 0; + _transform.TranslateX = 0; + SwipeStatus = SwipeStatus.Starting; + } + + /// + /// Handler for when slide manipulation is complete + /// + private void ContentGrid_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + if (SwipeStatus == SwipeStatus.Idle) + { + return; + } + + var x = _transform.TranslateX; + _contentAnimation.From = x; + _commandContainerClipTranslateAnimation.From = 0; + _commandContainerClipTranslateAnimation.To = -x; + _contentStoryboard.Begin(); + + if (SwipeStatus == SwipeStatus.SwipingPassedLeftThreshold) + { + RightCommandRequested?.Invoke(this, EventArgs.Empty); + RightCommand?.Execute(RightCommandParameter); + } + else if (SwipeStatus == SwipeStatus.SwipingPassedRightThreshold) + { + LeftCommandRequested?.Invoke(this, EventArgs.Empty); + LeftCommand?.Execute(LeftCommandParameter); + } + + Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { SwipeStatus = SwipeStatus.Idle; }).AsTask(); + } + + /// + /// Handler for when slide manipulation is underway + /// + private void ContentGrid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + if (SwipeStatus == SwipeStatus.Idle) + { + return; + } + + var newTranslationX = _transform.TranslateX + e.Delta.Translation.X; + bool swipingInDisabledArea = false; + SwipeStatus newSwipeStatus = SwipeStatus.Idle; + + if (newTranslationX > 0) + { + // Swiping from left to right + if (!IsLeftCommandEnabled) + { + // If swipe is not enabled, only allow swipe a very short distance + if (newTranslationX > 0) + { + swipingInDisabledArea = true; + newSwipeStatus = SwipeStatus.DisabledSwipingToRight; + } + + if (newTranslationX > 16) + { + newTranslationX = 16; + } + } + else if (IsOffsetLimited) + { + // If offset is limited, there will be a limit how much swipe is possible. + // This will be the value of the command panel plus some threshold. + // This value can't be less than the ActivationWidth. + var swipeThreshold = _leftCommandPanel.ActualWidth + ExtraSwipeThreshold; + if (swipeThreshold < ActivationWidth) + { + swipeThreshold = ActivationWidth; + } + + if (Math.Abs(newTranslationX) > swipeThreshold) + { + newTranslationX = swipeThreshold; + } + } + + // Don't allow swiping more than almost the whole content grid width + // (doing this will cause the control to change size). + if (newTranslationX > (_contentGrid.ActualWidth - 4)) + { + newTranslationX = _contentGrid.ActualWidth - 4; + } + } + else + { + // Swiping from right to left + if (!IsRightCommandEnabled) + { + // If swipe is not enabled, only allow swipe a very short distance + if (newTranslationX < 0) + { + swipingInDisabledArea = true; + newSwipeStatus = SwipeStatus.DisabledSwipingToLeft; + } + + if (newTranslationX < -16) + { + newTranslationX = -16; + } + } + else if (IsOffsetLimited) + { + // If offset is limited, there will be a limit how much swipe is possible. + // This will be the value of the command panel plus some threshold. + // This value can't be less than the ActivationWidth. + var swipeThreshold = _rightCommandPanel.ActualWidth + ExtraSwipeThreshold; + if (swipeThreshold < ActivationWidth) + { + swipeThreshold = ActivationWidth; + } + + if (Math.Abs(newTranslationX) > swipeThreshold) + { + newTranslationX = -swipeThreshold; + } + } + + // Don't allow swiping more than almost the whole content grid width + // (doing this will cause the control to change size). + if (newTranslationX < -(_contentGrid.ActualWidth - 4)) + { + newTranslationX = -(_contentGrid.ActualWidth - 4); + } + } + + bool hasPassedThreshold = !swipingInDisabledArea && Math.Abs(newTranslationX) >= ActivationWidth; + + if (swipingInDisabledArea) + { + // Don't show any command if swiping in disabled area. + _commandContainer.Opacity = 0; + _leftCommandPanel.Opacity = 0; + _rightCommandPanel.Opacity = 0; + } + else if (newTranslationX > 0) + { + // If swiping from left to right, show left command panel. + _rightCommandPanel.Opacity = 0; + + _commandContainer.Background = LeftBackground as Brush; + _commandContainer.Opacity = 1; + _leftCommandPanel.Opacity = 1; + + _commandContainer.Clip.Rect = new Rect(0, 0, Math.Max(newTranslationX - 1, 0), _commandContainer.ActualHeight); + + if (newTranslationX < ActivationWidth) + { + _leftCommandAnimationSet?.Stop(); + _leftCommandPanel.RenderTransform = _leftCommandTransform; + _leftCommandTransform.TranslateX = newTranslationX / 2; + + newSwipeStatus = SwipeStatus.SwipingToRightThreshold; + } + else + { + if (SwipeStatus == SwipeStatus.SwipingToRightThreshold) + { + // The control was just put below the threshold. + // Run an animation to put the text and icon + // in the correct position. + _leftCommandAnimationSet = _leftCommandPanel.Offset((float)(SnappedCommandMargin - _leftCommandTransform.TranslateX), duration: AnimationSetDuration); + _leftCommandAnimationSet.Start(); + } + else if (SwipeStatus != SwipeStatus.SwipingPassedRightThreshold) + { + // This will cover extreme cases when previous state wasn't + // below threshold. + _leftCommandAnimationSet?.Stop(); + _leftCommandPanel.RenderTransform = _leftCommandTransform; + _leftCommandTransform.TranslateX = SnappedCommandMargin; + } + + newSwipeStatus = SwipeStatus.SwipingPassedRightThreshold; + } + } + else + { + // If swiping from right to left, show right command panel. + _leftCommandPanel.Opacity = 0; + + _commandContainer.Background = RightBackground as Brush; + _commandContainer.Opacity = 1; + _rightCommandPanel.Opacity = 1; + + _commandContainer.Clip.Rect = new Rect(_commandContainer.ActualWidth + newTranslationX + 1, 0, Math.Max(-newTranslationX - 1, 0), _commandContainer.ActualHeight); + + if (-newTranslationX < ActivationWidth) + { + _rightCommandAnimationSet?.Stop(); + _rightCommandPanel.RenderTransform = _rightCommandTransform; + _rightCommandTransform.TranslateX = newTranslationX / 2; + + newSwipeStatus = SwipeStatus.SwipingToLeftThreshold; + } + else + { + if (SwipeStatus == SwipeStatus.SwipingToLeftThreshold) + { + // The control was just put below the threshold. + // Run an animation to put the text and icon + // in the correct position. + _rightCommandAnimationSet = _rightCommandPanel.Offset((float)(-SnappedCommandMargin - _rightCommandTransform.TranslateX), duration: AnimationSetDuration); + _rightCommandAnimationSet.Start(); + } + else if (SwipeStatus != SwipeStatus.SwipingPassedLeftThreshold) + { + // This will cover extreme cases when previous state wasn't + // below threshold. + _rightCommandAnimationSet?.Stop(); + _rightCommandPanel.RenderTransform = _rightCommandTransform; + _rightCommandTransform.TranslateX = -SnappedCommandMargin; + } + + newSwipeStatus = SwipeStatus.SwipingPassedLeftThreshold; + } + } + + _transform.TranslateX = newTranslationX; + SwipeStatus = newSwipeStatus; + } + + private void ContentStoryboard_Completed(object sender, object e) + { + _commandContainer.Opacity = 0.0; + } + + /// + /// Gets or sets the amount of extra pixels for swipe threshold when is enabled. + /// + public int ExtraSwipeThreshold + { + get { return (int)GetValue(ExtraSwipeThresholdProperty); } + set { SetValue(ExtraSwipeThresholdProperty, value); } + } + + /// + /// Gets or sets a value indicating whether maximum swipe offset is limited or not. + /// + public bool IsOffsetLimited + { + get { return (bool)GetValue(IsOffsetLimitedProperty); } + set { SetValue(IsOffsetLimitedProperty, value); } + } + + /// + /// Gets or sets a value indicating whether right command is enabled or not. + /// + public bool IsRightCommandEnabled + { + get { return (bool)GetValue(IsRightCommandEnabledProperty); } + set { SetValue(IsRightCommandEnabledProperty, value); } + } + + /// + /// Gets or sets a value indicating whether left command is enabled or not. + /// + public bool IsLeftCommandEnabled + { + get { return (bool)GetValue(IsLeftCommandEnabledProperty); } + set { SetValue(IsLeftCommandEnabledProperty, value); } + } + + /// + /// Gets or sets the amount of pixels the content needs to be swiped for an + /// action to be requested + /// + public double ActivationWidth + { + get { return (double)GetValue(ActivationWidthProperty); } + set { SetValue(ActivationWidthProperty, value); } + } + + /// + /// Gets or sets the left icon symbol + /// + public Symbol LeftIcon + { + get { return (Symbol)GetValue(LeftIconProperty); } + set { SetValue(LeftIconProperty, value); } + } + + /// + /// Gets or sets the right icon symbol + /// + public Symbol RightIcon + { + get { return (Symbol)GetValue(RightIconProperty); } + set { SetValue(RightIconProperty, value); } + } + + /// + /// Gets or sets the left label + /// + public string LeftLabel + { + get { return (string)GetValue(LeftLabelProperty); } + set { SetValue(LeftLabelProperty, value); } + } + + /// + /// Gets or sets the right label + /// + public string RightLabel + { + get { return (string)GetValue(RightLabelProperty); } + set { SetValue(RightLabelProperty, value); } + } + + /// + /// Gets or sets the left foreground color + /// + public Brush LeftForeground + { + get { return (Brush)GetValue(LeftForegroundProperty); } + set { SetValue(LeftForegroundProperty, value); } + } + + /// + /// Gets or sets the right foreground color + /// + public Brush RightForeground + { + get { return (Brush)GetValue(RightForegroundProperty); } + set { SetValue(RightForegroundProperty, value); } + } + + /// + /// Gets or sets the left background color + /// + public Brush LeftBackground + { + get { return (Brush)GetValue(LeftBackgroundProperty); } + set { SetValue(LeftBackgroundProperty, value); } + } + + /// + /// Gets or sets the right background color + /// + public Brush RightBackground + { + get { return (Brush)GetValue(RightBackgroundProperty); } + set { SetValue(RightBackgroundProperty, value); } + } + + /// + /// Gets or sets a value indicating whether it has the ability to slide the control with the mouse. False by default + /// + public bool MouseSlidingEnabled + { + get { return (bool)GetValue(MouseSlidingEnabledProperty); } + set { SetValue(MouseSlidingEnabledProperty, value); } + } + + /// + /// Gets or sets the ICommand for left command request + /// + public ICommand LeftCommand + { + get + { + return (ICommand)GetValue(LeftCommandProperty); + } + + set + { + SetValue(LeftCommandProperty, value); + } + } + + /// + /// Gets or sets the ICommand for right command request + /// + public ICommand RightCommand + { + get + { + return (ICommand)GetValue(RightCommandProperty); + } + + set + { + SetValue(RightCommandProperty, value); + } + } + + /// + /// Gets or sets the CommandParameter for left command request + /// + public object LeftCommandParameter + { + get + { + return GetValue(LeftCommandParameterProperty); + } + + set + { + SetValue(LeftCommandParameterProperty, value); + } + } + + /// + /// Gets or sets the CommandParameter for right command request + /// + public object RightCommandParameter + { + get + { + return GetValue(RightCommandParameterProperty); + } + + set + { + SetValue(RightCommandParameterProperty, value); + } + } + + /// + /// Gets the SwipeStatus for current swipe status + /// + public SwipeStatus SwipeStatus + { + get + { + return (SwipeStatus)GetValue(SwipeStatusProperty); + } + + private set + { + var oldValue = SwipeStatus; + + if (value != oldValue) + { + SetValue(SwipeStatusProperty, value); + + var eventArguments = new SwipeStatusChangedEventArgs() + { + OldValue = oldValue, + NewValue = value + }; + + SwipeStatusChanged?.Invoke(this, eventArguments); + } + } + } + + /// + /// Gets or sets a value indicating whether the PointerReleased event is handled when swiping. + /// Set this to true to prevent ItemClicked or selection to occur when swiping in a + /// + public bool IsPointerReleasedOnSwipingHandled + { + get { return (bool)GetValue(IsPointerReleasedOnSwipingHandledProperty); } + set { SetValue(IsPointerReleasedOnSwipingHandledProperty, value); } + } + + /// + /// Gets or sets a value indicating whether the SlidableListItem should use the SwipeControl when possible (Fall Creators Update and above) + /// When set to true and the device supports SwipeControl, the SlidableListItem will use a template based on SwipeControl + /// + public bool UseSwipeControlWhenPossible + { + get { return (bool)GetValue(UseSwipeControlWhenPossibleProperty); } + set { SetValue(UseSwipeControlWhenPossibleProperty, value); } + } + } +} diff --git a/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.xaml b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.xaml new file mode 100644 index 000000000..40c6a8b29 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItem.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItemSwipeControlTemplate.xaml b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItemSwipeControlTemplate.xaml new file mode 100644 index 000000000..b005ca284 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/SlidableListItem/SlidableListItemSwipeControlTemplate.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatus.cs b/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatus.cs new file mode 100644 index 000000000..5b8da7f84 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatus.cs @@ -0,0 +1,56 @@ +// Backport of the SlidableListItem from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/v4.0.0/Microsoft.Toolkit.Uwp.UI.Controls/SlidableListItem/SwipeStatus.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace UWPX_UI.Controls.Toolkit.SlidableListItem +{ + /// + /// Types of swipe status. + /// + public enum SwipeStatus + { + /// + /// Swiping is not occurring. + /// + Idle, + + /// + /// Swiping is going to start. + /// + Starting, + + /// + /// Swiping to the left, but the command is disabled. + /// + DisabledSwipingToLeft, + + /// + /// Swiping to the left below the threshold. + /// + SwipingToLeftThreshold, + + /// + /// Swiping to the left above the threshold. + /// + SwipingPassedLeftThreshold, + + /// + /// Swiping to the right, but the command is disabled. + /// + DisabledSwipingToRight, + + /// + /// Swiping to the right below the threshold. + /// + SwipingToRightThreshold, + + /// + /// Swiping to the right above the threshold. + /// + SwipingPassedRightThreshold + } +} diff --git a/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatusChangedEventArgs.cs b/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatusChangedEventArgs.cs new file mode 100644 index 000000000..8076f7193 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/SlidableListItem/SwipeStatusChangedEventArgs.cs @@ -0,0 +1,26 @@ +// Backport of the SlidableListItem from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/v4.0.0/Microsoft.Toolkit.Uwp.UI.Controls/SlidableListItem/SwipeStatusChangedEventArgs.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace UWPX_UI.Controls.Toolkit.SlidableListItem +{ + /// + /// Event args for a SwipeStatus changing event + /// + public class SwipeStatusChangedEventArgs + { + /// + /// Gets the old value. + /// + public SwipeStatus OldValue { get; internal set; } + + /// + /// Gets the new value. + /// + public SwipeStatus NewValue { get; internal set; } + } +} diff --git a/UWPX_UI/Controls/Toolkit/VisualTree.cs b/UWPX_UI/Controls/Toolkit/VisualTree.cs new file mode 100644 index 000000000..2c95eba80 --- /dev/null +++ b/UWPX_UI/Controls/Toolkit/VisualTree.cs @@ -0,0 +1,233 @@ +// Backport of the MasterDetailsView from the Windows Community Toolkit +// Source: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +// Original license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Controls.Toolkit +{ + /// + /// Defines a collection of extensions methods for UI. + /// + public static class VisualTree + { + /// + /// Find descendant control using its name. + /// + /// Parent element. + /// Name of the control to find + /// Descendant control or null if not found. + public static FrameworkElement FindDescendantByName(this DependencyObject element, string name) + { + if (element == null || string.IsNullOrWhiteSpace(name)) + { + return null; + } + + if (name.Equals((element as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) + { + return element as FrameworkElement; + } + + var childCount = VisualTreeHelper.GetChildrenCount(element); + for (int i = 0; i < childCount; i++) + { + var result = VisualTreeHelper.GetChild(element, i).FindDescendantByName(name); + if (result != null) + { + return result; + } + } + + return null; + } + + /// + /// Find first descendant control of a specified type. + /// + /// Type to search for. + /// Parent element. + /// Descendant control or null if not found. + public static T FindDescendant(this DependencyObject element) + where T : DependencyObject + { + T retValue = null; + var childrenCount = VisualTreeHelper.GetChildrenCount(element); + + for (var i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(element, i); + var type = child as T; + if (type != null) + { + retValue = type; + break; + } + + retValue = FindDescendant(child); + + if (retValue != null) + { + break; + } + } + + return retValue; + } + + /// + /// Find first descendant control of a specified type. + /// + /// Parent element. + /// Type of descendant. + /// Descendant control or null if not found. + public static object FindDescendant(this DependencyObject element, Type type) + { + object retValue = null; + var childrenCount = VisualTreeHelper.GetChildrenCount(element); + + for (var i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(element, i); + if (child.GetType() == type) + { + retValue = child; + break; + } + + retValue = FindDescendant(child, type); + + if (retValue != null) + { + break; + } + } + + return retValue; + } + + /// + /// Find all descendant controls of the specified type. + /// + /// Type to search for. + /// Parent element. + /// Descendant controls or empty if not found. + public static IEnumerable FindDescendants(this DependencyObject element) + where T : DependencyObject + { + var childrenCount = VisualTreeHelper.GetChildrenCount(element); + + for (var i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(element, i); + var type = child as T; + if (type != null) + { + yield return type; + } + + foreach (T childofChild in child.FindDescendants()) + { + yield return childofChild; + } + } + } + + /// + /// Find visual ascendant control using its name. + /// + /// Parent element. + /// Name of the control to find + /// Descendant control or null if not found. + public static FrameworkElement FindAscendantByName(this DependencyObject element, string name) + { + if (element == null || string.IsNullOrWhiteSpace(name)) + { + return null; + } + + var parent = VisualTreeHelper.GetParent(element); + + if (parent == null) + { + return null; + } + + if (name.Equals((parent as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) + { + return parent as FrameworkElement; + } + + return parent.FindAscendantByName(name); + } + + /// + /// Find first visual ascendant control of a specified type. + /// + /// Type to search for. + /// Child element. + /// Ascendant control or null if not found. + public static T FindAscendant(this DependencyObject element) + where T : DependencyObject + { + var parent = VisualTreeHelper.GetParent(element); + + if (parent == null) + { + return null; + } + + if (parent is T) + { + return parent as T; + } + + return parent.FindAscendant(); + } + + /// + /// Find first visual ascendant control of a specified type. + /// + /// Child element. + /// Type of ascendant to look for. + /// Ascendant control or null if not found. + public static object FindAscendant(this DependencyObject element, Type type) + { + var parent = VisualTreeHelper.GetParent(element); + + if (parent == null) + { + return null; + } + + if (parent.GetType() == type) + { + return parent; + } + + return parent.FindAscendant(type); + } + + /// + /// Find all visual ascendants for the element. + /// + /// Child element. + /// A collection of parent elements or null if none found. + public static IEnumerable FindAscendants(this DependencyObject element) + { + var parent = VisualTreeHelper.GetParent(element); + + while (parent != null) + { + yield return parent; + parent = VisualTreeHelper.GetParent(parent); + } + } + } +} diff --git a/UWPX_UI/Dialogs/AccountInfoDialog.xaml b/UWPX_UI/Dialogs/AccountInfoDialog.xaml new file mode 100644 index 000000000..fd561a3cf --- /dev/null +++ b/UWPX_UI/Dialogs/AccountInfoDialog.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Dialogs/AccountInfoDialog.xaml.cs b/UWPX_UI/Dialogs/AccountInfoDialog.xaml.cs new file mode 100644 index 000000000..23d9f75ce --- /dev/null +++ b/UWPX_UI/Dialogs/AccountInfoDialog.xaml.cs @@ -0,0 +1,48 @@ +using UWPX_UI_Context.Classes.DataTemplates; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class AccountInfoDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly AccountDataTemplate ACCOUNT; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public AccountInfoDialog(AccountDataTemplate account) + { + this.ACCOUNT = account; + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml b/UWPX_UI/Dialogs/AppCenterPushDialog.xaml similarity index 85% rename from UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml rename to UWPX_UI/Dialogs/AppCenterPushDialog.xaml index 1fbc9a4cf..5f3e4f55e 100644 --- a/UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml +++ b/UWPX_UI/Dialogs/AppCenterPushDialog.xaml @@ -1,9 +1,10 @@ - diff --git a/UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml.cs b/UWPX_UI/Dialogs/AppCenterPushDialog.xaml.cs similarity index 82% rename from UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml.cs rename to UWPX_UI/Dialogs/AppCenterPushDialog.xaml.cs index 5890e3088..3755bc289 100644 --- a/UWP XMPP Client/Dialogs/AppCenterPushDialog.xaml.cs +++ b/UWPX_UI/Dialogs/AppCenterPushDialog.xaml.cs @@ -1,9 +1,9 @@ using System; -using UWP_XMPP_Client.Classes; +using UWPX_UI_Context.Classes; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Dialogs +namespace UWPX_UI.Dialogs { public sealed partial class AppCenterPushDialog : ContentDialog { @@ -14,17 +14,11 @@ public string MarkdownText get { return (string)GetValue(MarkdownTextProperty); } set { SetValue(MarkdownTextProperty, value); } } - public static readonly DependencyProperty MarkdownTextProperty = DependencyProperty.Register("MarkdownText", typeof(string), typeof(AppCenterPushDialog), null); + public static readonly DependencyProperty MarkdownTextProperty = DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(AppCenterPushDialog), new PropertyMetadata("")); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 30/04/2018 Created [Fabian Sauter] - /// public AppCenterPushDialog(string title, string markdownText) { this.Title = title; @@ -57,7 +51,7 @@ public AppCenterPushDialog(string title, string markdownText) #region --Events-- private async void MarkdownTextBlock_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) { - await UiUtils.launchUriAsync(new Uri(e.Link)); + await UiUtils.LaunchUriAsync(new Uri(e.Link)); } #endregion diff --git a/UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml b/UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml new file mode 100644 index 000000000..bcd990296 --- /dev/null +++ b/UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Dialogs/DeleteChatDialog.xaml.cs b/UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml.cs similarity index 61% rename from UWP XMPP Client/Dialogs/DeleteChatDialog.xaml.cs rename to UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml.cs index 148cc0a21..fee0e99b8 100644 --- a/UWP XMPP Client/Dialogs/DeleteChatDialog.xaml.cs +++ b/UWPX_UI/Dialogs/CertificateRequirementsDialog.xaml.cs @@ -1,28 +1,21 @@ -using Windows.UI.Xaml; +using UWPX_UI_Context.Classes.DataContext.Dialogs; using Windows.UI.Xaml.Controls; +using XMPP_API.Classes.Network; -namespace UWP_XMPP_Client.Dialogs +namespace UWPX_UI.Dialogs { - public sealed partial class DeleteChatDialog : ContentDialog + public sealed partial class CertificateRequirementsDialog : ContentDialog { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public bool keepChatLog { get; private set; } - public bool deleteChat { get; private set; } + public readonly CertificateRequirementsDialogContext VIEW_MODEL; #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 26/11/2017 Created [Fabian Sauter] - /// - public DeleteChatDialog() + public CertificateRequirementsDialog(XMPPAccount account) { - this.deleteChat = false; - this.keepChatLog = true; + VIEW_MODEL = new CertificateRequirementsDialogContext(account); this.InitializeComponent(); } @@ -49,17 +42,15 @@ public DeleteChatDialog() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private void no_btn_Click(object sender, RoutedEventArgs e) + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { - keepChatLog = (bool)keepChat_cbx.IsChecked; - deleteChat = false; + VIEW_MODEL.Cancel(); Hide(); } - private void yes_btn_Click(object sender, RoutedEventArgs e) + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { - keepChatLog = (bool)keepChat_cbx.IsChecked; - deleteChat = true; + VIEW_MODEL.Confirm(); Hide(); } diff --git a/UWP XMPP Client/Dialogs/ClearCacheDialog.xaml b/UWPX_UI/Dialogs/ClearCacheDialog.xaml similarity index 87% rename from UWP XMPP Client/Dialogs/ClearCacheDialog.xaml rename to UWPX_UI/Dialogs/ClearCacheDialog.xaml index a7b4ffe55..de238aae1 100644 --- a/UWP XMPP Client/Dialogs/ClearCacheDialog.xaml +++ b/UWPX_UI/Dialogs/ClearCacheDialog.xaml @@ -1,11 +1,12 @@ - @@ -20,6 +21,8 @@ @@ -116,7 +119,9 @@ Grid.Column="0" Margin="0,0,1,0" HorizontalAlignment="Stretch" - Click="clear_btn_Click"> + Click="Clear_btn_Click" + IsEnabled="{x:Bind VIEW_MODEL.MODEL.IsCleaningCache, Mode=OneWay, Converter={StaticResource BoolInverterValueConverter}}" + Style="{ThemeResource ButtonRevealStyle}"> + Visibility="{x:Bind VIEW_MODEL.MODEL.IsCleaningCache, Mode=OneWay, Converter={StaticResource BoolVisabilityValueConverter}}"/> - diff --git a/UWPX_UI/Dialogs/ClearCacheDialog.xaml.cs b/UWPX_UI/Dialogs/ClearCacheDialog.xaml.cs new file mode 100644 index 000000000..d248645a1 --- /dev/null +++ b/UWPX_UI/Dialogs/ClearCacheDialog.xaml.cs @@ -0,0 +1,108 @@ +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class ClearCacheDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly ClearCacheDialogContext VIEW_MODEL = new ClearCacheDialogContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ClearCacheDialog() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + private void SetSelectedNodes() + { + tree_tv.SelectedNodes.Add(general_tvn); + tree_tv.SelectedNodes.Add(disco_tvn); + tree_tv.SelectedNodes.Add(muc_tvn); + tree_tv.SelectedNodes.Add(clients_tvn); + } + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void UpdateSelected() + { + // General: + VIEW_MODEL.MODEL.ChatMessages = tree_tv.SelectedNodes.Contains(chatMessages_tvn); + VIEW_MODEL.MODEL.Chats = tree_tv.SelectedNodes.Contains(chats_tvn); + VIEW_MODEL.MODEL.Images = tree_tv.SelectedNodes.Contains(images_tvn); + + // Disco: + VIEW_MODEL.MODEL.DiscoFeatures = tree_tv.SelectedNodes.Contains(discoFeatures_tvn); + VIEW_MODEL.MODEL.DiscoFeatures = tree_tv.SelectedNodes.Contains(discoIdentities_tvn); + VIEW_MODEL.MODEL.DiscoItems = tree_tv.SelectedNodes.Contains(discoItems_tvn); + + // MUC: + VIEW_MODEL.MODEL.MucChatInfo = tree_tv.SelectedNodes.Contains(mucChatInfo_tvn); + VIEW_MODEL.MODEL.MucOccupants = tree_tv.SelectedNodes.Contains(mucMembers_tvn); + VIEW_MODEL.MODEL.MucDirectInvites = tree_tv.SelectedNodes.Contains(mucDirectInvites_tvn); + + // Accounts: + VIEW_MODEL.MODEL.Accounts = tree_tv.SelectedNodes.Contains(accounts_tvn); + VIEW_MODEL.MODEL.PasswordVault = tree_tv.SelectedNodes.Contains(passwordVault_tvn); + VIEW_MODEL.MODEL.IgnoredCertErrors = tree_tv.SelectedNodes.Contains(ignoredCertificateErrors_tvn); + VIEW_MODEL.MODEL.ConnectionOptions = tree_tv.SelectedNodes.Contains(connectionOptions_tvn); + + // OMEMO: + VIEW_MODEL.MODEL.OmemoDeviceListSubscriptions = tree_tv.SelectedNodes.Contains(omemoDeviceListSubscriptions_tvn); + VIEW_MODEL.MODEL.OmemoDevices = tree_tv.SelectedNodes.Contains(omemoDevices_tvn); + VIEW_MODEL.MODEL.OmemoIdentityKeys = tree_tv.SelectedNodes.Contains(omemoIdentityKeys_tvn); + VIEW_MODEL.MODEL.OmemoPreKeys = tree_tv.SelectedNodes.Contains(omemoPreKeys_tvn); + VIEW_MODEL.MODEL.OmemoSignedPreKeys = tree_tv.SelectedNodes.Contains(omemoSignedPreKeys_tvn); + VIEW_MODEL.MODEL.OmemoSessions = tree_tv.SelectedNodes.Contains(omemoSessions_tvn); + + // Clients: + VIEW_MODEL.MODEL.ReloadClients = tree_tv.SelectedNodes.Contains(reloadClients_tvn); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Cancel_btn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + Hide(); + } + + private async void Clear_btn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + UpdateSelected(); + bool result = await VIEW_MODEL.ClearCacheAsync(); + if (result) + { + done_notification.Show("Done cleaning cache.", 5000); + } + else + { + done_notification.Show("Failed to clear cache! View the logs for more information.", 0); + } + } + + private void Tree_tv_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + SetSelectedNodes(); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/ColorPickerDialog.xaml b/UWPX_UI/Dialogs/ColorPickerDialog.xaml new file mode 100644 index 000000000..5aa1425dd --- /dev/null +++ b/UWPX_UI/Dialogs/ColorPickerDialog.xaml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/UWPX_UI/Dialogs/ColorPickerDialog.xaml.cs b/UWPX_UI/Dialogs/ColorPickerDialog.xaml.cs new file mode 100644 index 000000000..62b2e44d2 --- /dev/null +++ b/UWPX_UI/Dialogs/ColorPickerDialog.xaml.cs @@ -0,0 +1,59 @@ +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class ColorPickerDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly ColorPickerDialogContext VIEW_MODEL; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ColorPickerDialog(Color color) + { + this.VIEW_MODEL = new ColorPickerDialogContext(color); + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Cancel(); + Hide(); + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Confirm(); + Hide(); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/ConfirmDialog.xaml b/UWPX_UI/Dialogs/ConfirmDialog.xaml new file mode 100644 index 000000000..af79e4528 --- /dev/null +++ b/UWPX_UI/Dialogs/ConfirmDialog.xaml @@ -0,0 +1,22 @@ + + + + + + diff --git a/UWPX_UI/Dialogs/ConfirmDialog.xaml.cs b/UWPX_UI/Dialogs/ConfirmDialog.xaml.cs new file mode 100644 index 000000000..79590ef2c --- /dev/null +++ b/UWPX_UI/Dialogs/ConfirmDialog.xaml.cs @@ -0,0 +1,84 @@ +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class ConfirmDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string MarkdownText + { + get { return (string)GetValue(MarkdownTextProperty); } + set { SetValue(MarkdownTextProperty, value); } + } + public static readonly DependencyProperty MarkdownTextProperty = DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(ConfirmDialog), new PropertyMetadata("")); + + public string PositiveText + { + get { return (string)GetValue(PositiveTextProperty); } + set { SetValue(PositiveTextProperty, value); } + } + public static readonly DependencyProperty PositiveTextProperty = DependencyProperty.Register(nameof(PositiveText), typeof(string), typeof(ConfirmDialog), new PropertyMetadata("Yes")); + + public string NegativeText + { + get { return (string)GetValue(NegativeTextProperty); } + set { SetValue(NegativeTextProperty, value); } + } + public static readonly DependencyProperty NegativeTextProperty = DependencyProperty.Register(nameof(NegativeText), typeof(string), typeof(ConfirmDialog), new PropertyMetadata("No")); + + public readonly ConfirmDialogContext VIEW_MODEL = new ConfirmDialogContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public ConfirmDialog(string title, string markdownText) + { + this.Title = title; + this.MarkdownText = markdownText; + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.OnNegative(); + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.OnPositive(); + } + + private async void Text_mrkdwn_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) + { + await VIEW_MODEL.OnLinkClickedAsync(e.Link); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml b/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml new file mode 100644 index 000000000..ad6df9c8a --- /dev/null +++ b/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + diff --git a/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml.cs b/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml.cs new file mode 100644 index 000000000..9647c8d58 --- /dev/null +++ b/UWPX_UI/Dialogs/DeleteAccountConfirmDialog.xaml.cs @@ -0,0 +1,55 @@ +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class DeleteAccountConfirmDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly DeleteAccountConfirmDialogContext VIEW_MODEL = new DeleteAccountConfirmDialogContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public DeleteAccountConfirmDialog() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Confirm(); + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Cancel(); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml b/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml new file mode 100644 index 000000000..cb50d5f12 --- /dev/null +++ b/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml.cs b/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml.cs new file mode 100644 index 000000000..5d6591f99 --- /dev/null +++ b/UWPX_UI/Dialogs/DeleteChatConfirmDialog.xaml.cs @@ -0,0 +1,55 @@ +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class DeleteChatConfirmDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly DeleteChatConfirmDialogContext VIEW_MODEL = new DeleteChatConfirmDialogContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public DeleteChatConfirmDialog() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Confirm(); + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + VIEW_MODEL.Cancel(); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/InfoDialog.xaml b/UWPX_UI/Dialogs/InfoDialog.xaml new file mode 100644 index 000000000..2a6cfa4e0 --- /dev/null +++ b/UWPX_UI/Dialogs/InfoDialog.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/UWPX_UI/Dialogs/InfoDialog.xaml.cs b/UWPX_UI/Dialogs/InfoDialog.xaml.cs new file mode 100644 index 000000000..4d54976b6 --- /dev/null +++ b/UWPX_UI/Dialogs/InfoDialog.xaml.cs @@ -0,0 +1,59 @@ +using System; +using UWPX_UI_Context.Classes; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class InfoDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public string MarkdownText + { + get { return (string)GetValue(MarkdownTextProperty); } + set { SetValue(MarkdownTextProperty, value); } + } + public static readonly DependencyProperty MarkdownTextProperty = DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(InfoDialog), new PropertyMetadata("")); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public InfoDialog(string title, string markdownText) + { + this.Title = title; + this.MarkdownText = markdownText; + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private async void MarkdownTextBlock_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) + { + await UiUtils.LaunchUriAsync(new Uri(e.Link)); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/InitialStartDialog.xaml b/UWPX_UI/Dialogs/InitialStartDialog.xaml new file mode 100644 index 000000000..59ead8a96 --- /dev/null +++ b/UWPX_UI/Dialogs/InitialStartDialog.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/UWPX_UI/Dialogs/InitialStartDialog.xaml.cs b/UWPX_UI/Dialogs/InitialStartDialog.xaml.cs new file mode 100644 index 000000000..faf919d9e --- /dev/null +++ b/UWPX_UI/Dialogs/InitialStartDialog.xaml.cs @@ -0,0 +1,52 @@ +using System; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Dialogs; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Dialogs +{ + public sealed partial class InitialStartDialog : ContentDialog + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly InitialStartDialogContext VIEW_MODEL = new InitialStartDialogContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public InitialStartDialog() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private async void Content_mdc_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) + { + await UiUtils.LaunchUriAsync(new Uri(e.Link)); + } + + #endregion + } +} diff --git a/UWPX_UI/Dialogs/WhatsNewDialog.xaml b/UWPX_UI/Dialogs/WhatsNewDialog.xaml new file mode 100644 index 000000000..d589ba3b0 --- /dev/null +++ b/UWPX_UI/Dialogs/WhatsNewDialog.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Dialogs/WhatsNewDialog.xaml.cs b/UWPX_UI/Dialogs/WhatsNewDialog.xaml.cs similarity index 61% rename from UWP XMPP Client/Dialogs/WhatsNewDialog.xaml.cs rename to UWPX_UI/Dialogs/WhatsNewDialog.xaml.cs index 21f596659..7af70049e 100644 --- a/UWP XMPP Client/Dialogs/WhatsNewDialog.xaml.cs +++ b/UWPX_UI/Dialogs/WhatsNewDialog.xaml.cs @@ -1,28 +1,22 @@ -using UWP_XMPP_Client.Classes; -using UWP_XMPP_Client.Pages.SettingsPages; -using Windows.UI.Xaml; +using System; +using UWPX_UI.Pages.Settings; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Dialogs; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Dialogs +namespace UWPX_UI.Dialogs { public sealed partial class WhatsNewDialog : ContentDialog { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - public bool showOnStartup { get; private set; } + public readonly WhatsNewDialogContext VIEW_MODEL = new WhatsNewDialogContext(); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 31/01/2018 Created [Fabian Sauter] - /// public WhatsNewDialog() { - this.showOnStartup = true; this.InitializeComponent(); } @@ -49,21 +43,21 @@ public WhatsNewDialog() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private void close_btn_Click(object sender, RoutedEventArgs e) + private async void Content_mdc_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) { - showOnStartup = (bool)showOnStartup_cbx.IsChecked; - Hide(); + await UiUtils.LaunchUriAsync(new Uri(e.Link)); } - private void donate_btn_Click(object sender, RoutedEventArgs e) + private void Close_btn_Click(Controls.IconButtonControl sender, Windows.UI.Xaml.RoutedEventArgs args) { - (Window.Current.Content as Frame).Navigate(typeof(DonateSettingsPage)); Hide(); } - private async void MarkdownTextBlock_LinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) + private void Donate_btn_Click(Controls.IconButtonControl sender, Windows.UI.Xaml.RoutedEventArgs args) { - await UiUtils.launchUriAsync(new System.Uri(e.Link)); + VIEW_MODEL.MODEL.ToDonatePageNavigated = true; + UiUtils.NavigateToPage(typeof(DonateSettingsPage)); + Hide(); } #endregion diff --git a/UWPX_UI/Extensions/ScrollViewerExtensions.cs b/UWPX_UI/Extensions/ScrollViewerExtensions.cs new file mode 100644 index 000000000..648760237 --- /dev/null +++ b/UWPX_UI/Extensions/ScrollViewerExtensions.cs @@ -0,0 +1,58 @@ +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Extensions +{ + public static class ScrollViewerExtensions + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + public static void ScrollIntoViewVertically(this ScrollViewer scrollViewer, UIElement element, bool disableAnimation) + { + GeneralTransform visual = element.TransformToVisual((UIElement)scrollViewer.Content); + Point pos = visual.TransformPoint(new Point(0, 0)); + scrollViewer.ChangeView(null, pos.Y, null, disableAnimation); + } + + public static void ScrollIntoViewHorizontally(this ScrollViewer scrollViewer, UIElement element, bool disableAnimation) + { + GeneralTransform visual = element.TransformToVisual((UIElement)scrollViewer.Content); + Point pos = visual.TransformPoint(new Point(0, 0)); + scrollViewer.ChangeView(pos.X, null, null, disableAnimation); + } + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + + + #endregion + } +} diff --git a/UWP XMPP Client/Controls/Extensions/TextBlockChatTextFormatExtension.cs b/UWPX_UI/Extensions/TextBlockChatMessageFormatExtension.cs similarity index 61% rename from UWP XMPP Client/Controls/Extensions/TextBlockChatTextFormatExtension.cs rename to UWPX_UI/Extensions/TextBlockChatMessageFormatExtension.cs index 05e408261..043087347 100644 --- a/UWP XMPP Client/Controls/Extensions/TextBlockChatTextFormatExtension.cs +++ b/UWPX_UI/Extensions/TextBlockChatMessageFormatExtension.cs @@ -4,20 +4,19 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; -using UWP_XMPP_Client.Classes; +using UWPX_UI_Context.Classes; using Windows.ApplicationModel.Contacts; -using Windows.ApplicationModel.Core; -using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Documents; +using Windows.UI.Xaml.Media; -namespace UWP_XMPP_Client.Controls.Extensions +namespace UWPX_UI.Extensions { /// /// Based on: https://stackoverflow.com/questions/38208741/auto-detect-url-phone-number-email-in-textblock /// - public static class TextBlockChatTextFormatExtension + public sealed class TextBlockChatMessageFormatExtension { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- @@ -34,89 +33,7 @@ public static class TextBlockChatTextFormatExtension private static readonly Regex EMAIL_REGEX = new Regex(EMAIL_REGEX_PATTERN, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(500)); private static readonly Regex PHONE_REGEX = new Regex(PHONE_REGEX_PATTERN, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)); - public static readonly DependencyProperty FormattedTextProperty = - DependencyProperty.Register("FormattedText", typeof(string), typeof(TextBlockChatTextFormatExtension), - new PropertyMetadata(string.Empty, (sender, e) => - { - if (sender is TextBlock textBlock && e.NewValue is string text && !string.IsNullOrWhiteSpace(text)) - { - // Check if disabled: - if (Settings.getSettingBoolean(SettingsConsts.DISABLE_ADVANCED_CHAT_MESSAGE_PROCESSING)) - { - textBlock.Inlines.Add(new Run { Text = text }); - return; - } - - // Clear all inlines: - textBlock.Inlines.Clear(); - - Task.Run(async () => - { - // Check if single emoji: - if (Emoji.IsEmoji(text.TrimEnd(UiUtils.TRIM_CHARS).TrimStart(UiUtils.TRIM_CHARS))) - { - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => textBlock.Inlines.Add(new Run - { - Text = text, - FontSize = 50 - })); - } - else - { - var lastPosition = 0; - var matches = new Match[3] { Match.Empty, Match.Empty, Match.Empty }; - do - { - try - { - matches[0] = URL_REGEX.Match(text, lastPosition); - matches[1] = EMAIL_REGEX.Match(text, lastPosition); - matches[2] = PHONE_REGEX.Match(text, lastPosition); - } - catch (RegexMatchTimeoutException) - { - } - - var firstMatch = matches.Where(x => x != null && x.Success).OrderBy(x => x.Index).FirstOrDefault(); - if (firstMatch == matches[0]) - { - // the first match is an URL: - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - createRunElement(textBlock, text, lastPosition, firstMatch.Index); - lastPosition = createUrlElement(textBlock, firstMatch); - }); - } - else if (firstMatch == matches[1]) - { - // the first match is an email: - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - createRunElement(textBlock, text, lastPosition, firstMatch.Index); - lastPosition = createContactElement(textBlock, firstMatch, null); - }); - } - else if (firstMatch == matches[2]) - { - // the first match is a phone number: - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - createRunElement(textBlock, text, lastPosition, firstMatch.Index); - lastPosition = createContactElement(textBlock, null, firstMatch); - }); - } - else - { - // no match, we add the whole text: - await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => textBlock.Inlines.Add(new Run { Text = text.Substring(lastPosition) })); - lastPosition = text.Length; - } - } - while (lastPosition < text.Length); - } - }); - } - })); + public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.Register("FormattedText", typeof(string), typeof(TextBlockChatMessageFormatExtension), new PropertyMetadata("", OnFormattedTextChanged)); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ @@ -169,8 +86,12 @@ private static int createUrlElement(TextBlock textBlock, Match urlMatch) { if (Uri.TryCreate(urlMatch.Value, UriKind.RelativeOrAbsolute, out Uri targetUri)) { - var link = new Hyperlink(); - link.Inlines.Add(new Run { Text = urlMatch.Value }); + Hyperlink link = new Hyperlink(); + link.Inlines.Add(new Run + { + Text = urlMatch.Value, + Foreground = (Brush)Application.Current.Resources["SpeechBubbleForegroundBrush"] + }); if (targetUri.IsAbsoluteUri) { @@ -206,8 +127,12 @@ private static int createContactElement(TextBlock textBlock, Match emailMatch, M { var currentMatch = emailMatch ?? phoneMatch; - var link = new Hyperlink(); - link.Inlines.Add(new Run { Text = currentMatch.Value }); + Hyperlink link = new Hyperlink(); + link.Inlines.Add(new Run + { + Text = currentMatch.Value, + Foreground = (Brush)Application.Current.Resources["SpeechBubbleForegroundBrush"] + }); link.Click += (s, a) => { var contact = new Contact(); @@ -235,7 +160,79 @@ private static int createContactElement(TextBlock textBlock, Match emailMatch, M #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- + private static async void OnFormattedTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is TextBlock textBlock && e.NewValue is string text && !string.IsNullOrWhiteSpace(text)) + { + // Check if advanced chat message processing is disabled: + if (Settings.getSettingBoolean(SettingsConsts.DISABLE_ADVANCED_CHAT_MESSAGE_PROCESSING)) + { + textBlock.Inlines.Add(new Run { Text = text }); + return; + } + + // Clear all inlines: + textBlock.Inlines.Clear(); + + bool isEmoji = await Task.Run(() => Emoji.IsEmoji(text.TrimEnd(UiUtils.TRIM_CHARS).TrimStart(UiUtils.TRIM_CHARS))); + if (isEmoji) + { + textBlock.Inlines.Add(new Run + { + Text = text, + FontSize = 50 + }); + } + else + { + var lastPosition = 0; + var matches = new Match[3] { Match.Empty, Match.Empty, Match.Empty }; + do + { + await Task.Run(() => + { + try + { + matches[0] = URL_REGEX.Match(text, lastPosition); + matches[1] = EMAIL_REGEX.Match(text, lastPosition); + matches[2] = PHONE_REGEX.Match(text, lastPosition); + } + catch (RegexMatchTimeoutException) + { + } + }); + + var firstMatch = matches.Where(x => x != null && x.Success).OrderBy(x => x.Index).FirstOrDefault(); + if (firstMatch == matches[0]) + { + // the first match is an URL: + createRunElement(textBlock, text, lastPosition, firstMatch.Index); + lastPosition = createUrlElement(textBlock, firstMatch); + } + else if (firstMatch == matches[1]) + { + // the first match is an email: + createRunElement(textBlock, text, lastPosition, firstMatch.Index); + lastPosition = createContactElement(textBlock, firstMatch, null); + } + else if (firstMatch == matches[2]) + { + // the first match is a phone number: + createRunElement(textBlock, text, lastPosition, firstMatch.Index); + lastPosition = createContactElement(textBlock, null, firstMatch); + } + else + { + // no match, we add the whole text: + textBlock.Inlines.Add(new Run { Text = text.Substring(lastPosition) }); + lastPosition = text.Length; + } + + } while (lastPosition < text.Length); + } + } + } #endregion } diff --git a/UWP XMPP Client/Package.StoreAssociation.xml b/UWPX_UI/Package.StoreAssociation.xml similarity index 96% rename from UWP XMPP Client/Package.StoreAssociation.xml rename to UWPX_UI/Package.StoreAssociation.xml index e44a77154..ac4a94b42 100644 --- a/UWP XMPP Client/Package.StoreAssociation.xml +++ b/UWPX_UI/Package.StoreAssociation.xml @@ -2,6 +2,7 @@ CN=8AFEBA0F-E085-403B-A05B-71A8952F40A3 Fabian Sauter + MSA http://www.w3.org/2001/04/xmlenc#sha256 @@ -364,9 +365,28 @@ + 790FabianSauter.OnewheelCompanion 790FabianSauter.TUM-Campus-App 790FabianSauter.UWPX 790FabianSauter.UWPXBeta - + + + + 0.0.0.0 + X86 + 0.12.0.0 + + + 0.0.0.0 + X64 + 0.12.0.0 + + + 0.0.0.0 + Arm + 0.12.0.0 + + + \ No newline at end of file diff --git a/UWP XMPP Client/Package.appxmanifest b/UWPX_UI/Package.appxmanifest similarity index 75% rename from UWP XMPP Client/Package.appxmanifest rename to UWPX_UI/Package.appxmanifest index 5423c35d7..9163015aa 100644 --- a/UWP XMPP Client/Package.appxmanifest +++ b/UWPX_UI/Package.appxmanifest @@ -1,7 +1,7 @@  - - + + UWPX Alpha Fabian Sauter @@ -14,23 +14,23 @@ - + - + + + + + + - + - - - - - Assets\uri_activation_icon.png - UWPX + XMPP diff --git a/UWPX_UI/Pages/AddAccountPage.xaml b/UWPX_UI/Pages/AddAccountPage.xaml new file mode 100644 index 000000000..c11735ba9 --- /dev/null +++ b/UWPX_UI/Pages/AddAccountPage.xaml @@ -0,0 +1,477 @@ + + + + + + + + + 0:0:0.5 + 0:0:0.5 + 600,0,0 + 0 + 0,0,0 + -600,0,0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enforce + Use if available + Prohibit + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Pages/Settings/DonateSettingsPage.xaml.cs b/UWPX_UI/Pages/Settings/DonateSettingsPage.xaml.cs new file mode 100644 index 000000000..9444f9966 --- /dev/null +++ b/UWPX_UI/Pages/Settings/DonateSettingsPage.xaml.cs @@ -0,0 +1,90 @@ +using UWPX_UI.Extensions; +using UWPX_UI_Context.Classes.DataContext.Pages; +using Windows.UI.Xaml.Controls; + +namespace UWPX_UI.Pages.Settings +{ + public sealed partial class DonateSettingsPage : Page + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly DonateSettingsPageContext VIEW_MODEL = new DonateSettingsPageContext(); + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public DonateSettingsPage() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void Main_nview_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItem is Microsoft.UI.Xaml.Controls.NavigationViewItem item) + { + switch (item.Tag) + { + case "General": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, general_scp, false); + break; + + case "Bank transfer": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, bankTransfer_scp, false); + break; + } + } + } + + private void Main_nview_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + foreach (object item in main_nview.MenuItems) + { + if (item is Microsoft.UI.Xaml.Controls.NavigationViewItem navItem && string.Equals((string)navItem.Tag, "General")) + { + main_nview.SelectedItem = item; + break; + } + } + } + + private async void DonatePP_btn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + await VIEW_MODEL.DonateViaPayPalAsync(); + } + + private async void DonateLP_btn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + await VIEW_MODEL.DonateViaLiberapayAsync(); + } + + private async void SendMail_link_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + await VIEW_MODEL.SendBankDetailsMailAsync(); + } + + #endregion + } +} diff --git a/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml b/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml new file mode 100644 index 000000000..ad5b925d9 --- /dev/null +++ b/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml.cs b/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml.cs new file mode 100644 index 000000000..47c0ef52f --- /dev/null +++ b/UWPX_UI/Pages/Settings/MiscSettingsPage.xaml.cs @@ -0,0 +1,188 @@ +using UWPX_UI.Dialogs; +using UWPX_UI.Extensions; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Pages; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Navigation; + +namespace UWPX_UI.Pages.Settings +{ + public sealed partial class MiscSettingsPage : Page + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + private readonly MiscSettingsPageContext VIEW_MODEL = new MiscSettingsPageContext(); + private string requestedSection; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public MiscSettingsPage() + { + this.InitializeComponent(); + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void ScrollToSection(string section) + { + switch (section) + { + case "Logs": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, logs_scp, false); + break; + + case "Cache": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, cache_scp, false); + break; + + case "Analytics": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, analytics_scp, false); + break; + + case "Misc": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, misc_scp, false); + break; + + case "About": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, about_scp, false); + break; + } + } + + private void SelectMenuItem(string section) + { + foreach (object item in main_nview.MenuItems) + { + if (item is Microsoft.UI.Xaml.Controls.NavigationViewItem navItem && string.Equals((string)navItem.Tag, section)) + { + main_nview.SelectedItem = item; + break; + } + } + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private async void MoreInformation_hlb_Tapped(object sender, TappedRoutedEventArgs e) + { + await VIEW_MODEL.ShowAnalyticsCrashesMoreInformationAsync(); + } + + private async void OpenAppDataFolder_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.OpenAppDataFolderAsync(); + } + + private async void DeleteLogs_btn_Click(object sender, RoutedEventArgs e) + { + ConfirmDialog dialog = new ConfirmDialog("Delete logs:", "Do you really want to **delete** all logs?"); + await UiUtils.ShowDialogAsync(dialog); + await VIEW_MODEL.DeleteLogsAsync(dialog.VIEW_MODEL); + await logsFolder_fsc.RecalculateFolderSizeAsync(); + } + + private async void ExportLogs_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ExportLogsAsync(); + } + + private async void ClearImageCache_btn_Click(object sender, RoutedEventArgs e) + { + ConfirmDialog dialog = new ConfirmDialog("Clear image cache:", "Do you really want to **delete** all cached images?"); + await UiUtils.ShowDialogAsync(dialog); + await VIEW_MODEL.ClearImageCacheAsync(dialog.VIEW_MODEL); + await imageCacheFolder_fsc.RecalculateFolderSizeAsync(); + } + + private async void OpenImageCahceFolder_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.OpenImageCacheFolderAsync(); + } + + private void Credits_btn_Click(object sender, RoutedEventArgs e) + { + UiUtils.NavigateToPage(typeof(CreditsPage)); + } + + private async void PrivacyPolicy_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ShowPrivacyPolicy(); + } + + private async void License_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ShowLicenceAsync(); + } + + private async void Feedback_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.GiveFeedbackAsync(); + } + + private async void ReportBug_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ReportBugAsync(); + } + + private async void ViewOnGitHub_btn_Click(object sender, RoutedEventArgs e) + { + await VIEW_MODEL.ViewOnGithubAsync(); + } + + private void Main_nview_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItem is Microsoft.UI.Xaml.Controls.NavigationViewItem item && item.Tag is string s) + { + ScrollToSection(s); + } + } + + private async void ClearCache_btn_Click(object sender, RoutedEventArgs e) + { + ClearCacheDialog dialog = new ClearCacheDialog(); + await UiUtils.ShowDialogAsync(dialog); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + if (e.Parameter is string s) + { + requestedSection = s; + } + } + + private void Main_nview_Loaded(object sender, RoutedEventArgs e) + { + if (requestedSection is null) + { + SelectMenuItem("Logs"); + } + else + { + SelectMenuItem(requestedSection); + } + } + + #endregion + } +} diff --git a/UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml b/UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml new file mode 100644 index 000000000..e9993a94c --- /dev/null +++ b/UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml.cs b/UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml.cs similarity index 53% rename from UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml.cs rename to UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml.cs index c6e36e6cb..8715702c4 100644 --- a/UWP XMPP Client/Pages/SettingsPages/BackgroundTasksSettingsPage.xaml.cs +++ b/UWPX_UI/Pages/Settings/PersonalizeSettingsPage.xaml.cs @@ -1,29 +1,22 @@ -using Data_Manager2.Classes; +using UWPX_UI.Extensions; +using UWPX_UI_Context.Classes.DataContext.Pages; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -namespace UWP_XMPP_Client.Pages.SettingsPages +namespace UWPX_UI.Pages.Settings { - public sealed partial class BackgroundTasksSettingsPage : Page + public sealed partial class PersonalizeSettingsPage : Page { //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ #region --Attributes-- - + public readonly PersonalizeSettingsPageContext VIEW_MODEL = new PersonalizeSettingsPageContext(); #endregion //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- - /// - /// Basic Constructor - /// - /// - /// 04/09/2017 Created [Fabian Sauter] - /// - public BackgroundTasksSettingsPage() + public PersonalizeSettingsPage() { this.InitializeComponent(); - Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += AbstractBackRequestPage_BackRequested; - loadSettings(); } #endregion @@ -39,10 +32,7 @@ public BackgroundTasksSettingsPage() #endregion #region --Misc Methods (Private)-- - private void loadSettings() - { - disablePush_tgls.IsOn = Settings.getSettingBoolean(SettingsConsts.DISABLE_PUSH); - } + #endregion @@ -52,23 +42,29 @@ private void loadSettings() #endregion //--------------------------------------------------------Events:---------------------------------------------------------------------\\ #region --Events-- - private void AbstractBackRequestPage_BackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e) + private void Main_nview_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args) { - Frame rootFrame = Window.Current.Content as Frame; - if (rootFrame is null) + if (args.SelectedItem is Microsoft.UI.Xaml.Controls.NavigationViewItem item) { - return; - } - if (rootFrame.CanGoBack && e.Handled == false) - { - e.Handled = true; - rootFrame.GoBack(); + switch (item.Tag) + { + case "Theme": + ScrollViewerExtensions.ScrollIntoViewVertically(main_scv, theme_scp, false); + break; + } } } - private void disablePush_tgls_Toggled(object sender, RoutedEventArgs e) + private void Main_nview_Loaded(object sender, RoutedEventArgs e) { - Settings.setSetting(SettingsConsts.DISABLE_PUSH, disablePush_tgls.IsOn); + foreach (object item in main_nview.MenuItems) + { + if (item is Microsoft.UI.Xaml.Controls.NavigationViewItem navItem && string.Equals((string)navItem.Tag, "Theme")) + { + main_nview.SelectedItem = item; + break; + } + } } #endregion diff --git a/UWPX_UI/Pages/Settings/SettingsPage.xaml b/UWPX_UI/Pages/Settings/SettingsPage.xaml new file mode 100644 index 000000000..2f6076084 --- /dev/null +++ b/UWPX_UI/Pages/Settings/SettingsPage.xaml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPX_UI/Pages/Settings/SettingsPage.xaml.cs b/UWPX_UI/Pages/Settings/SettingsPage.xaml.cs new file mode 100644 index 000000000..f11090bd6 --- /dev/null +++ b/UWPX_UI/Pages/Settings/SettingsPage.xaml.cs @@ -0,0 +1,135 @@ +using Microsoft.Toolkit.Uwp.UI.Animations; +using System.Collections.ObjectModel; +using System.Text; +using UWPX_UI_Context.Classes; +using UWPX_UI_Context.Classes.DataContext.Pages; +using UWPX_UI_Context.Classes.DataTemplates; +using UWPX_UI_Context.Classes.DataTemplates.Pages; +using Windows.ApplicationModel; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace UWPX_UI.Pages.Settings +{ + public sealed partial class SettingsPage : Page + { + //--------------------------------------------------------Attributes:-----------------------------------------------------------------\\ + #region --Attributes-- + public readonly SettingsPageContext VIEW_MODEL = new SettingsPageContext(); + private ObservableCollection SETTINGS_PAGES = new ObservableCollection() + { + new SettingsPageButtonDataTemplate {Glyph = "\xE13D", Name = "Accounts", Description = "Manage Accounts", NavTarget = typeof(AccountsSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\xE771", Name = "Personalize", Description = "Background, Color", NavTarget = typeof(PersonalizeSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\xE12B", Name = "Data", Description = "Mobile Data, Wifi", NavTarget = typeof(DataSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\xE15F", Name = "Chat", Description = "Availability", NavTarget = typeof(ChatSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\xE71D", Name = "Background Tasks", Description = "Manage Tasks", NavTarget = typeof(BackgroundTaskSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\uE72E", Name = "Security", Description = "Certificates, Password Vault", NavTarget = typeof(SettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\uEB52", Name = "Donate", Description = "Keep The Project Running", NavTarget = typeof(DonateSettingsPage)}, + new SettingsPageButtonDataTemplate {Glyph = "\xE713", Name = "Misc", Description = "Everything Else", NavTarget = typeof(MiscSettingsPage)}, + }; + + private FrameworkElement LastPopUpElement = null; + + #endregion + //--------------------------------------------------------Constructor:----------------------------------------------------------------\\ + #region --Constructors-- + public SettingsPage() + { + this.InitializeComponent(); + LoadAppVersion(); + VIEW_MODEL.MODEL.PropertyChanged += MODEL_PropertyChanged; + } + + private void MODEL_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (sender is SettingsPageDataTemplate settingsPageDataTemplate) + { + switch (e.PropertyName) + { + case nameof(settingsPageDataTemplate.DebugSettingsEnabled): + if (settingsPageDataTemplate.DebugSettingsEnabled) + { + debugSettings_notification.Show("Debug settings enabled.", 5000); + } + else + { + debugSettings_notification.Show("Debug settings disabled.", 5000); + } + break; + } + } + } + + #endregion + //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\ + #region --Set-, Get- Methods-- + + + #endregion + //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ + #region --Misc Methods (Public)-- + + + #endregion + + #region --Misc Methods (Private)-- + private void LoadAppVersion() + { + name_run.Text = Package.Current.DisplayName; + PackageVersion version = Package.Current.Id.Version; + StringBuilder sb = new StringBuilder("v."); + sb.Append(version.Major); + sb.Append('.'); + sb.Append(version.Minor); + sb.Append('.'); + sb.Append(version.Build); + sb.Append('.'); + sb.Append(version.Revision); + version_run.Text = sb.ToString(); + } + + #endregion + + #region --Misc Methods (Protected)-- + + + #endregion + //--------------------------------------------------------Events:---------------------------------------------------------------------\\ + #region --Events-- + private void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is SettingsPageButtonDataTemplate page) + { + UiUtils.NavigateToPage(page.NavTarget); + } + } + + private void SettingsSelectionControl_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (DeviceFamilyHelper.IsMouseInteractionMode() && sender is FrameworkElement settingsSelection) + { + LastPopUpElement = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(settingsSelection) as FrameworkElement) as FrameworkElement; + Canvas.SetZIndex(LastPopUpElement, 10); + LastPopUpElement.Scale(scaleX: 1.05f, scaleY: 1.05f, centerX: (float)LastPopUpElement.Width / 2, centerY: (float)LastPopUpElement.Height / 2, easingType: EasingType.Sine).Start(); + } + } + + private void SettingsSelectionControl_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (!(LastPopUpElement is null)) + { + Canvas.SetZIndex(LastPopUpElement, 0); + LastPopUpElement.Scale(centerX: (float)LastPopUpElement.Width / 2, centerY: (float)LastPopUpElement.Height / 2, easingType: EasingType.Sine).Start(); + LastPopUpElement = null; + } + } + + private void Version_tbx_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) + { + VIEW_MODEL.OnVersionTextTapped(); + } + + #endregion + } +} diff --git a/UWPX_UI/Properties/AssemblyInfo.cs b/UWPX_UI/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..39181635a --- /dev/null +++ b/UWPX_UI/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UWPX_UI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UWPX_UI")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/UWPX_UI/Properties/Default.rd.xml b/UWPX_UI/Properties/Default.rd.xml new file mode 100644 index 000000000..af00722cd --- /dev/null +++ b/UWPX_UI/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/UWPX_UI/Resources/MyBrushes.xaml b/UWPX_UI/Resources/MyBrushes.xaml new file mode 100644 index 000000000..eaf6c4563 --- /dev/null +++ b/UWPX_UI/Resources/MyBrushes.xaml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + #FF000000 + + + + + + + + + #FF000000 + + + + + + + + + + diff --git a/UWPX_UI/Resources/MyValueConverters.xaml b/UWPX_UI/Resources/MyValueConverters.xaml new file mode 100644 index 000000000..62d2248ec --- /dev/null +++ b/UWPX_UI/Resources/MyValueConverters.xaml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/UWPX_UI/Resources/Styles/ChatsQueryTextBoxStyle.xaml b/UWPX_UI/Resources/Styles/ChatsQueryTextBoxStyle.xaml new file mode 100644 index 000000000..3c96d07ec --- /dev/null +++ b/UWPX_UI/Resources/Styles/ChatsQueryTextBoxStyle.xaml @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +