diff --git a/Quarrel.sln b/Quarrel.sln
index 9d9dabf59..5b9f07dfa 100644
--- a/Quarrel.sln
+++ b/Quarrel.sln
@@ -42,26 +42,35 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quarrel.Testing.DirtyServic
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quarrel.Markdown", "src\Libs\Quarrel.Markdown\Quarrel.Markdown.csproj", "{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quarrel.Common", "src\Libs\Quarrel.Common\Quarrel.Common.csproj", "{DA2758C2-8D98-46EF-8DE3-833089AF99C4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Alpha|Any CPU = Alpha|Any CPU
Alpha|ARM = Alpha|ARM
Alpha|ARM64 = Alpha|ARM64
Alpha|x64 = Alpha|x64
Alpha|x86 = Alpha|x86
+ Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
+ Insider|Any CPU = Insider|Any CPU
Insider|ARM = Insider|ARM
Insider|ARM64 = Insider|ARM64
Insider|x64 = Insider|x64
Insider|x86 = Insider|x86
+ Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|Any CPU.ActiveCfg = Alpha|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|Any CPU.Build.0 = Alpha|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|Any CPU.Deploy.0 = Alpha|x64
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|ARM.ActiveCfg = Alpha|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|ARM.Build.0 = Alpha|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|ARM.Deploy.0 = Alpha|ARM
@@ -74,6 +83,9 @@ Global
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|x86.ActiveCfg = Alpha|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|x86.Build.0 = Alpha|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Alpha|x86.Deploy.0 = Alpha|x86
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|Any CPU.Build.0 = Debug|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|Any CPU.Deploy.0 = Debug|x64
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|ARM.ActiveCfg = Debug|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|ARM.Build.0 = Debug|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|ARM.Deploy.0 = Debug|ARM
@@ -86,6 +98,9 @@ Global
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|x86.ActiveCfg = Debug|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|x86.Build.0 = Debug|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Debug|x86.Deploy.0 = Debug|x86
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|Any CPU.ActiveCfg = Insider|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|Any CPU.Build.0 = Insider|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|Any CPU.Deploy.0 = Insider|x64
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|ARM.ActiveCfg = Insider|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|ARM.Build.0 = Insider|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|ARM.Deploy.0 = Insider|ARM
@@ -98,6 +113,9 @@ Global
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|x86.ActiveCfg = Insider|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|x86.Build.0 = Insider|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Insider|x86.Deploy.0 = Insider|x86
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|Any CPU.ActiveCfg = Release|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|Any CPU.Build.0 = Release|x64
+ {0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|Any CPU.Deploy.0 = Release|x64
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|ARM.ActiveCfg = Release|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|ARM.Build.0 = Release|ARM
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|ARM.Deploy.0 = Release|ARM
@@ -110,6 +128,8 @@ Global
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|x86.ActiveCfg = Release|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|x86.Build.0 = Release|x86
{0247C94D-5FD1-45D6-9312-7440317F6C40}.Release|x86.Deploy.0 = Release|x86
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|ARM.Build.0 = Alpha|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -118,6 +138,8 @@ Global
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|x64.Build.0 = Alpha|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|ARM.ActiveCfg = Debug|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|ARM.Build.0 = Debug|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -126,6 +148,8 @@ Global
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|x64.Build.0 = Debug|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|x86.ActiveCfg = Debug|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Debug|x86.Build.0 = Debug|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|Any CPU.Build.0 = Insider|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|ARM.ActiveCfg = Insider|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|ARM.Build.0 = Insider|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|ARM64.ActiveCfg = Insider|Any CPU
@@ -134,6 +158,8 @@ Global
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|x64.Build.0 = Insider|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|x86.ActiveCfg = Insider|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Insider|x86.Build.0 = Insider|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|Any CPU.Build.0 = Release|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|ARM.ActiveCfg = Release|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|ARM.Build.0 = Release|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -142,6 +168,8 @@ Global
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|x64.Build.0 = Release|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|x86.ActiveCfg = Release|Any CPU
{E069A285-68FE-43C7-957F-9CC6BBF17BCE}.Release|x86.Build.0 = Release|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|ARM.Build.0 = Alpha|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -150,6 +178,8 @@ Global
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|x64.Build.0 = Alpha|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|ARM.ActiveCfg = Debug|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|ARM.Build.0 = Debug|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -158,6 +188,8 @@ Global
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|x64.Build.0 = Debug|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|x86.ActiveCfg = Debug|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Debug|x86.Build.0 = Debug|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Insider|Any CPU.Build.0 = Insider|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|ARM.ActiveCfg = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|ARM.Build.0 = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|ARM64.ActiveCfg = Release|Any CPU
@@ -166,6 +198,8 @@ Global
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|x64.Build.0 = Insider|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|x86.ActiveCfg = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Insider|x86.Build.0 = Release|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {43360223-1CD9-4962-84CC-0698F6D11837}.Release|Any CPU.Build.0 = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|ARM.ActiveCfg = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|ARM.Build.0 = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -174,6 +208,8 @@ Global
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|x64.Build.0 = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|x86.ActiveCfg = Release|Any CPU
{43360223-1CD9-4962-84CC-0698F6D11837}.Release|x86.Build.0 = Release|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|ARM.Build.0 = Alpha|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -182,6 +218,8 @@ Global
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|x64.Build.0 = Alpha|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|ARM.Build.0 = Debug|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -190,6 +228,8 @@ Global
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|x64.Build.0 = Debug|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|x86.ActiveCfg = Debug|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Debug|x86.Build.0 = Debug|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|Any CPU.Build.0 = Insider|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|ARM.ActiveCfg = Insider|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|ARM.Build.0 = Insider|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|ARM64.ActiveCfg = Insider|Any CPU
@@ -198,6 +238,8 @@ Global
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|x64.Build.0 = Insider|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|x86.ActiveCfg = Insider|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Insider|x86.Build.0 = Insider|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|Any CPU.Build.0 = Release|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|ARM.ActiveCfg = Release|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|ARM.Build.0 = Release|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -206,6 +248,8 @@ Global
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|x64.Build.0 = Release|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|x86.ActiveCfg = Release|Any CPU
{CEBA2186-BA2C-4616-BEEE-01E0F899A4A1}.Release|x86.Build.0 = Release|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|ARM.Build.0 = Alpha|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -214,6 +258,8 @@ Global
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|x64.Build.0 = Alpha|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|ARM.Build.0 = Debug|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -222,6 +268,8 @@ Global
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|x64.Build.0 = Debug|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|x86.ActiveCfg = Debug|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Debug|x86.Build.0 = Debug|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|Any CPU.Build.0 = Insider|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|ARM.ActiveCfg = Insider|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|ARM.Build.0 = Insider|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|ARM64.ActiveCfg = Insider|Any CPU
@@ -230,6 +278,8 @@ Global
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|x64.Build.0 = Insider|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|x86.ActiveCfg = Insider|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Insider|x86.Build.0 = Insider|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|Any CPU.Build.0 = Release|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|ARM.ActiveCfg = Release|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|ARM.Build.0 = Release|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -238,6 +288,8 @@ Global
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|x64.Build.0 = Release|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|x86.ActiveCfg = Release|Any CPU
{E20B2927-0C9E-437F-9B51-B76BAE2EF67C}.Release|x86.Build.0 = Release|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|ARM.Build.0 = Alpha|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -246,6 +298,8 @@ Global
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|x64.Build.0 = Alpha|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|ARM.Build.0 = Debug|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -254,6 +308,8 @@ Global
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|x64.Build.0 = Debug|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|x86.ActiveCfg = Debug|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Debug|x86.Build.0 = Debug|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|Any CPU.Build.0 = Insider|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|ARM.ActiveCfg = Insider|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|ARM.Build.0 = Insider|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|ARM64.ActiveCfg = Insider|Any CPU
@@ -262,6 +318,8 @@ Global
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|x64.Build.0 = Insider|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|x86.ActiveCfg = Insider|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Insider|x86.Build.0 = Insider|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|Any CPU.Build.0 = Release|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|ARM.ActiveCfg = Release|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|ARM.Build.0 = Release|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -270,6 +328,8 @@ Global
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|x64.Build.0 = Release|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|x86.ActiveCfg = Release|Any CPU
{D5C342A2-FB47-4D19-8073-E7F15EE43F24}.Release|x86.Build.0 = Release|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|Any CPU.ActiveCfg = Alpha|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|Any CPU.Build.0 = Alpha|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|ARM.ActiveCfg = Alpha|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|ARM.Build.0 = Alpha|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|ARM64.ActiveCfg = Alpha|Any CPU
@@ -278,6 +338,8 @@ Global
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|x64.Build.0 = Alpha|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|x86.ActiveCfg = Alpha|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Alpha|x86.Build.0 = Alpha|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|ARM.Build.0 = Debug|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -286,6 +348,8 @@ Global
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|x64.Build.0 = Debug|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|x86.ActiveCfg = Debug|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Debug|x86.Build.0 = Debug|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|Any CPU.Build.0 = Insider|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|ARM.ActiveCfg = Insider|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|ARM.Build.0 = Insider|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|ARM64.ActiveCfg = Insider|Any CPU
@@ -294,6 +358,8 @@ Global
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|x64.Build.0 = Insider|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|x86.ActiveCfg = Insider|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Insider|x86.Build.0 = Insider|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|Any CPU.Build.0 = Release|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|ARM.ActiveCfg = Release|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|ARM.Build.0 = Release|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|ARM64.ActiveCfg = Release|Any CPU
@@ -302,6 +368,8 @@ Global
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|x64.Build.0 = Release|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|x86.ActiveCfg = Release|Any CPU
{D34F80C0-0316-43C3-BBE9-7AF099A1C6A0}.Release|x86.Build.0 = Release|Any CPU
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|Any CPU.ActiveCfg = Alpha|x64
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|Any CPU.Build.0 = Alpha|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|ARM.ActiveCfg = Alpha|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|ARM.Build.0 = Alpha|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|ARM64.ActiveCfg = Alpha|ARM64
@@ -310,6 +378,8 @@ Global
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|x64.Build.0 = Alpha|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|x86.ActiveCfg = Alpha|x86
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Alpha|x86.Build.0 = Alpha|x86
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|Any CPU.Build.0 = Debug|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|ARM.ActiveCfg = Debug|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|ARM.Build.0 = Debug|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -318,6 +388,8 @@ Global
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|x64.Build.0 = Debug|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|x86.ActiveCfg = Debug|x86
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Debug|x86.Build.0 = Debug|x86
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|Any CPU.ActiveCfg = Insider|x64
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|Any CPU.Build.0 = Insider|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|ARM.ActiveCfg = Insider|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|ARM.Build.0 = Insider|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|ARM64.ActiveCfg = Insider|ARM64
@@ -326,6 +398,8 @@ Global
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|x64.Build.0 = Insider|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|x86.ActiveCfg = Insider|x86
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Insider|x86.Build.0 = Insider|x86
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|Any CPU.ActiveCfg = Release|x64
+ {BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|Any CPU.Build.0 = Release|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|ARM.ActiveCfg = Release|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|ARM.Build.0 = Release|ARM
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -334,10 +408,16 @@ Global
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|x64.Build.0 = Release|x64
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|x86.ActiveCfg = Release|x86
{BA8BE893-2B4D-4213-A4AF-53AA28BF2E46}.Release|x86.Build.0 = Release|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|Any CPU.ActiveCfg = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|Any CPU.Build.0 = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|Any CPU.Deploy.0 = Debug|x86
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|ARM.ActiveCfg = Debug|ARM
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|ARM64.ActiveCfg = Debug|ARM64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|x64.ActiveCfg = Debug|x64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Alpha|x86.ActiveCfg = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|Any CPU.Build.0 = Debug|x64
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|Any CPU.Deploy.0 = Debug|x64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|ARM.ActiveCfg = Debug|ARM
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|ARM.Build.0 = Debug|ARM
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|ARM.Deploy.0 = Debug|ARM
@@ -350,18 +430,28 @@ Global
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|x86.ActiveCfg = Debug|x86
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|x86.Build.0 = Debug|x86
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Debug|x86.Deploy.0 = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|Any CPU.ActiveCfg = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|Any CPU.Build.0 = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|Any CPU.Deploy.0 = Debug|x86
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|ARM.ActiveCfg = Debug|ARM
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|ARM64.ActiveCfg = Debug|ARM64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|x64.ActiveCfg = Debug|x64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Insider|x86.ActiveCfg = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|Any CPU.ActiveCfg = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|Any CPU.Build.0 = Debug|x86
+ {4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|Any CPU.Deploy.0 = Debug|x86
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|ARM.ActiveCfg = Debug|ARM
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|ARM64.ActiveCfg = Debug|ARM64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|x64.ActiveCfg = Debug|x64
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE}.Release|x86.ActiveCfg = Debug|x86
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|Any CPU.ActiveCfg = Release|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|Any CPU.Build.0 = Release|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|ARM.ActiveCfg = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|ARM64.ActiveCfg = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|x64.ActiveCfg = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Alpha|x86.ActiveCfg = Debug|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|ARM.Build.0 = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
@@ -370,14 +460,20 @@ Global
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|x64.Build.0 = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|x86.ActiveCfg = Debug|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Debug|x86.Build.0 = Debug|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|Any CPU.Build.0 = Insider|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|ARM.ActiveCfg = Insider|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|ARM64.ActiveCfg = Insider|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|x64.ActiveCfg = Insider|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Insider|x86.ActiveCfg = Insider|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|Any CPU.Build.0 = Release|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|ARM.ActiveCfg = Release|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|ARM64.ActiveCfg = Release|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|x64.ActiveCfg = Release|Any CPU
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|Any CPU.ActiveCfg = Alpha|x64
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|Any CPU.Build.0 = Alpha|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|ARM.ActiveCfg = Alpha|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|ARM.Build.0 = Alpha|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|ARM64.ActiveCfg = Alpha|ARM64
@@ -386,6 +482,8 @@ Global
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|x64.Build.0 = Alpha|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|x86.ActiveCfg = Alpha|x86
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Alpha|x86.Build.0 = Alpha|x86
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|Any CPU.Build.0 = Debug|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|ARM.ActiveCfg = Debug|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|ARM.Build.0 = Debug|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -394,6 +492,8 @@ Global
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|x64.Build.0 = Debug|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|x86.ActiveCfg = Debug|x86
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Debug|x86.Build.0 = Debug|x86
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|Any CPU.ActiveCfg = Insider|x64
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|Any CPU.Build.0 = Insider|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|ARM.ActiveCfg = Insider|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|ARM.Build.0 = Insider|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|ARM64.ActiveCfg = Insider|ARM64
@@ -402,6 +502,8 @@ Global
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|x64.Build.0 = Insider|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|x86.ActiveCfg = Insider|x86
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Insider|x86.Build.0 = Insider|x86
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|Any CPU.ActiveCfg = Release|x64
+ {E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|Any CPU.Build.0 = Release|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|ARM.ActiveCfg = Release|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|ARM.Build.0 = Release|ARM
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -410,6 +512,46 @@ Global
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|x64.Build.0 = Release|x64
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|x86.ActiveCfg = Release|x86
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3}.Release|x86.Build.0 = Release|x86
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|Any CPU.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|ARM.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|ARM.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|ARM64.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|ARM64.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|x64.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|x64.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|x86.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Alpha|x86.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|ARM.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|x64.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Debug|x86.Build.0 = Debug|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|Any CPU.ActiveCfg = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|Any CPU.Build.0 = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|ARM.ActiveCfg = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|ARM.Build.0 = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|ARM64.ActiveCfg = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|ARM64.Build.0 = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|x64.ActiveCfg = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|x64.Build.0 = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|x86.ActiveCfg = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Insider|x86.Build.0 = Insider|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|ARM.ActiveCfg = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|ARM.Build.0 = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|ARM64.Build.0 = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|x64.ActiveCfg = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|x64.Build.0 = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|x86.ActiveCfg = Release|Any CPU
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -424,6 +566,7 @@ Global
{4304E7AB-92E3-4313-AD8B-EFDCB033C0CE} = {7B631D3A-F353-4557-B848-A68140341F53}
{8BD6BF36-6ECD-4103-B064-1CAE37E954F2} = {E3BFEBEE-5570-4885-B4C7-2CB3E7B04C60}
{E4B6DFB8-B9B7-4EE7-9F86-AFDDC221FFB3} = {29E0E840-B85F-4209-933B-8369DB2EA187}
+ {DA2758C2-8D98-46EF-8DE3-833089AF99C4} = {29E0E840-B85F-4209-933B-8369DB2EA187}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B4323A3-5C28-4929-AB1C-20CE992D6024}
diff --git a/samples/Quarrel.Samples.RichPresence/MainPage.xaml.cs b/samples/Quarrel.Samples.RichPresence/MainPage.xaml.cs
index 09c16983d..f1b30cacd 100644
--- a/samples/Quarrel.Samples.RichPresence/MainPage.xaml.cs
+++ b/samples/Quarrel.Samples.RichPresence/MainPage.xaml.cs
@@ -43,7 +43,7 @@ private async void Connect(object sender, RoutedEventArgs e)
private async void SetActivity(object sender, RoutedEventArgs e)
{
Activity activity = new Activity(ActivityName.Text);
- bool success = await _connection.SetActivity(activity);
+ await _connection.SetActivity(activity);
}
}
}
diff --git a/src/API/Discord.API.Status/Discord.API.Status.csproj b/src/API/Discord.API.Status/Discord.API.Status.csproj
index 217d52365..221d2b7d1 100644
--- a/src/API/Discord.API.Status/Discord.API.Status.csproj
+++ b/src/API/Discord.API.Status/Discord.API.Status.csproj
@@ -3,6 +3,7 @@
netstandard2.0
Debug;Insider;Alpha;Release
+ false
diff --git a/src/API/Discord.API.Status/Models/Datum.cs b/src/API/Discord.API.Status/Models/Datum.cs
index 2ff08f822..f2b159a9a 100644
--- a/src/API/Discord.API.Status/Models/Datum.cs
+++ b/src/API/Discord.API.Status/Models/Datum.cs
@@ -2,6 +2,9 @@
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Status.Models
{
///
diff --git a/src/API/Discord.API.Status/Models/Summary.cs b/src/API/Discord.API.Status/Models/Summary.cs
index 5a68e80b0..92ade592f 100644
--- a/src/API/Discord.API.Status/Models/Summary.cs
+++ b/src/API/Discord.API.Status/Models/Summary.cs
@@ -2,13 +2,25 @@
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Status.Models
{
+ ///
+ /// A summary of a data set.
+ ///
public partial class Summary
{
+ ///
+ /// The sum of the data.
+ ///
[JsonPropertyName("sum")]
public double Sum { get; set; }
+ ///
+ /// The mean of the data.
+ ///
[JsonPropertyName("mean")]
public double Mean { get; set; }
}
diff --git a/src/API/Discord.API/Discord.API.csproj b/src/API/Discord.API/Discord.API.csproj
index 7b563d1a1..582f183a0 100644
--- a/src/API/Discord.API/Discord.API.csproj
+++ b/src/API/Discord.API/Discord.API.csproj
@@ -10,4 +10,8 @@
+
+
+
+
diff --git a/src/API/Discord.API/Gateways/Gateway.Requests.cs b/src/API/Discord.API/Gateways/Gateway.Requests.cs
index 17bcb7c65..cee451962 100644
--- a/src/API/Discord.API/Gateways/Gateway.Requests.cs
+++ b/src/API/Discord.API/Gateways/Gateway.Requests.cs
@@ -1,6 +1,7 @@
// Quarrel © 2022
using Discord.API.Gateways.Models;
+using Discord.API.Models.Enums.Users;
using System;
using System.Threading.Tasks;
@@ -43,11 +44,11 @@ public void SubscribeToGuildAsync(ulong[] channelIds)
throw new NotImplementedException();
}
- public async Task UpdateStatusAsync(string status, int? idleSince, bool isAfk)
+ public async Task UpdateStatusAsync(UserStatus status, int? idleSince = null, bool isAfk = false)
{
var payload = new StatusUpdate()
{
- Status = status,
+ Status = status.GetStringValue(),
IdleSince = idleSince,
IsAFK = isAfk,
};
@@ -55,7 +56,7 @@ public async Task UpdateStatusAsync(string status, int? idleSince, bool isAfk)
await UpdateStatusAsync(payload);
}
- public async Task UpdateStatusAsync(StatusUpdate payload)
+ private async Task UpdateStatusAsync(StatusUpdate payload)
{
var frame = new SocketFrame()
{
diff --git a/src/API/Discord.API/Models/Enums/Messages/ComponentType.cs b/src/API/Discord.API/Models/Enums/Messages/ComponentType.cs
index e53491ee4..a8587308e 100644
--- a/src/API/Discord.API/Models/Enums/Messages/ComponentType.cs
+++ b/src/API/Discord.API/Models/Enums/Messages/ComponentType.cs
@@ -1,9 +1,5 @@
// Quarrel © 2022
-using System;
-using System.Collections.Generic;
-using System.Text;
-
namespace Discord.API.Models.Enums.Messages
{
public enum ComponentType
diff --git a/src/API/Discord.API/Models/Enums/Messages/MessageFlags.cs b/src/API/Discord.API/Models/Enums/Messages/MessageFlags.cs
index df1fab8aa..f2ac91d64 100644
--- a/src/API/Discord.API/Models/Enums/Messages/MessageFlags.cs
+++ b/src/API/Discord.API/Models/Enums/Messages/MessageFlags.cs
@@ -1,9 +1,5 @@
// Quarrel © 2022
-using System;
-using System.Collections.Generic;
-using System.Text;
-
namespace Discord.API.Models.Enums.Messages
{
public enum MessageFlags
diff --git a/src/API/Discord.API/Models/Enums/Users/UserStatus.cs b/src/API/Discord.API/Models/Enums/Users/UserStatus.cs
index baeae8432..f8c098ad8 100644
--- a/src/API/Discord.API/Models/Enums/Users/UserStatus.cs
+++ b/src/API/Discord.API/Models/Enums/Users/UserStatus.cs
@@ -1,5 +1,7 @@
// Quarrel © 2022
+using Quarrel.Attributes;
+
namespace Discord.API.Models.Enums.Users
{
///
@@ -10,31 +12,37 @@ public enum UserStatus
///
/// The user is offline.
///
+ [StringValue("offline")]
Offline,
///
/// The user is online.
///
+ [StringValue("online")]
Online,
///
/// The user is idle.
///
+ [StringValue("idle")]
Idle,
///
/// The user is AFK.
///
+ [StringValue("afk")]
AFK,
///
/// The user is on do not disturb.
///
+ [StringValue("dnd")]
DoNotDisturb,
///
/// The user is marked offline.
///
+ [StringValue("invisible")]
Invisible,
}
}
diff --git a/src/API/Discord.API/Models/Json/Applications/JsonApplication.cs b/src/API/Discord.API/Models/Json/Applications/JsonApplication.cs
index 17a815413..acb67f844 100644
--- a/src/API/Discord.API/Models/Json/Applications/JsonApplication.cs
+++ b/src/API/Discord.API/Models/Json/Applications/JsonApplication.cs
@@ -1,11 +1,12 @@
// Quarrel © 2022
using Discord.API.Models.Json.Users;
-using System;
using System.Collections.Generic;
-using System.Text;
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Models.Json.Applications
{
internal class JsonApplication
diff --git a/src/API/Discord.API/Models/Json/Messages/JsonMessageComponent.cs b/src/API/Discord.API/Models/Json/Messages/JsonMessageComponent.cs
index 4c08dc543..0b38a8099 100644
--- a/src/API/Discord.API/Models/Json/Messages/JsonMessageComponent.cs
+++ b/src/API/Discord.API/Models/Json/Messages/JsonMessageComponent.cs
@@ -1,11 +1,12 @@
// Quarrel © 2022
using Discord.API.Models.Enums.Messages;
-using System;
using System.Collections.Generic;
-using System.Text;
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Models.Json.Messages
{
internal class JsonMessageComponent
diff --git a/src/API/Discord.API/Models/Json/Messages/JsonMessageInteraction.cs b/src/API/Discord.API/Models/Json/Messages/JsonMessageInteraction.cs
index 3cd27892f..584135b8b 100644
--- a/src/API/Discord.API/Models/Json/Messages/JsonMessageInteraction.cs
+++ b/src/API/Discord.API/Models/Json/Messages/JsonMessageInteraction.cs
@@ -4,6 +4,9 @@
using Discord.API.Models.Json.Users;
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Models.Json.Messages
{
internal class JsonMessageInteraction
diff --git a/src/API/Discord.API/Models/Json/Messages/JsonMessageStickerItem.cs b/src/API/Discord.API/Models/Json/Messages/JsonMessageStickerItem.cs
index a61903288..93722bcff 100644
--- a/src/API/Discord.API/Models/Json/Messages/JsonMessageStickerItem.cs
+++ b/src/API/Discord.API/Models/Json/Messages/JsonMessageStickerItem.cs
@@ -3,6 +3,9 @@
using Discord.API.Models.Enums.Stickers;
using System.Text.Json.Serialization;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Discord.API.Models.Json.Messages
{
internal class JsonMessageStickerItem
diff --git a/src/API/Discord.API/Models/Json/Settings/JsonModifyUserSettings.cs b/src/API/Discord.API/Models/Json/Settings/JsonModifyUserSettings.cs
new file mode 100644
index 000000000..4339de6ad
--- /dev/null
+++ b/src/API/Discord.API/Models/Json/Settings/JsonModifyUserSettings.cs
@@ -0,0 +1,15 @@
+// Quarrel © 2022
+
+using System.Text.Json.Serialization;
+
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
+namespace Discord.API.Models.Json.Settings
+{
+ internal class JsonModifyUserSettings
+ {
+ [JsonPropertyName("status")]
+ public string Status { get; set; }
+ }
+}
diff --git a/src/API/Discord.API/Rest/DiscordRestFactory.cs b/src/API/Discord.API/Rest/DiscordRestFactory.cs
index 69a505311..5a4c65a13 100644
--- a/src/API/Discord.API/Rest/DiscordRestFactory.cs
+++ b/src/API/Discord.API/Rest/DiscordRestFactory.cs
@@ -46,6 +46,14 @@ internal IChannelService GetChannelService()
{
return RestService.For(GetHttpClient(), _settings);
}
+
+ ///
+ /// Gets an instance of the .
+ ///
+ internal IUserService GetUserService()
+ {
+ return RestService.For(GetHttpClient(), _settings);
+ }
private HttpClient GetHttpClient(bool authenticated = true)
{
diff --git a/src/API/Discord.API/Rest/IChannelService.cs b/src/API/Discord.API/Rest/IChannelService.cs
index fef47d71f..54193e8d2 100644
--- a/src/API/Discord.API/Rest/IChannelService.cs
+++ b/src/API/Discord.API/Rest/IChannelService.cs
@@ -16,5 +16,8 @@ internal interface IChannelService
[Post("/v9/channels/{channelId}/messages")]
Task CreateMessage([AliasAs("channelId")] ulong channelId, [Body] JsonMessageUpsert message);
+
+ [Delete("/v9/channels/{channelId}/messages/{messageId}")]
+ Task DeleteMessage([AliasAs("channelId")] ulong channelId, [AliasAs("messageId")] ulong messageId);
}
}
diff --git a/src/API/Discord.API/Rest/IUserService.cs b/src/API/Discord.API/Rest/IUserService.cs
new file mode 100644
index 000000000..bc4b44218
--- /dev/null
+++ b/src/API/Discord.API/Rest/IUserService.cs
@@ -0,0 +1,15 @@
+// Quarrel © 2022
+
+using Discord.API.Models.Json.Settings;
+using Refit;
+using System.Threading.Tasks;
+
+namespace Discord.API.Rest
+{
+ internal interface IUserService
+ {
+ [Patch("/v6/users/@me/settings")]
+ [Headers("Content-Type: application/json;")]
+ Task UpdateSettings([Body] JsonModifyUserSettings settings);
+ }
+}
diff --git a/src/Quarrel.ViewModels/Attributes/StringValueAttribute.cs b/src/Libs/Quarrel.Common/Attributes/StringValueAttribute.cs
similarity index 100%
rename from src/Quarrel.ViewModels/Attributes/StringValueAttribute.cs
rename to src/Libs/Quarrel.Common/Attributes/StringValueAttribute.cs
diff --git a/src/Quarrel.ViewModels/Extensions/System/EnumExtensions.cs b/src/Libs/Quarrel.Common/Extensions/System/EnumExtensions.cs
similarity index 100%
rename from src/Quarrel.ViewModels/Extensions/System/EnumExtensions.cs
rename to src/Libs/Quarrel.Common/Extensions/System/EnumExtensions.cs
diff --git a/src/Libs/Quarrel.Common/Quarrel.Common.csproj b/src/Libs/Quarrel.Common/Quarrel.Common.csproj
new file mode 100644
index 000000000..5813cf0b4
--- /dev/null
+++ b/src/Libs/Quarrel.Common/Quarrel.Common.csproj
@@ -0,0 +1,10 @@
+
+
+
+ netstandard2.0
+ Quarrel.Common
+ Quarrel
+ Debug;Insider;Alpha;Release
+
+
+
diff --git a/src/Libs/Quarrel.Markdown/Quarrel.Markdown.csproj b/src/Libs/Quarrel.Markdown/Quarrel.Markdown.csproj
index 9741facdd..e7e2602b4 100644
--- a/src/Libs/Quarrel.Markdown/Quarrel.Markdown.csproj
+++ b/src/Libs/Quarrel.Markdown/Quarrel.Markdown.csproj
@@ -3814,11 +3814,6 @@
-
-
- ..\..\..\..\..\..\.nuget\packages\microsoft.xaml.behaviors.uwp.managed\2.0.1\lib\uap10.0\Microsoft.Xaml.Interactivity.dll
-
-
14.0
diff --git a/src/Libs/Quarrel.Markdown/Rendering/Elements/TimestampElement.cs b/src/Libs/Quarrel.Markdown/Rendering/Elements/TimestampElement.cs
index f69eb8f33..c0b25be98 100644
--- a/src/Libs/Quarrel.Markdown/Rendering/Elements/TimestampElement.cs
+++ b/src/Libs/Quarrel.Markdown/Rendering/Elements/TimestampElement.cs
@@ -14,11 +14,11 @@ internal TimestampElement(Timestamp timestamp) : base(timestamp)
{
"F" or "" => timestamp.Time.ToString("F"),
"D" => timestamp.Time.ToString("d MMMM yyyy"),
- "R" => timestamp.Time.Humanize(),
"T" => timestamp.Time.ToString("T"),
"d" => timestamp.Time.ToString("d"),
"f" => timestamp.Time.ToString("MMMM yyyy HH:mm"),
"t" => timestamp.Time.ToString("t"),
+ "R" or _ => timestamp.Time.Humanize(),
};
}
}
diff --git a/src/Quarrel.Client/Models/Messages/Embeds/Attachment.cs b/src/Quarrel.Client/Models/Messages/Embeds/Attachment.cs
index 9a6b03e16..3f8f8943c 100644
--- a/src/Quarrel.Client/Models/Messages/Embeds/Attachment.cs
+++ b/src/Quarrel.Client/Models/Messages/Embeds/Attachment.cs
@@ -5,6 +5,9 @@
namespace Quarrel.Client.Models.Messages.Embeds
{
+ ///
+ /// An attachment in a message.
+ ///
public class Attachment : SnowflakeItem
{
internal Attachment(JsonAttachment jsonAttachment, QuarrelClient context) :
@@ -12,22 +15,41 @@ internal Attachment(JsonAttachment jsonAttachment, QuarrelClient context) :
{
Id = jsonAttachment.Id;
Filename = jsonAttachment.Filename;
+ Size = jsonAttachment.Size;
Url = jsonAttachment.Url;
ProxyUrl = jsonAttachment.ProxyUrl;
Height = jsonAttachment.Height;
Width = jsonAttachment.Width;
}
+ ///
+ /// Gets the name of the attached file.
+ ///
public string Filename { get; }
+ ///
+ /// Gets the size of the attached file.
+ ///
public ulong Size { get; }
+ ///
+ /// Gets the url of the attached file.
+ ///
public string Url { get; }
+ ///
+ /// Gets the proxy url of the attached file.
+ ///
public string ProxyUrl { get; }
+ ///
+ /// Gets the height of the attached file if the file is an image or video.
+ ///
public int? Height { get; }
+ ///
+ /// Gets the width of the attached file if the file is an image or video.
+ ///
public int? Width { get; }
}
}
diff --git a/src/Quarrel.Client/Models/Messages/Message.cs b/src/Quarrel.Client/Models/Messages/Message.cs
index 78d246ded..5d9bd725b 100644
--- a/src/Quarrel.Client/Models/Messages/Message.cs
+++ b/src/Quarrel.Client/Models/Messages/Message.cs
@@ -8,6 +8,9 @@
using Quarrel.Client.Models.Users;
using System;
+// JSON models don't need to respect standard nullable rules.
+#pragma warning disable CS8618
+
namespace Quarrel.Client.Models.Messages
{
///
@@ -66,8 +69,10 @@ internal Message(JsonMessage jsonMessage, QuarrelClient context) :
Interaction = jsonMessage.Interaction;
}
- public ulong? ChannelId { get; private set; }
+ ///
+ public ulong ChannelId { get; private set; }
+ ///
public ulong? GuildId { get; private set; }
///
@@ -92,7 +97,10 @@ internal Message(JsonMessage jsonMessage, QuarrelClient context) :
public DateTimeOffset? EditedTimestamp { get; private set; }
///
- public User? Author { get; private set; }
+ public User Author { get; private set; }
+
+ ///
+ public bool IsOwn => Author.Id == Context.CurrentUser?.Id;
///
public User[] Mentions { get; private set; }
@@ -108,5 +116,11 @@ internal Message(JsonMessage jsonMessage, QuarrelClient context) :
///
public ulong? WebhookId { get; private set; }
+
+ ///
+ public Uri MessageUri
+ => new Uri($"https://discord.com/channels/{GuildDisplayId}/{ChannelId}/{Id}");
+
+ private string GuildDisplayId => GuildId.HasValue ? $"{GuildId.Value}" : "@me";
}
}
diff --git a/src/Quarrel.Client/Models/Users/Interfaces/IUser.cs b/src/Quarrel.Client/Models/Users/Interfaces/IUser.cs
index 74b1e7783..376340998 100644
--- a/src/Quarrel.Client/Models/Users/Interfaces/IUser.cs
+++ b/src/Quarrel.Client/Models/Users/Interfaces/IUser.cs
@@ -5,6 +5,9 @@
namespace Quarrel.Client.Models.Users.Interfaces
{
+ ///
+ /// An interface for an object representing a user.
+ ///
public interface IUser : ISnowflakeItem
{
///
diff --git a/src/Quarrel.Client/QuarrelClient.Methods.cs b/src/Quarrel.Client/QuarrelClient.Methods.cs
index ddfd94255..fce54b977 100644
--- a/src/Quarrel.Client/QuarrelClient.Methods.cs
+++ b/src/Quarrel.Client/QuarrelClient.Methods.cs
@@ -1,8 +1,9 @@
// Quarrel © 2022
using CommunityToolkit.Diagnostics;
+using Discord.API.Models.Enums.Users;
using Discord.API.Models.Json.Messages;
-using Quarrel.Client.Models.Channels;
+using Discord.API.Models.Json.Settings;
using Quarrel.Client.Models.Channels.Interfaces;
using Quarrel.Client.Models.Guilds;
using Quarrel.Client.Models.Messages;
@@ -10,7 +11,6 @@
using Quarrel.Client.Models.Users;
using Refit;
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -26,12 +26,19 @@ public partial class QuarrelClient
return CurrentUser;
}
+ ///
+ /// Gets a user by id.
+ ///
+ /// The id of the user to get.
public User? GetUser(ulong id)
{
_userMap.TryGetValue(id, out var user);
return user;
}
+ ///
+ /// Gets the client settings.
+ ///
public Settings? GetSettings()
{
return _settings;
@@ -166,12 +173,57 @@ public IPrivateChannel[] GetPrivateChannels()
return privateChannels;
}
- public void SendMessage(ulong channelId, string content)
+ ///
+ /// Sends a message.
+ ///
+ /// The channel to send the message in.
+ /// The content of the message.
+ public async Task SendMessage(ulong channelId, string content)
{
Guard.IsNotNull(_channelService, nameof(_channelService));
ulong nonce = (ulong)new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds() << 22;
JsonMessageUpsert message = new JsonMessageUpsert(content, false, $"{nonce}");
- _channelService.CreateMessage(channelId, message);
+ await MakeRefitRequest(() => _channelService.CreateMessage(channelId, message));
+ }
+
+ ///
+ /// Deletes a message.
+ ///
+ /// The id of channel the message belongs to.
+ /// The id of the message to delete.
+ public async Task DeleteMessage(ulong channelId, ulong messageId)
+ {
+ Guard.IsNotNull(_channelService, nameof(_channelService));
+ await MakeRefitRequest(() => _channelService.DeleteMessage(channelId, messageId));
+ }
+
+ ///
+ /// Updates the user's online status.
+ ///
+ /// The new online status to set.
+ public async Task UpdateStatus(UserStatus status)
+ {
+ Guard.IsNotNull(_gateway, nameof(_gateway));
+ Guard.IsNotNull(_userService, nameof(_userService));
+
+ await _gateway.UpdateStatusAsync(status);
+ var settingsUpdate = new JsonModifyUserSettings()
+ {
+ Status = status.GetStringValue(),
+ };
+ await _userService.UpdateSettings(settingsUpdate);
+ }
+
+ private async Task MakeRefitRequest(Func request)
+ {
+ try
+ {
+ await request();
+ }
+ catch (ApiException ex)
+ {
+ HttpExceptionHandled?.Invoke(this, ex);
+ }
}
private async Task MakeRefitRequest(Func> request)
diff --git a/src/Quarrel.Client/QuarrelClient.State.cs b/src/Quarrel.Client/QuarrelClient.State.cs
index 258c52610..2f557fb3c 100644
--- a/src/Quarrel.Client/QuarrelClient.State.cs
+++ b/src/Quarrel.Client/QuarrelClient.State.cs
@@ -318,6 +318,13 @@ internal void UpdateSettings(JsonUserSettings jsonUserSettings)
{
var settings = new Settings(jsonUserSettings, this);
_settings = settings;
+
+ Guard.IsNotNull(_selfUser, nameof(_selfUser));
+
+ _selfUser.Presence = new Presence(new JsonPresence()
+ {
+ Status = jsonUserSettings.Status,
+ });
}
}
}
diff --git a/src/Quarrel.Client/QuarrelClient.cs b/src/Quarrel.Client/QuarrelClient.cs
index 1bf434aeb..3fa190d19 100644
--- a/src/Quarrel.Client/QuarrelClient.cs
+++ b/src/Quarrel.Client/QuarrelClient.cs
@@ -20,6 +20,7 @@ namespace Quarrel.Client
public partial class QuarrelClient
{
private IChannelService? _channelService;
+ private IUserService? _userService;
private IGatewayService? _gatewayService;
private Gateway? _gateway;
private string? _token;
@@ -61,6 +62,7 @@ private void InitializeServices(string token)
};
_channelService = restFactory.GetChannelService();
_gatewayService = restFactory.GetGatewayService();
+ _userService = restFactory.GetUserService();
}
private async Task SetupGatewayAsync(string token)
diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs
index fe9923a06..048412c6d 100644
--- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs
+++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs
@@ -75,7 +75,10 @@ private set
///
public abstract bool IsAccessible { get; }
-
+
+ ///
+ /// A virtual method that notifies property changed for a .
+ ///
protected virtual void AckUpdate()
{
OnPropertyChanged(nameof(Channel));
@@ -93,6 +96,7 @@ private void AckUpdateRoot()
///
/// Creates a new instance of a based on the type.
///
+ /// The to pass to the .
/// The to pass to the .
/// The to pass to the .
/// The to pass to the .
diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs
index f38946100..6e842afb0 100644
--- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs
+++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs
@@ -65,6 +65,7 @@ internal BindableGuildChannel(
///
/// Creates a new based on the type.
///
+ /// The to pass to the .
/// The to pass to the .
/// The to pass to the .
/// The to pass to the .
diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs
index 375ca3e32..00890bf0f 100644
--- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs
+++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs
@@ -1,6 +1,7 @@
// Quarrel © 2022
using Microsoft.Toolkit.Mvvm.Messaging;
+using Quarrel.Bindables.Abstract;
using Quarrel.Bindables.Channels.Interfaces;
using Quarrel.Client.Models.Channels.Abstract;
using Quarrel.Client.Models.Channels.Interfaces;
@@ -36,6 +37,14 @@ internal BindablePrivateChannel(
///
public IMessageChannel MessageChannel => (IMessageChannel)Channel;
+ ///
+ /// Creates a new instance of a based on the type.
+ ///
+ /// The to pass to the .
+ /// The to pass to the .
+ /// The to pass to the .
+ /// The to pass to the .
+ /// The channel to wrap.
public static BindablePrivateChannel? Create(
IMessenger messenger,
IDiscordService discordService,
diff --git a/src/Quarrel.ViewModels/Bindables/Messages/BindableMessage.cs b/src/Quarrel.ViewModels/Bindables/Messages/BindableMessage.cs
index f6e242fbb..db3a46ce0 100644
--- a/src/Quarrel.ViewModels/Bindables/Messages/BindableMessage.cs
+++ b/src/Quarrel.ViewModels/Bindables/Messages/BindableMessage.cs
@@ -1,12 +1,15 @@
// Quarrel © 2022
using Discord.API.Models.Enums.Messages;
+using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Mvvm.Messaging;
using Quarrel.Bindables.Abstract;
+using Quarrel.Bindables.Channels.Interfaces;
using Quarrel.Bindables.Messages.Embeds;
using Quarrel.Bindables.Users;
using Quarrel.Client.Models.Messages;
using Quarrel.Messages.Discord.Messages;
+using Quarrel.Services.Clipboard;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
using System.Collections.Generic;
@@ -18,7 +21,10 @@ namespace Quarrel.Bindables.Messages
///
public class BindableMessage : SelectableItem
{
+ private readonly IClipboardService _clipboardService;
+
private Message _message;
+ private Message? _previousMessage;
private bool _isDeleted;
///
@@ -28,12 +34,17 @@ internal BindableMessage(
IMessenger messenger,
IDiscordService discordService,
IDispatcherService dispatcherService,
+ IClipboardService clipboardService,
+ IBindableMessageChannel channel,
Message message,
Message? previousMessage = null) :
base(messenger, discordService, dispatcherService)
{
+ _clipboardService = clipboardService;
+
_message = message;
_previousMessage = previousMessage;
+ Channel = channel;
Users = new Dictionary();
if (message.Author is not null)
@@ -61,6 +72,10 @@ internal BindableMessage(
Attachments[i] = new BindableAttachment(messenger, discordService, dispatcherService, _message.Attachments[i]);
}
+ CopyIdCommand = new RelayCommand(() => _clipboardService.Copy($"{Id}"));
+ CopyLinkCommand = new RelayCommand(() => _clipboardService.Copy($"{message.MessageUri}"));
+ DeleteCommand = new RelayCommand(() => _discordService.DeleteMessage(ChannelId, Id));
+
_messenger.Register(this, (_, e) =>
{
if (Id == e.Message.Id)
@@ -81,6 +96,12 @@ internal BindableMessage(
///
public ulong Id => Message.Id;
+ ///
+ public ulong ChannelId => Message.ChannelId;
+
+ ///
+ /// Gets the wrapped .
+ ///
public Message Message
{
get => _message;
@@ -91,27 +112,43 @@ public Message Message
}
}
- public string Content => Message.Content;
-
+ ///
+ /// Gets whether or not the message has been deleted.
+ ///
public bool IsDeleted
{
get => _isDeleted;
- set => SetProperty(ref _isDeleted, value);
+ private set => SetProperty(ref _isDeleted, value);
}
+ ///
+ /// Gets the that the message belongs to.
+ ///
+ public IBindableMessageChannel Channel { get; }
+
///
/// Gets the author of the message as a bindable user.
///
public BindableUser? Author { get; }
+ ///
+ /// Gets the author of the message as a bindable guild memeber.
+ ///
public BindableGuildMember? AuthorMember { get; }
+ ///
+ /// Gets a dictionary of bindable users relavent to
+ ///
public Dictionary Users { get; }
+ ///
+ /// Gets the message attachments.
+ ///
public BindableAttachment[] Attachments { get; }
- private Message? _previousMessage;
-
+ ///
+ /// Gets whether or not the message is a continuation.
+ ///
public bool IsContinuation => !(
_previousMessage == null ||
_message.Type is MessageType.ApplicationCommand or MessageType.ContextMenuCommand ||
@@ -126,10 +163,31 @@ _message.Type is MessageType.ApplicationCommand or MessageType.ContextMenuComman
_message.WebhookId != null && _previousMessage.Author?.Username != _message.Author?.Username ||
_message.Timestamp.ToUnixTimeMilliseconds() - _previousMessage.Timestamp.ToUnixTimeMilliseconds() > 7 * 60 * 1000)));
+ ///
+ /// Gets whether or not the user can delete the message.
+ ///
+ // TODO: Properly handle deletable condition
+ public bool CanDelete => Message.IsOwn;
+
+ ///
+ /// Gets a command that copies the message id to the clipboard.
+ ///
+ public RelayCommand CopyIdCommand { get; }
+
+ ///
+ /// Gets a command that copies the message link to the clipboard.
+ ///
+ public RelayCommand CopyLinkCommand { get; }
+
+ ///
+ /// Gets a command that deletes the message.
+ ///
+ public RelayCommand DeleteCommand { get; }
+
+ ///
protected virtual void AckUpdate()
{
OnPropertyChanged(nameof(Message));
- OnPropertyChanged(nameof(Content));
}
}
}
diff --git a/src/Quarrel.ViewModels/Bindables/Messages/Embeds/BindableAttachment.cs b/src/Quarrel.ViewModels/Bindables/Messages/Embeds/BindableAttachment.cs
index a2bdf3d27..c3000d077 100644
--- a/src/Quarrel.ViewModels/Bindables/Messages/Embeds/BindableAttachment.cs
+++ b/src/Quarrel.ViewModels/Bindables/Messages/Embeds/BindableAttachment.cs
@@ -5,11 +5,17 @@
using Quarrel.Client.Models.Messages.Embeds;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
+using System.Text.RegularExpressions;
namespace Quarrel.Bindables.Messages.Embeds
{
+ ///
+ /// A wrapper of a that can be bound to the UI.
+ ///
public class BindableAttachment : BindableItem
{
+ private const string FileExtensionRegex = @"\.([\w]+)$";
+
private Attachment _attachment;
internal BindableAttachment(
@@ -19,13 +25,28 @@ internal BindableAttachment(
Attachment attachment) :
base(messenger, discordService, dispatcherService)
{
- Attachment = attachment;
+ _attachment = attachment;
}
+ ///
+ /// Gets the wrapped .
+ ///
public Attachment Attachment
{
get => _attachment;
- set => SetProperty(ref _attachment, value);
+ private set => SetProperty(ref _attachment, value);
+ }
+
+ ///
+ /// Gets the file's extension.
+ ///
+ public string FileExtension
+ {
+ get
+ {
+ var match = Regex.Match(Attachment.Filename, FileExtensionRegex);
+ return match.Groups[1].Value;
+ }
}
}
}
diff --git a/src/Quarrel.ViewModels/Bindables/Users/BindableGuildMember.cs b/src/Quarrel.ViewModels/Bindables/Users/BindableGuildMember.cs
index 8ed853291..8cfcefe5c 100644
--- a/src/Quarrel.ViewModels/Bindables/Users/BindableGuildMember.cs
+++ b/src/Quarrel.ViewModels/Bindables/Users/BindableGuildMember.cs
@@ -8,6 +8,9 @@
namespace Quarrel.Bindables.Users
{
+ ///
+ /// A wrapper of a that can be bound to the UI.
+ ///
public class BindableGuildMember : BindableItem
{
private GuildMember _guildMember;
@@ -25,6 +28,9 @@ internal BindableGuildMember(
_guildMember = guildMember;
}
+ ///
+ /// Gets the wrapped .
+ ///
public GuildMember GuildMember
{
get => _guildMember;
diff --git a/src/Quarrel.ViewModels/Messages/Discord/Channels/ChannelUpdatedMessage.cs b/src/Quarrel.ViewModels/Messages/Discord/Channels/ChannelUpdatedMessage.cs
index ef33060ca..da2779844 100644
--- a/src/Quarrel.ViewModels/Messages/Discord/Channels/ChannelUpdatedMessage.cs
+++ b/src/Quarrel.ViewModels/Messages/Discord/Channels/ChannelUpdatedMessage.cs
@@ -4,13 +4,23 @@
namespace Quarrel.Messages.Discord.Channels
{
+ ///
+ /// A message sent when a channel is updated.
+ ///
public class ChannelUpdatedMessage
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The channel updated.
public ChannelUpdatedMessage(Channel channel)
{
Channel = channel;
}
+ ///
+ /// Gets the updated channel.
+ ///
public Channel Channel { get; }
}
}
diff --git a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageCreatedMessage.cs b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageCreatedMessage.cs
index 40465a0bb..da1a94c93 100644
--- a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageCreatedMessage.cs
+++ b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageCreatedMessage.cs
@@ -4,13 +4,23 @@
namespace Quarrel.Messages.Discord.Messages
{
+ ///
+ /// A message sent when a message is created.
+ ///
public class MessageCreatedMessage
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message created.
public MessageCreatedMessage(Message message)
{
Message = message;
}
+ ///
+ /// Gets the message created.
+ ///
public Message Message { get; }
}
}
diff --git a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageDeletedMessage.cs b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageDeletedMessage.cs
index 68074393a..b476b76fe 100644
--- a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageDeletedMessage.cs
+++ b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageDeletedMessage.cs
@@ -2,16 +2,30 @@
namespace Quarrel.Messages.Discord.Messages
{
+ ///
+ /// A message sent when a message is deleted.
+ ///
public class MessageDeletedMessage
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The channel id of deleted message.
+ /// The id of the deleted message.
public MessageDeletedMessage(ulong channelId, ulong messageId)
{
ChannelId = channelId;
MessageId = messageId;
}
+ ///
+ /// Gets the channel id of deleted message.
+ ///
public ulong ChannelId { get; }
+ ///
+ /// Gets the id of deleted message.
+ ///
public ulong MessageId { get; }
}
}
diff --git a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageUpdatedMessage.cs b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageUpdatedMessage.cs
index 89899cf6d..4404f768e 100644
--- a/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageUpdatedMessage.cs
+++ b/src/Quarrel.ViewModels/Messages/Discord/Messages/MessageUpdatedMessage.cs
@@ -4,13 +4,23 @@
namespace Quarrel.Messages.Discord.Messages
{
+ ///
+ /// A message sent when a message is updated.
+ ///
public class MessageUpdatedMessage
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message updated.
public MessageUpdatedMessage(Message message)
{
Message = message;
}
+ ///
+ /// Gets the updated message.
+ ///
public Message Message { get; }
}
}
diff --git a/src/Quarrel.ViewModels/Quarrel.ViewModels.csproj b/src/Quarrel.ViewModels/Quarrel.ViewModels.csproj
index dd9920eb9..b74dd08e3 100644
--- a/src/Quarrel.ViewModels/Quarrel.ViewModels.csproj
+++ b/src/Quarrel.ViewModels/Quarrel.ViewModels.csproj
@@ -21,6 +21,7 @@
+
diff --git a/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs b/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs
index bf4f6df1e..4ebbc6dc9 100644
--- a/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs
+++ b/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs
@@ -22,6 +22,12 @@ public enum LoggedEvent
[StringValue("Message Sent")]
MessageSent,
+ ///
+ /// The user's status was set.
+ ///
+ [StringValue("Status Set")]
+ StatusSet,
+
///
/// Opened a channel.
///
diff --git a/src/Quarrel.ViewModels/Services/Clipboard/IClipboardService.cs b/src/Quarrel.ViewModels/Services/Clipboard/IClipboardService.cs
new file mode 100644
index 000000000..52a031a37
--- /dev/null
+++ b/src/Quarrel.ViewModels/Services/Clipboard/IClipboardService.cs
@@ -0,0 +1,26 @@
+// Quarrel © 2022
+
+using System;
+
+namespace Quarrel.Services.Clipboard
+{
+ ///
+ /// An interface for a service that handles interactions with the clipboard.
+ ///
+ public interface IClipboardService
+ {
+ ///
+ /// Copies text to the clipboard.
+ ///
+ /// The text to copy.
+ /// Whether or not to flush the clipboard for persistent when the app closes.
+ void Copy(string text, bool flush = true);
+
+ ///
+ /// Copies text to the clipboard.
+ ///
+ /// The uri to copy.
+ /// Whether or not to flush the clipboard for persistent when the app closes.
+ void Copy(Uri uri, bool flush = true);
+ }
+}
diff --git a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Events.cs b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Events.cs
index 02e720fe5..dcc41e9aa 100644
--- a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Events.cs
+++ b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Events.cs
@@ -10,7 +10,7 @@ namespace Quarrel.Services.Discord
{
public partial class DiscordService
{
- public void RegisterChannelEvents()
+ private void RegisterChannelEvents()
{
_quarrelClient.MessageCreated += OnMessageCreate;
_quarrelClient.MessageUpdated += OnMessageUpdated;
diff --git a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs
index ee70ec07f..bbdc65d22 100644
--- a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs
+++ b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs
@@ -2,11 +2,11 @@
using CommunityToolkit.Diagnostics;
using Discord.API.Models.Enums.Channels;
+using Discord.API.Models.Enums.Users;
using Quarrel.Bindables.Channels;
using Quarrel.Bindables.Channels.Abstract;
using Quarrel.Bindables.Channels.Interfaces;
using Quarrel.Bindables.Guilds;
-using Quarrel.Bindables.Messages;
using Quarrel.Bindables.Users;
using Quarrel.Client.Models.Channels;
using Quarrel.Client.Models.Channels.Interfaces;
@@ -179,11 +179,23 @@ public async Task GetChannelMessagesAsync(IBindableMessageChannel cha
}
///
- public void SendMessage(ulong channelId, string content)
+ public async Task SendMessage(ulong channelId, string content)
{
_analyticsService.Log(LoggedEvent.MessageSent);
- _quarrelClient.SendMessage(channelId, content);
+ await _quarrelClient.SendMessage(channelId, content);
+ }
+
+ ///
+ public async Task DeleteMessage(ulong channelId, ulong messageId)
+ {
+ await _quarrelClient.DeleteMessage(channelId, messageId);
+ }
+
+ ///
+ public async Task SetStatus(UserStatus status)
+ {
+ await _quarrelClient.UpdateStatus(status);
}
}
}
diff --git a/src/Quarrel.ViewModels/Services/Discord/IDiscordService.cs b/src/Quarrel.ViewModels/Services/Discord/IDiscordService.cs
index d9412068b..1b715eb05 100644
--- a/src/Quarrel.ViewModels/Services/Discord/IDiscordService.cs
+++ b/src/Quarrel.ViewModels/Services/Discord/IDiscordService.cs
@@ -1,5 +1,6 @@
// Quarrel © 2022
+using Discord.API.Models.Enums.Users;
using Quarrel.Bindables.Channels.Abstract;
using Quarrel.Bindables.Channels.Interfaces;
using Quarrel.Bindables.Guilds;
@@ -58,13 +59,13 @@ public interface IDiscordService
/// The if of the last message to load messages before, or null.
/// An array of s from the channel.
Task GetChannelMessagesAsync(IBindableMessageChannel channel, ulong? beforeId = null);
-
+
///
/// Gets the channels in a guild.
///
/// The guild to get the channels for.
/// The selected channel as an .
- /// An array of s from the guild.
+ /// An array of s from the guild.
BindableGuildChannel?[] GetGuildChannels(BindableGuild guild, out IBindableSelectableChannel? selectedChannel);
///
@@ -72,11 +73,29 @@ public interface IDiscordService
///
/// The .
/// The selected channel as an .
- /// An array of s.
+ /// An array of s.
BindablePrivateChannel?[] GetPrivateChannels(BindableHomeItem home, out IBindableSelectableChannel? selectedChannel);
- BindableGuildMember GetGuildMember(ulong userId, ulong guildId);
+ BindableGuildMember? GetGuildMember(ulong userId, ulong guildId);
+
+ ///
+ /// Sends a message.
+ ///
+ /// The id of the channel to send the message in.
+ /// The content of the message.
+ Task SendMessage(ulong channelId, string content);
+
+ ///
+ /// Deletes a message.
+ ///
+ /// The id of the channel the message is in.
+ /// The id of the message to delete.
+ Task DeleteMessage(ulong channelId, ulong messageId);
- void SendMessage(ulong channelId, string content);
+ ///
+ /// Updates the user's online status.
+ ///
+ /// The new online status to set.
+ Task SetStatus(UserStatus status);
}
}
diff --git a/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs b/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs
index 7af675c46..51b9d710c 100644
--- a/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs
+++ b/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs
@@ -29,6 +29,9 @@ public interface ILocalizationService
///
string CommaList(params string[] args);
+ ///
+ /// Gets or sets the app's language override.
+ ///
public string LanguageOverride { get; set; }
///
diff --git a/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs b/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs
index fafb55235..d4ffbead7 100644
--- a/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs
@@ -1,14 +1,18 @@
// Quarrel © 2022
+using Discord.API.Models.Enums.Users;
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Mvvm.Messaging;
using Quarrel.Bindables.Users;
using Quarrel.Messages;
using Quarrel.Messages.Navigation.SubPages;
+using Quarrel.Services.Analytics;
+using Quarrel.Services.Analytics.Enums;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
using Quarrel.ViewModels.SubPages.Settings;
+using System;
namespace Quarrel.ViewModels
{
@@ -17,22 +21,26 @@ namespace Quarrel.ViewModels
///
public partial class CurrentUserViewModel : ObservableRecipient
{
+ private readonly IAnalyticsService _analyticsService;
private readonly IMessenger _messenger;
private readonly IDiscordService _discordService;
private readonly IDispatcherService _dispatcherService;
- [ObservableProperty]
private BindableSelfUser? _me;
///
/// Initializes a new instance of the class.
///
- public CurrentUserViewModel(IMessenger messenger, IDiscordService discordService, IDispatcherService dispatcherService)
+ public CurrentUserViewModel(IAnalyticsService analyticsService, IMessenger messenger, IDiscordService discordService, IDispatcherService dispatcherService)
{
+ _analyticsService = analyticsService;
_messenger = messenger;
_discordService = discordService;
_dispatcherService = dispatcherService;
+ NavigateToSettingsCommand = new RelayCommand(NavigateToSettings);
+ SetStatusCommand = new RelayCommand(SetStatus);
+
_messenger.Register(this, (_, _) =>
{
_dispatcherService.RunOnUIThread(() =>
@@ -43,12 +51,35 @@ public CurrentUserViewModel(IMessenger messenger, IDiscordService discordService
}
///
- /// Sends a request to open the settings subpage.
+ /// Gets the bindable current self user.
+ ///
+ public BindableSelfUser? Me
+ {
+ get => _me;
+ set => SetProperty(ref _me, value);
+ }
+
+ ///
+ /// Gets a command that requests navigation to settings.
+ ///
+ public RelayCommand NavigateToSettingsCommand { get; }
+
+ ///
+ /// Gets a command that sets the current user's status.
///
- [ICommand]
- public void NavigateToSettings()
+ public RelayCommand SetStatusCommand { get; }
+
+ private void NavigateToSettings()
{
_messenger.Send(new NavigateToSubPageMessage(typeof(UserSettingsPageViewModel)));
}
+
+ private void SetStatus(UserStatus status)
+ {
+ _analyticsService.Log(LoggedEvent.StatusSet,
+ ("Status", status.GetStringValue()));
+
+ _discordService.SetStatus(status);
+ }
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs
index 8e59ff9d5..805cde1fd 100644
--- a/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs
@@ -9,6 +9,9 @@
namespace Quarrel.ViewModels.Panels
{
+ ///
+ /// The view model for the command bar.
+ ///
public class CommandBarViewModel : ObservableRecipient
{
private readonly IAnalyticsService _analyticsService;
@@ -17,6 +20,9 @@ public class CommandBarViewModel : ObservableRecipient
private IBindableSelectableChannel? _selectedChannel;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public CommandBarViewModel(IAnalyticsService analyticsService, IMessenger messenger, IDiscordService discordService)
{
_analyticsService = analyticsService;
@@ -26,10 +32,13 @@ public CommandBarViewModel(IAnalyticsService analyticsService, IMessenger messen
_messenger.Register>(this, (_, m) => SelectedChannel = m.Channel);
}
+ ///
+ /// Gets the selected channel.
+ ///
public IBindableSelectableChannel? SelectedChannel
{
get => _selectedChannel;
- set => SetProperty(ref _selectedChannel, value);
+ private set => SetProperty(ref _selectedChannel, value);
}
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs
index 0fa0b2b93..12a762270 100644
--- a/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs
@@ -10,6 +10,9 @@
namespace Quarrel.ViewModels.Panels
{
+ ///
+ /// The view model for the message box.
+ ///
public class MessageBoxViewModel : ObservableRecipient
{
private readonly IMessenger _messenger;
@@ -17,8 +20,11 @@ public class MessageBoxViewModel : ObservableRecipient
private readonly IDispatcherService _dispatcherService;
private ulong? _channelId;
- private string _draftText;
+ private string? _draftText;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public MessageBoxViewModel(IMessenger messenger, IDiscordService discordService, IDispatcherService dispatcherService)
{
_messenger = messenger;
@@ -33,25 +39,34 @@ public MessageBoxViewModel(IMessenger messenger, IDiscordService discordService,
});
}
+ ///
+ /// Gets a command that sends the drafted message.
+ ///
public RelayCommand SendMessageCommand { get; }
- public string DraftText
+ ///
+ /// Gets or sets the drafted text in the message box.
+ ///
+ public string? DraftText
{
get => _draftText;
set => SetProperty(ref _draftText, value);
}
+ ///
+ /// Gets the current channel id.
+ ///
public ulong? ChannelId
{
get => _channelId;
- set => SetProperty(ref _channelId, value);
+ private set => SetProperty(ref _channelId, value);
}
- public void SendMessage()
+ private void SendMessage()
{
- if (ChannelId.HasValue)
+ if (ChannelId.HasValue && !string.IsNullOrEmpty(DraftText))
{
- _discordService.SendMessage(ChannelId.Value, DraftText);
+ _discordService.SendMessage(ChannelId.Value, DraftText!);
DraftText = string.Empty;
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs
index 0c2e582c3..a674220a2 100644
--- a/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs
@@ -8,6 +8,7 @@
using Quarrel.Client.Models.Messages;
using Quarrel.Messages.Discord.Messages;
using Quarrel.Messages.Navigation;
+using Quarrel.Services.Clipboard;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
using System.Collections.ObjectModel;
@@ -23,6 +24,7 @@ public partial class MessagesViewModel : ObservableRecipient
private readonly IMessenger _messenger;
private readonly IDiscordService _discordService;
private readonly IDispatcherService _dispatcherService;
+ private readonly IClipboardService _clipboardService;
private bool _isLoading;
private SemaphoreSlim _semaphore;
@@ -31,11 +33,16 @@ public partial class MessagesViewModel : ObservableRecipient
///
/// Initializes a new instance of the class.
///
- public MessagesViewModel(IMessenger messenger, IDiscordService discordService, IDispatcherService dispatcherService)
+ public MessagesViewModel(
+ IMessenger messenger,
+ IDiscordService discordService,
+ IDispatcherService dispatcherService,
+ IClipboardService clipboardService)
{
_messenger = messenger;
_discordService = discordService;
_dispatcherService = dispatcherService;
+ _clipboardService = clipboardService;
_semaphore = new SemaphoreSlim(1,1);
Source = new ObservableRangeCollection();
@@ -54,6 +61,9 @@ public MessagesViewModel(IMessenger messenger, IDiscordService discordService, I
///
public ObservableRangeCollection Source;
+ ///
+ /// Gets the currently selected channel.
+ ///
public IBindableSelectableChannel? SelectedChannel
{
get => _selectedChannel;
@@ -145,13 +155,16 @@ private void LoadChannel()
private BindableMessage[] ParseMessages(Message[] messages)
{
+ IBindableMessageChannel? channel = SelectedChannel as IBindableMessageChannel;
+ Guard.IsNotNull(channel, nameof(channel));
+
BindableMessage[] bindableMessages = new BindableMessage[messages.Length];
if (bindableMessages.Length == 0) return bindableMessages;
- bindableMessages[0] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1]);
+ bindableMessages[0] = new BindableMessage(_messenger, _discordService, _dispatcherService, _clipboardService, channel, messages[messages.Length - 1]);
for (int i = 1; i < messages.Length; i++)
{
- bindableMessages[i] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1 - i], messages[messages.Length - i]);
+ bindableMessages[i] = new BindableMessage(_messenger, _discordService, _dispatcherService, _clipboardService, channel, messages[messages.Length - 1 - i], messages[messages.Length - i]);
}
return bindableMessages;
}
@@ -161,7 +174,9 @@ private BindableMessage[] ParseMessages(Message[] messages)
///
private void AppendMessage(Message message)
{
- Source.Add(new BindableMessage(_messenger, _discordService, _dispatcherService, message, Source.Count > 0 ? Source[Source.Count - 1].Message : null));
+ IBindableMessageChannel? channel = SelectedChannel as IBindableMessageChannel;
+ Guard.IsNotNull(channel, nameof(channel));
+ Source.Add(new BindableMessage(_messenger, _discordService, _dispatcherService, _clipboardService, channel, message, Source.Count > 0 ? Source[Source.Count - 1].Message : null));
}
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/DiscordStatus/DiscordStatusViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/DiscordStatus/DiscordStatusViewModel.cs
index 44f768dc2..614bfe49e 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/DiscordStatus/DiscordStatusViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/DiscordStatus/DiscordStatusViewModel.cs
@@ -205,7 +205,9 @@ public async void ShowMetrics(string duration)
}
catch (Exception ex)
{
- _analyticsService.Log(LoggedEvent.DiscordStatusRequestFailed, ("Endpoint", "GetMetrics"), ("Exception", ex.Message));
+ _analyticsService.Log(LoggedEvent.DiscordStatusRequestFailed,
+ ("Endpoint", "GetMetrics"),
+ ("Exception", ex.Message));
}
if (metrics != null && metrics.Metrics != null && metrics.Metrics.Length > 0)
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/Meta/AboutPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/Meta/AboutPageViewModel.cs
index ca3f730bc..3fe72423b 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/Meta/AboutPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/Meta/AboutPageViewModel.cs
@@ -17,6 +17,7 @@ public partial class AboutPageViewModel : ObservableRecipient
{
private const string CommitResource = "About/Commit";
private const string BranchResource = "About/Branch";
+ private const string VersionResource = "About/Version";
private const string QuarrelDevResource = "About/QuarrelDev";
private const string QuarrelAlphaResource = "About/QuarrelAlpha";
private const string QuarrelInsiderResource = "About/QuarrelInsider";
@@ -42,10 +43,11 @@ public AboutPageViewModel(
///
/// Gets the app version as a .
///
- public string AppVersion => string.Format("{0}.{1}.{2}",
+ public string AppVersion =>
+ _localizationService[VersionResource,
_versioningService.AppVersion.MajorVersion,
_versioningService.AppVersion.MinorVersion,
- _versioningService.AppVersion.BuildNumber);
+ _versioningService.AppVersion.BuildNumber];
///
/// Gets the app's version type.
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/IUserSettingsMenuItem.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/IUserSettingsMenuItem.cs
new file mode 100644
index 000000000..87351f72a
--- /dev/null
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/IUserSettingsMenuItem.cs
@@ -0,0 +1,15 @@
+// Quarrel © 2022
+
+namespace Quarrel.ViewModels.SubPages.UserSettings
+{
+ ///
+ /// An interface for items in the
+ ///
+ public interface IUserSettingsMenuItem
+ {
+ ///
+ /// Gets the title of the menu item.
+ ///
+ string Title { get; }
+ }
+}
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs
index b7f364810..c91a0a105 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs
@@ -6,21 +6,40 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract
{
- public abstract class UserSettingsSubPageViewModel : ObservableObject
+ ///
+ /// A base class for user settings sub-page view models.
+ ///
+ public abstract class UserSettingsSubPageViewModel : ObservableObject, IUserSettingsMenuItem
{
+ ///
+ /// The localization service.
+ ///
protected readonly ILocalizationService _localizationService;
+
+ ///
+ /// The storage service.
+ ///
protected readonly IStorageService _storageService;
- public UserSettingsSubPageViewModel(ILocalizationService localizationService, IStorageService storageService)
+ internal UserSettingsSubPageViewModel(ILocalizationService localizationService, IStorageService storageService)
{
_localizationService = localizationService;
_storageService = storageService;
}
+ ///
+ /// Gets the string used as a glyph for the sub page.
+ ///
public abstract string Glyph { get; }
+ ///
+ /// Gets the title of the sub page.
+ ///
public abstract string Title { get; }
+ ///
+ /// Gets whether or not the page is currently active.
+ ///
public virtual bool IsActive => false;
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs
index d19c1a274..4d0977367 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs
@@ -6,6 +6,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the behaviors page in settings.
+ ///
public class BehaviorPageViewModel : UserSettingsSubPageViewModel
{
private const string BehaviorResource = "UserSettings/Behavior";
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs
index 17578450e..fabd5ed22 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs
@@ -6,6 +6,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the connections page in settings.
+ ///
public class ConnectionsPageViewModel : UserSettingsSubPageViewModel
{
private const string ConnectionsResource = "UserSettings/Connections";
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs
index ff0ffb7df..49b6f2df8 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs
@@ -7,6 +7,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the display page in settings.
+ ///
public class DisplayPageViewModel : UserSettingsSubPageViewModel
{
private const string ConnectionsResource = "UserSettings/Display";
@@ -25,12 +28,18 @@ internal DisplayPageViewModel(ILocalizationService localizationService, IStorage
///
public override bool IsActive => true;
+ ///
+ /// Gets the app selected language.
+ ///
public string SelectedLanguage
{
get => _localizationService.LanguageOverride;
set => _localizationService.LanguageOverride = value;
}
+ ///
+ /// Gets the list of languages available for the app.
+ ///
public IReadOnlyList LanguageOptions => _localizationService.AvailableLanguages;
}
}
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs
index 71b7c8c1c..3d2ef524c 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs
@@ -8,24 +8,31 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the account page in settings.
+ ///
public class MyAccountPageViewModel : UserSettingsSubPageViewModel
{
private const string MyAccountResource = "UserSettings/MyAccount";
private readonly IDiscordService _discordService;
+ private bool _isLoggedIn;
private string? _email;
- private string _username;
+ private string? _username;
private int _discriminator;
private string? _aboutMe;
internal MyAccountPageViewModel(ILocalizationService localizationService, IStorageService storageService, IDiscordService discordService) :
base(localizationService, storageService)
{
+ _isLoggedIn = false;
_discordService = discordService;
var user = _discordService.GetMe();
+
if (user is not null)
{
+ _isLoggedIn = true;
SetBaseValues(user);
}
}
@@ -36,26 +43,39 @@ internal MyAccountPageViewModel(ILocalizationService localizationService, IStora
///
public override string Title => _localizationService[MyAccountResource];
- public override bool IsActive => true;
+ ///
+ public override bool IsActive => _isLoggedIn;
+ ///
+ /// Gets or sets the current user's email.
+ ///
public string? Email
{
get => _email;
set => SetProperty(ref _email, value);
}
- public string Username
+ ///
+ /// Gets or sets the current user's username.
+ ///
+ public string? Username
{
get => _username;
set => SetProperty(ref _username, value);
}
+ ///
+ /// Gets or sets the current user's discriminator.
+ ///
public int Discriminator
{
get => _discriminator;
set => SetProperty(ref _discriminator, value);
}
+ ///
+ /// Gets or sets the current user's about me description.
+ ///
public string? AboutMe
{
get => _aboutMe;
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs
index 4cb13c4e0..0425289a7 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs
@@ -6,6 +6,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the notifications page in settings.
+ ///
public class NotificationsPageViewModel : UserSettingsSubPageViewModel
{
private const string NotificationsResource = "UserSettings/Notifications";
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs
index bc592a857..772581363 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs
@@ -8,6 +8,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the privacy page in settings.
+ ///
public class PrivacyPageViewModel : UserSettingsSubPageViewModel
{
private const string PrivacyResource = "UserSettings/Privacy";
@@ -19,11 +22,14 @@ internal PrivacyPageViewModel(ILocalizationService localizationService, IStorage
{
_discordService = discordService;
}
-
+
+ ///
public override string Glyph => "";
+ ///
public override string Title => _localizationService[PrivacyResource];
+ ///
public override bool IsActive => true;
private ExplicitContentFilterLevel ContentFilterLevel
@@ -40,9 +46,15 @@ private ExplicitContentFilterLevel ContentFilterLevel
}
}
+ ///
+ /// Gets or sets if the content filter level is all.
+ ///
+ ///
+ /// Can only be set to , clearing public and none.
+ ///
public bool FilterAll
{
- get => _contentFilterLevel == ExplicitContentFilterLevel.All;
+ get => ContentFilterLevel == ExplicitContentFilterLevel.All;
set
{
if (!value) return;
@@ -50,9 +62,15 @@ public bool FilterAll
}
}
+ ///
+ /// Gets or sets if the content filter level is public.
+ ///
+ ///
+ /// Can only be set to , clearing all and none.
+ ///
public bool FilterPublic
{
- get => _contentFilterLevel == ExplicitContentFilterLevel.Public;
+ get => ContentFilterLevel == ExplicitContentFilterLevel.Public;
set
{
if (!value) return;
@@ -60,9 +78,15 @@ public bool FilterPublic
}
}
+ ///
+ /// Gets or sets if the content filter level is none.
+ ///
+ ///
+ /// Can only be set to , clearing all and public.
+ ///
public bool FilterNone
{
- get => _contentFilterLevel == ExplicitContentFilterLevel.None;
+ get => ContentFilterLevel == ExplicitContentFilterLevel.None;
set
{
if (!value) return;
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs
index cc742acd7..2f0ae4abf 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs
@@ -6,6 +6,9 @@
namespace Quarrel.ViewModels.SubPages.UserSettings.Pages
{
+ ///
+ /// A view model for the voice page in settings.
+ ///
public class VoicePageViewModel : UserSettingsSubPageViewModel
{
private const string VoiceResource = "UserSettings/Voice";
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsHeader.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsHeader.cs
new file mode 100644
index 000000000..c6e9ec2f3
--- /dev/null
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsHeader.cs
@@ -0,0 +1,21 @@
+// Quarrel © 2022
+
+using Microsoft.Toolkit.Mvvm.ComponentModel;
+using Quarrel.Services.Localization;
+
+namespace Quarrel.ViewModels.SubPages.UserSettings
+{
+ ///
+ /// A header for a category of user settings menu items.
+ ///
+ public class UserSettingsHeader : ObservableObject, IUserSettingsMenuItem
+ {
+ internal UserSettingsHeader(ILocalizationService localizationService, string resource)
+ {
+ Title = localizationService[resource];
+ }
+
+ ///
+ public string Title { get; }
+ }
+}
diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs
index 9ad3b4dd2..a9f07d1d5 100644
--- a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs
+++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs
@@ -4,40 +4,59 @@
using Quarrel.Services.Discord;
using Quarrel.Services.Localization;
using Quarrel.Services.Storage;
+using Quarrel.ViewModels.SubPages.UserSettings;
using Quarrel.ViewModels.SubPages.UserSettings.Pages;
using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract;
using System.Collections.ObjectModel;
namespace Quarrel.ViewModels.SubPages.Settings
{
+ ///
+ /// A view model for the user settings page.
+ ///
public class UserSettingsPageViewModel : ObservableObject
{
- private readonly ILocalizationService _localizationService;
+ private const string AccountSettingsResource = "UserSettings/AccountSettings";
+ private const string AppSettingsResource = "UserSettings/AppSettings";
- private UserSettingsSubPageViewModel _selectedSubPage;
+ private readonly ILocalizationService _localizationService;
+ private UserSettingsSubPageViewModel? _selectedSubPage;
+ ///
+ /// Initializes a new instance of the .
+ ///
public UserSettingsPageViewModel(ILocalizationService localizationService, IStorageService storageService, IDiscordService discordService)
{
_localizationService = localizationService;
- Pages = new ObservableCollection();
+ Pages = new ObservableCollection();
- Pages.Add(new MyAccountPageViewModel(localizationService, storageService, discordService));
+ // Account settings
+ Pages.Add(new UserSettingsHeader(localizationService, AccountSettingsResource));
+ Pages.Add(new MyAccountPageViewModel(_localizationService, storageService, discordService));
Pages.Add(new PrivacyPageViewModel(_localizationService, storageService, discordService));
Pages.Add(new ConnectionsPageViewModel(_localizationService, storageService));
+ // App Settings
+ Pages.Add(new UserSettingsHeader(localizationService, AppSettingsResource));
Pages.Add(new DisplayPageViewModel(_localizationService, storageService));
Pages.Add(new BehaviorPageViewModel(_localizationService, storageService));
Pages.Add(new NotificationsPageViewModel(_localizationService, storageService));
Pages.Add(new VoicePageViewModel(_localizationService, storageService));
}
- public UserSettingsSubPageViewModel SelectedSubPage
+ ///
+ /// Gets the view model of the selected sub page.
+ ///
+ public UserSettingsSubPageViewModel? SelectedSubPage
{
get => _selectedSubPage;
set => SetProperty(ref _selectedSubPage, value);
}
- public ObservableCollection Pages { get; }
+ ///
+ /// Gets the view models of all subpage options.
+ ///
+ public ObservableCollection Pages { get; }
}
}
diff --git a/src/Quarrel/App.Services.cs b/src/Quarrel/App.Services.cs
index 3faed2625..910f18a78 100644
--- a/src/Quarrel/App.Services.cs
+++ b/src/Quarrel/App.Services.cs
@@ -6,6 +6,7 @@
using Quarrel.Services.Analytics;
using Quarrel.Services.APIs.GitHubService;
using Quarrel.Services.AppConnections;
+using Quarrel.Services.Clipboard;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
using Quarrel.Services.Localization;
@@ -39,6 +40,7 @@ private IServiceProvider ConfigureServices()
services.AddSingleton();
services.AddSingleton();
services.AddSingleton(new StorageService(appDataFolder, JsonAsyncSerializer.Singleton));
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
diff --git a/src/Quarrel/Controls/Panels/Channels/CurrentUserButton.xaml b/src/Quarrel/Controls/Panels/Channels/CurrentUserButton.xaml
index add0e0d10..3790202ec 100644
--- a/src/Quarrel/Controls/Panels/Channels/CurrentUserButton.xaml
+++ b/src/Quarrel/Controls/Panels/Channels/CurrentUserButton.xaml
@@ -5,6 +5,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:qc="using:Quarrel.Controls"
+ xmlns:bconvert="using:Quarrel.Converters.Common.Boolean"
+ xmlns:uenum="using:Discord.API.Models.Enums.Users"
mc:Ignorable="d"
d:DesignHeight="64"
d:DesignWidth="224">
@@ -25,25 +27,25 @@
+ ToolTipService.ToolTip="Online" GroupName="Status" Tag="Online"
+ IsChecked="{x:Bind bconvert:EqualityConverter.Convert(ViewModel.Me.User.Presence.Status, uenum:UserStatus.Online), Mode=OneWay}"
+ Command="{x:Bind ViewModel.SetStatusCommand}" CommandParameter="{x:Bind uenum:UserStatus.Online}"
+ Foreground="{StaticResource OnlineBrush}" Style="{StaticResource QuarrelRadioButton}"/>
+ ToolTipService.ToolTip="Idle" GroupName="Status" Tag="Idle"
+ IsChecked="{x:Bind bconvert:EqualityConverter.Convert(ViewModel.Me.User.Presence.Status, uenum:UserStatus.Idle), Mode=OneWay}"
+ Command="{x:Bind ViewModel.SetStatusCommand}" CommandParameter="{x:Bind uenum:UserStatus.Idle}"
+ Foreground="{StaticResource IdleBrush}" Style="{StaticResource QuarrelRadioButton}"/>
+ ToolTipService.ToolTip="Do Not Disturb" GroupName="Status" Tag="Dnd"
+ IsChecked="{x:Bind bconvert:EqualityConverter.Convert(ViewModel.Me.User.Presence.Status, uenum:UserStatus.DoNotDisturb), Mode=OneWay}"
+ Command="{x:Bind ViewModel.SetStatusCommand}" CommandParameter="{x:Bind uenum:UserStatus.DoNotDisturb}"
+ Foreground="{StaticResource DndBrush}" Style="{StaticResource QuarrelRadioButton}"/>
+ ToolTipService.ToolTip="Invisible" GroupName="Status" Tag="Invisible"
+ IsChecked="{x:Bind bconvert:EqualityConverter.Convert(ViewModel.Me.User.Presence.Status, uenum:UserStatus.Invisible), Mode=OneWay}"
+ Command="{x:Bind ViewModel.SetStatusCommand}" CommandParameter="{x:Bind uenum:UserStatus.Invisible}"
+ Foreground="{StaticResource OfflineBrush}" Style="{StaticResource QuarrelRadioButton}"/>
diff --git a/src/Quarrel/Controls/Panels/Guilds/GuildPanel.xaml.cs b/src/Quarrel/Controls/Panels/Guilds/GuildPanel.xaml.cs
index 1a96d8d27..e5d8fdcdd 100644
--- a/src/Quarrel/Controls/Panels/Guilds/GuildPanel.xaml.cs
+++ b/src/Quarrel/Controls/Panels/Guilds/GuildPanel.xaml.cs
@@ -19,7 +19,6 @@ public GuildPanel()
private void GuildList_OnItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
{
- var container = (TreeViewItem)sender.ContainerFromItem(args.InvokedItem);
if (args.InvokedItem is IBindableSelectableGuildItem guild)
{
ViewModel.SelectedGuild = guild;
diff --git a/src/Quarrel/Converters/Common/Boolean/IsNotNullConverter.cs b/src/Quarrel/Converters/Common/Boolean/IsNotNullConverter.cs
new file mode 100644
index 000000000..2c81a06fe
--- /dev/null
+++ b/src/Quarrel/Converters/Common/Boolean/IsNotNullConverter.cs
@@ -0,0 +1,9 @@
+// Quarrel © 2022
+
+namespace Quarrel.Converters.Common.Boolean
+{
+ public class IsNotNullConverter
+ {
+ public static bool Convert(object? item1) => item1 is not null;
+ }
+}
diff --git a/src/Quarrel/Converters/Common/Boolean/IsNullConverter.cs b/src/Quarrel/Converters/Common/Boolean/IsNullConverter.cs
new file mode 100644
index 000000000..37f44e175
--- /dev/null
+++ b/src/Quarrel/Converters/Common/Boolean/IsNullConverter.cs
@@ -0,0 +1,9 @@
+// Quarrel © 2022
+
+namespace Quarrel.Converters.Common.Boolean
+{
+ public class IsNullConverter
+ {
+ public static bool Convert(object? item1) => item1 is null;
+ }
+}
diff --git a/src/Quarrel/Converters/Common/Text/CaseConverter.cs b/src/Quarrel/Converters/Common/Text/CaseConverter.cs
index 7e1193f2a..affbf2eb5 100644
--- a/src/Quarrel/Converters/Common/Text/CaseConverter.cs
+++ b/src/Quarrel/Converters/Common/Text/CaseConverter.cs
@@ -16,7 +16,6 @@ public class CaseConverter
///
public CharacterCasing Case { get; set; }
-
///
/// Initializes a new instance of the class.
///
diff --git a/src/Quarrel/Converters/Common/Time/SmartTimeFormatConverter.cs b/src/Quarrel/Converters/Common/Time/SmartDateTimeFormatConverter.cs
similarity index 83%
rename from src/Quarrel/Converters/Common/Time/SmartTimeFormatConverter.cs
rename to src/Quarrel/Converters/Common/Time/SmartDateTimeFormatConverter.cs
index 60a1ad182..39d293d0f 100644
--- a/src/Quarrel/Converters/Common/Time/SmartTimeFormatConverter.cs
+++ b/src/Quarrel/Converters/Common/Time/SmartDateTimeFormatConverter.cs
@@ -6,14 +6,14 @@
namespace Quarrel.Converters.Common.Time
{
- public class SmartTimeFormatConverter
+ public class SmartDateTimeFormatConverter
{
- public static string Convert(DateTimeOffset time)
+ public static string Convert(DateTimeOffset dateTime)
{
ILocalizationService localizationService = App.Current.Services.GetRequiredService();
string resource;
DateTimeOffset now = DateTimeOffset.Now;
- var timeDiff = now - time;
+ var timeDiff = now - dateTime;
// Minutes
if (timeDiff.TotalMinutes < 1)
@@ -57,20 +57,20 @@ public static string Convert(DateTimeOffset time)
//}
// Day + Time
- string shortTime = time.ToString("t");
- if (time.DayOfYear == now.DayOfYear && time.Year == now.Year)
+ string shortTime = dateTime.ToString("t");
+ if (dateTime.DayOfYear == now.DayOfYear && dateTime.Year == now.Year)
{
resource = "Time/TodayAt";
return localizationService[resource, shortTime];
}
DateTime yesterday = (DateTime.Today - TimeSpan.FromDays(1)).Date;
- if (time.DayOfYear == yesterday.DayOfYear && time.Year == yesterday.Year)
+ if (dateTime.DayOfYear == yesterday.DayOfYear && dateTime.Year == yesterday.Year)
{
resource = "Time/YesterdayAt";
return localizationService[resource, shortTime];
}
- return time.ToString("d");
+ return dateTime.ToString("d");
}
}
}
diff --git a/src/Quarrel/Converters/Common/Time/TimeFormatConverter.cs b/src/Quarrel/Converters/Common/Time/TimeFormatConverter.cs
new file mode 100644
index 000000000..fc12de4ad
--- /dev/null
+++ b/src/Quarrel/Converters/Common/Time/TimeFormatConverter.cs
@@ -0,0 +1,14 @@
+// Quarrel © 2022
+
+using System;
+
+namespace Quarrel.Converters.Common.Time
+{
+ public class TimeFormatConverter
+ {
+ public static string Convert(DateTimeOffset dateTime)
+ {
+ return dateTime.ToString("t");
+ }
+ }
+}
diff --git a/src/Quarrel/Converters/Discord/Messages/Attachments/AttachmentIconConverter.cs b/src/Quarrel/Converters/Discord/Messages/Attachments/AttachmentIconConverter.cs
new file mode 100644
index 000000000..88b822140
--- /dev/null
+++ b/src/Quarrel/Converters/Discord/Messages/Attachments/AttachmentIconConverter.cs
@@ -0,0 +1,64 @@
+// Quarrel © 2022
+
+namespace Quarrel.Converters.Discord.Messages.Attachments
+{
+ public class AttachmentIconConverter
+ {
+ public static string Convert(string filetype)
+ {
+ // TODO: Replace with dictionary from file
+ return filetype switch
+ {
+ // Text file icon
+ "txt" => "",
+
+ // Zipped file(s) icon
+ "7z" or
+ "gz" or
+ "zip" => "",
+
+ // Image icon
+ "png" or
+ "jpg" or
+ "jpeg" or
+ "bmp" or
+ "gif" => "",
+
+ // Audio icon
+ "mp3" or
+ "wav" => "",
+
+ // Video icon
+ "mp4" or
+ "mov" => "",
+
+ // 3D icon
+ "obj" or
+ "blend" => "",
+
+ // Console icon
+ "ps1" or
+ "batch" => "",
+
+ // App Package icon
+ "appx" or
+ "appxbundle" or
+ "msix" or
+ "msixbundle" or
+ "appinstaller" => "",
+
+ // Certificate icon
+ "cer" => "",
+
+ // Code icon
+ "json" => "",
+
+ // Disc icon
+ "iso" => "",
+
+ // File icon
+ _ => "",
+ };
+ }
+ }
+}
diff --git a/src/Quarrel/Converters/Discord/Messages/Attachments/HumanizeByteSizeConverter.cs b/src/Quarrel/Converters/Discord/Messages/Attachments/HumanizeByteSizeConverter.cs
new file mode 100644
index 000000000..db502deff
--- /dev/null
+++ b/src/Quarrel/Converters/Discord/Messages/Attachments/HumanizeByteSizeConverter.cs
@@ -0,0 +1,16 @@
+// Quarrel © 2022
+
+using Humanizer;
+using Humanizer.Bytes;
+
+namespace Quarrel.Converters.Discord.Messages.Attachments
+{
+ public class HumanizeByteSizeConverter
+ {
+ public static string Convert(ulong bytes)
+ {
+ var byteSize = new ByteSize(bytes);
+ return byteSize.Humanize("0.00");
+ }
+ }
+}
diff --git a/src/Quarrel/DataTemplates/ChannelTemplates.xaml b/src/Quarrel/DataTemplates/ChannelTemplates.xaml
index 317ca5314..1491c0fac 100644
--- a/src/Quarrel/DataTemplates/ChannelTemplates.xaml
+++ b/src/Quarrel/DataTemplates/ChannelTemplates.xaml
@@ -3,8 +3,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls"
+ xmlns:vconvert="using:Quarrel.Converters.Common.Visible"
xmlns:cconvert="using:Quarrel.Converters.Discord.Channels"
xmlns:a="using:Quarrel.Attached"
+ xmlns:cenums="using:Discord.API.Models.Enums.Channels"
xmlns:bindablechannels="using:Quarrel.Bindables.Channels">
@@ -20,7 +22,14 @@
-
+
+
+
+
diff --git a/src/Quarrel/DataTemplates/MessageTemplates.xaml b/src/Quarrel/DataTemplates/MessageTemplates.xaml
index ff6398e6b..7fbdcc275 100644
--- a/src/Quarrel/DataTemplates/MessageTemplates.xaml
+++ b/src/Quarrel/DataTemplates/MessageTemplates.xaml
@@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tc="using:Microsoft.Toolkit.Uwp.UI.Controls"
+ xmlns:aconvert="using:Quarrel.Converters.Discord.Messages.Attachments"
xmlns:bconvert="using:Quarrel.Converters.Common.Boolean"
xmlns:tconvert="using:Quarrel.Converters.Common.Time"
xmlns:mconvert="using:Quarrel.Converters.Discord.Messages"
@@ -10,8 +11,54 @@
xmlns:mcontrols="using:Quarrel.Controls.Panels.Messages"
xmlns:bindablemessages="using:Quarrel.Bindables.Messages"
xmlns:bindableembeds="using:Quarrel.Bindables.Messages.Embeds"
+ xmlns:mselector="using:Quarrel.Selectors.Messages"
xmlns:markdown="using:Quarrel.Markdown">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -69,22 +132,17 @@
-
-
-
-
-
-
-
-
-
+
@@ -99,14 +157,14 @@
-
+
-
diff --git a/src/Quarrel/MultilingualResources/Quarrel.he-IL.xlf b/src/Quarrel/MultilingualResources/Quarrel.he-IL.xlf
index 6d38a4bb3..685872396 100644
--- a/src/Quarrel/MultilingualResources/Quarrel.he-IL.xlf
+++ b/src/Quarrel/MultilingualResources/Quarrel.he-IL.xlf
@@ -375,6 +375,30 @@
Failure
כישלון
+
+ Copy ID
+ העתק מזהה
+
+
+ Copy Link
+ העתק קישור
+
+
+ Delete
+ מחק
+
+
+ Account Settings
+ הגדרות חשבון
+
+
+ App Settings
+ הגדרות אפליקציה
+
+
+ Version: {0}.{1}.{2}
+ גרסת: {0}.{1}.{2}
+