mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-25 14:42:47 +01:00
Compare commits
No commits in common. "b1328e5b1f12cdcf7d06301bc958b2e9fda3be3f" and "b58c8f65feea50fd9b6297b005bca70372977ef0" have entirely different histories.
b1328e5b1f
...
b58c8f65fe
@ -11,8 +11,8 @@
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net6.0-windows7.0" />
|
||||
<option name="PROJECT_KIND" value="Console" />
|
||||
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.2" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
|
32
README.md
32
README.md
@ -43,14 +43,14 @@ Download links and system requirements are on the [official website](https://twe
|
||||
|
||||
Building TweetDuck for Windows requires at minimum [Visual Studio 2019](https://visualstudio.microsoft.com/downloads) and Windows 7. Before opening the solution, open Visual Studio Installer and make sure you have the following Visual Studio workloads and components installed:
|
||||
* **.NET desktop development**
|
||||
* .NET SDK
|
||||
* .NET Framework 4.7.2 targeting pack
|
||||
* F# desktop language support
|
||||
* **Desktop development with C++**
|
||||
* MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20 / Latest)
|
||||
|
||||
In the **Installation details** panel, you can expand the workloads you selected, and uncheck any components that are not listed above to save space. You may uncheck the .NET SDK component if you installed the [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) directly.
|
||||
In the **Installation details** panel, you can expand the workloads you selected, and uncheck any components that are not listed above to save space.
|
||||
|
||||
Building TweetDuck for Linux requires [.NET 6 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux). The Linux project has its own solution file in the `linux/` folder.
|
||||
Building TweetDuck for Linux requires [.NET 5](https://docs.microsoft.com/en-us/dotnet/core/install/linux). The Linux project has its own solution file in the `linux/` folder.
|
||||
|
||||
### Editors
|
||||
|
||||
@ -66,15 +66,15 @@ Icons and logos were designed in [Affinity Designer](https://affinity.serif.com/
|
||||
> If you don't want to build installers using the existing foundations, you can skip this section.
|
||||
|
||||
Official Windows installers are built using [InnoSetup](https://jrsoftware.org/isinfo.php) and [Inno Download Plugin](https://mitrichsoftware.wordpress.com/inno-setup-tools/inno-download-plugin/), specifically:
|
||||
* [InnoSetup 6.2.1](https://files.jrsoftware.org/is/6/innosetup-6.2.1.exe)
|
||||
* [Inno Download Plugin 1.5.1](https://drive.google.com/folderview?id=0Bzw1xBVt0mokSXZrUEFIanV4azA&usp=sharing#list)
|
||||
* [InnoSetup 5.6.1](https://files.jrsoftware.org/is/5/innosetup-5.6.1.exe) with Preprocessor support
|
||||
* [Inno Download Plugin 1.5.0](https://drive.google.com/folderview?id=0Bzw1xBVt0mokSXZrUEFIanV4azA&usp=sharing#list)
|
||||
|
||||
During installation, the download plugin will ask whether to add its include path to `ISPPBuiltins.iss`. Note that this option does not work with InnoSetup 6, so TweetDuck installers don't need it.
|
||||
When installing InnoSetup, you can choose to include Inno Script Studio which I recommend for editing and testing installer configuration files in the `bld` folder (`.iss` extension).
|
||||
|
||||
Scripts for building installers require the `PATH` environment variable to include the InnoSetup installation folder. You can either edit `PATH` manually, or use a program like [Rapid Environment Editor](https://www.rapidee.com/en/about) to simplify the process. For example, this is the installation folder I added to `PATH` under **User variables**:
|
||||
* `C:\Program Files (x86)\Inno Setup 6`
|
||||
* `C:\Program Files (x86)\Inno Setup 5`
|
||||
|
||||
You may need to restart Visual Studio or Rider after changing `PATH` for the change to take place.
|
||||
You may need to restart Visual Studio after changing `PATH` for the change to take place.
|
||||
|
||||
## Solution Overview
|
||||
|
||||
@ -84,13 +84,13 @@ On Windows, TweetDuck uses the [CefSharp](https://github.com/cefsharp/CefSharp/)
|
||||
|
||||
On Linux, TweetDuck uses the [ChromiumGtk](https://github.com/lunixo/ChromiumGtk) library, which combines [CefGlue](https://gitlab.com/xiliumhq/chromiumembedded/cefglue) for the browser component and [GtkSharp](https://github.com/GtkSharp/GtkSharp) for the GUI.
|
||||
|
||||
The solution contains several C# projects for executables and libraries, and F# projects for automated tests. All projects target `.NET 6` and either `C# 10` or `F#`.
|
||||
The solution contains several C# projects for executables and libraries, and F# projects for automated tests.
|
||||
|
||||
Projects are organized into folders:
|
||||
* Windows projects are in the `windows/` folder
|
||||
* Linux projects are in the `linux/` folder
|
||||
* Libraries (`TweetLib.*`) are in the `lib/` folder
|
||||
* Tests (`TweetTest.*`) are also in the `lib/` folder
|
||||
* Windows projects are in the `windows/` folder, and target `.NET Framework 4.7.2` + `C# 8.0`
|
||||
* Linux projects are in the `linux/` folder, and target `.NET 5` + `C#`
|
||||
* Libraries (`TweetLib.*`) are in the `lib/` folder, and target `.NET Standard 2.0` + `C# 9.0`
|
||||
* Tests (`TweetTest.*`) are also in the `lib/` folder, and target `.NET Framework 4.7.2` + `F#`
|
||||
|
||||
Here are a few things to keep in mind:
|
||||
* Executable projects have their entry points in `Program.cs`
|
||||
@ -130,7 +130,7 @@ Main Windows executable. It has a dependency on [CefSharp](https://github.com/ce
|
||||
|
||||
#### TweetDuck.Browser
|
||||
|
||||
Windows executable that hosts various Chromium processes. It has a dependency on [CefSharp](https://github.com/cefsharp/CefSharp/).
|
||||
Windows executable that hosts various Chromium processes. It depends on two specific DLLs from the [CefSharp](https://github.com/cefsharp/CefSharp/) package. After updating [CefSharp](https://github.com/cefsharp/CefSharp/), run the `windows/TweetDuck/Resources/PostCefUpdate.ps1` PowerShell script to update these dependencies to the new version.
|
||||
|
||||
#### TweetDuck.Video
|
||||
|
||||
@ -142,10 +142,6 @@ By default, [CefSharp](https://github.com/cefsharp/CefSharp/) is not built with
|
||||
|
||||
Windows library that implements `TweetLib.Browser.CEF` using the [CefSharp](https://github.com/cefsharp/CefSharp/) library and Windows Forms.
|
||||
|
||||
#### TweetLib.WinForms.Legacy
|
||||
|
||||
Windows library that re-adds some legacy Windows Forms components that were removed in .NET Core 3.1. The sources were taken from the [.NET Core 3.0 sources of Windows Forms](https://github.com/dotnet/winforms/tree/v3.0.2), and edited to remove unnecessary features.
|
||||
|
||||
### Linux Projects
|
||||
|
||||
#### TweetDuck
|
||||
|
@ -10,8 +10,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "windows\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetImpl.CefSharp", "windows\TweetImpl.CefSharp\TweetImpl.CefSharp.csproj", "{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.WinForms.Legacy", "windows\TweetLib.WinForms.Legacy\TweetLib.WinForms.Legacy.csproj", "{B54E732A-4090-4DAA-9ABD-311368C17B68}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
|
||||
@ -50,10 +48,6 @@ Global
|
||||
{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Debug|x86.Build.0 = Debug|x86
|
||||
{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Release|x86.ActiveCfg = Release|x86
|
||||
{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Release|x86.Build.0 = Release|x86
|
||||
{B54E732A-4090-4DAA-9ABD-311368C17B68}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B54E732A-4090-4DAA-9ABD-311368C17B68}.Debug|x86.Build.0 = Debug|x86
|
||||
{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.ActiveCfg = Release|x86
|
||||
{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.Build.0 = Release|x86
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
|
||||
|
@ -6,6 +6,6 @@
|
||||
|
||||
namespace TweetDuck {
|
||||
internal static class Version {
|
||||
public const string Tag = "1.23";
|
||||
public const string Tag = "1.22.1";
|
||||
}
|
||||
}
|
||||
|
BIN
bld/Redist/api-ms-win-core-console-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-console-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-datetime-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-datetime-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-debug-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-debug-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-errorhandling-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-errorhandling-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-file-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-file-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-file-l1-2-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-file-l1-2-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-file-l2-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-file-l2-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-handle-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-handle-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-heap-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-heap-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-interlocked-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-interlocked-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-libraryloader-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-libraryloader-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-localization-l1-2-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-localization-l1-2-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-memory-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-memory-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-namedpipe-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-namedpipe-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-processenvironment-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-processenvironment-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-processthreads-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-processthreads-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-processthreads-l1-1-1.dll
Normal file
BIN
bld/Redist/api-ms-win-core-processthreads-l1-1-1.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-profile-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-profile-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-rtlsupport-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-rtlsupport-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-string-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-string-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-synch-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-synch-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-synch-l1-2-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-synch-l1-2-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-sysinfo-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-sysinfo-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-timezone-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-timezone-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-core-util-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-core-util-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-conio-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-conio-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-convert-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-convert-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-environment-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-environment-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-filesystem-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-filesystem-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-heap-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-heap-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-locale-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-locale-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-math-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-math-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-multibyte-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-multibyte-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-private-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-private-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-process-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-process-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-runtime-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-runtime-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-stdio-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-stdio-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-string-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-string-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-time-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-time-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/api-ms-win-crt-utility-l1-1-0.dll
Normal file
BIN
bld/Redist/api-ms-win-crt-utility-l1-1-0.dll
Normal file
Binary file not shown.
BIN
bld/Redist/concrt140.dll
Normal file
BIN
bld/Redist/concrt140.dll
Normal file
Binary file not shown.
BIN
bld/Redist/msvcp140.dll
Normal file
BIN
bld/Redist/msvcp140.dll
Normal file
Binary file not shown.
BIN
bld/Redist/msvcp140_1.dll
Normal file
BIN
bld/Redist/msvcp140_1.dll
Normal file
Binary file not shown.
BIN
bld/Redist/msvcp140_2.dll
Normal file
BIN
bld/Redist/msvcp140_2.dll
Normal file
Binary file not shown.
BIN
bld/Redist/msvcp140_atomic_wait.dll
Normal file
BIN
bld/Redist/msvcp140_atomic_wait.dll
Normal file
Binary file not shown.
BIN
bld/Redist/msvcp140_codecvt_ids.dll
Normal file
BIN
bld/Redist/msvcp140_codecvt_ids.dll
Normal file
Binary file not shown.
BIN
bld/Redist/ucrtbase.dll
Normal file
BIN
bld/Redist/ucrtbase.dll
Normal file
Binary file not shown.
BIN
bld/Redist/vccorlib140.dll
Normal file
BIN
bld/Redist/vccorlib140.dll
Normal file
Binary file not shown.
BIN
bld/Redist/vcruntime140.dll
Normal file
BIN
bld/Redist/vcruntime140.dll
Normal file
Binary file not shown.
@ -9,8 +9,6 @@
|
||||
|
||||
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe")
|
||||
|
||||
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
|
||||
|
||||
[Setup]
|
||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||
AppName={#MyAppName}
|
||||
@ -31,11 +29,13 @@ Uninstallable=TDIsUninstallable
|
||||
UninstallDisplayName={#MyAppName}
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
Compression=lzma2/ultra
|
||||
LZMADictionarySize=32768
|
||||
LZMADictionarySize=15360
|
||||
SolidCompression=yes
|
||||
InternalCompressLevel=normal
|
||||
MinVersion=0,6.1
|
||||
|
||||
#include <idp.iss>
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
@ -68,11 +68,20 @@ AdditionalTasks=Additional shortcuts and components:
|
||||
var UpdatePath: String;
|
||||
var VisitedTasksPage: Boolean;
|
||||
|
||||
{ Prepare installation variables. }
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||
VisitedTasksPage := False
|
||||
|
||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := True
|
||||
end;
|
||||
|
||||
@ -131,3 +140,17 @@ function TDIsUninstallable: Boolean;
|
||||
begin
|
||||
Result := (UpdatePath = '')
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := 0
|
||||
end;
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe")
|
||||
|
||||
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
|
||||
|
||||
[Setup]
|
||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||
AppName={#MyAppName} Portable
|
||||
@ -31,11 +29,13 @@ Uninstallable=no
|
||||
UsePreviousAppDir=no
|
||||
PrivilegesRequired=lowest
|
||||
Compression=lzma2/ultra
|
||||
LZMADictionarySize=32768
|
||||
LZMADictionarySize=15360
|
||||
SolidCompression=yes
|
||||
InternalCompressLevel=normal
|
||||
MinVersion=0,6.1
|
||||
|
||||
#include <idp.iss>
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
@ -52,10 +52,19 @@ AdditionalTasks=Additional components:
|
||||
[Code]
|
||||
var UpdatePath: String;
|
||||
|
||||
{ Prepare installation variables. }
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||
|
||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := True
|
||||
end;
|
||||
|
||||
@ -93,3 +102,17 @@ begin
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := 0
|
||||
end;
|
||||
|
@ -11,8 +11,6 @@
|
||||
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe")
|
||||
#define CefVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\libcef.dll")
|
||||
|
||||
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
|
||||
|
||||
[Setup]
|
||||
AppId={{{#MyAppID}}
|
||||
AppName={#MyAppName}
|
||||
@ -39,6 +37,8 @@ SolidCompression=True
|
||||
InternalCompressLevel=normal
|
||||
MinVersion=0,6.1
|
||||
|
||||
#include <idp.iss>
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
@ -84,6 +84,7 @@ Type: filesandordirs; Name: "{app}\scripts"
|
||||
function TDIsUninstallable: Boolean; forward;
|
||||
function TDGetRunArgs(Param: String): String; forward;
|
||||
function TDFindUpdatePath: String; forward;
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
function TDGetAppVersionClean: String; forward;
|
||||
function TDGetFullDownloadFileName: String; forward;
|
||||
function TDIsMatchingCEFVersion: Boolean; forward;
|
||||
@ -92,7 +93,7 @@ procedure TDExecuteFullDownload; forward;
|
||||
var IsPortable: Boolean;
|
||||
var UpdatePath: String;
|
||||
|
||||
{ Prepare update installation, and the full download package if required. }
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. Prepare full download package if required. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
IsPortable := ExpandConstant('{param:PORTABLE}') = '1'
|
||||
@ -110,6 +111,12 @@ begin
|
||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'))
|
||||
end;
|
||||
|
||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := True
|
||||
end;
|
||||
|
||||
@ -204,6 +211,20 @@ begin
|
||||
Result := Path
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := 0
|
||||
end;
|
||||
|
||||
{ Return the name of the full installer file to download from GitHub. }
|
||||
function TDGetFullDownloadFileName: String;
|
||||
begin
|
||||
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "6.0.0",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TweetLib.Browser.CEF.Data {
|
||||
abstract class ContextMenuActionRegistry<T> where T : notnull {
|
||||
abstract class ContextMenuActionRegistry<T> {
|
||||
private readonly Dictionary<T, Action> actions = new ();
|
||||
|
||||
protected abstract T NextId(int n);
|
||||
|
@ -21,7 +21,7 @@ internal bool HasHandler(string url) {
|
||||
}
|
||||
|
||||
private void Register(string url, Func<TResourceHandler> factory) {
|
||||
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) {
|
||||
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) {
|
||||
throw new ArgumentException("Resource handler URL must be absolute!");
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ public bool OnFileDialog(FileDialogType type, IEnumerable<string> acceptFilters,
|
||||
};
|
||||
|
||||
fileDialogOpener.OpenFile("Open Files", multiple, filters, files => {
|
||||
string ext = Path.GetExtension(files[0]).ToLower();
|
||||
string ext = Path.GetExtension(files[0])!.ToLower();
|
||||
callbackAdapter.Continue(callback, Array.FindIndex(supportedExtensions, filter => ParseFileType(filter).Contains(ext)), files);
|
||||
callbackAdapter.Dispose(callback);
|
||||
}, () => {
|
||||
|
@ -9,7 +9,7 @@ private static (MessageDialogType, string) GetMessageDialogProperties(string tex
|
||||
|
||||
int pipe = text.IndexOf('|');
|
||||
if (pipe != -1) {
|
||||
type = text[..pipe] switch {
|
||||
type = text.Substring(0, pipe) switch {
|
||||
"error" => MessageDialogType.Error,
|
||||
"warning" => MessageDialogType.Warning,
|
||||
"info" => MessageDialogType.Information,
|
||||
@ -18,7 +18,7 @@ private static (MessageDialogType, string) GetMessageDialogProperties(string tex
|
||||
};
|
||||
|
||||
if (type != MessageDialogType.None) {
|
||||
text = text[(pipe + 1)..];
|
||||
text = text.Substring(pipe + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
@ -8,7 +8,7 @@ public static string GetCacheFolder(string storagePath) {
|
||||
return Path.Combine(storagePath, "Cache");
|
||||
}
|
||||
|
||||
public static CommandLineArgs ParseCommandLineArguments(string? argumentString) {
|
||||
public static CommandLineArgs ParseCommandLineArguments(string argumentString) {
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(argumentString)) {
|
||||
@ -26,8 +26,8 @@ public static CommandLineArgs ParseCommandLineArguments(string? argumentString)
|
||||
value = "1";
|
||||
}
|
||||
else {
|
||||
key = matchValue[..indexEquals].TrimStart('-');
|
||||
value = matchValue[(indexEquals + 1)..].Trim('"');
|
||||
key = matchValue.Substring(0, indexEquals).TrimStart('-');
|
||||
value = matchValue.Substring(indexEquals + 1).Trim('"');
|
||||
}
|
||||
|
||||
if (key.Length != 0) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
namespace TweetLib.Browser.Contexts {
|
||||
public struct Notification {
|
||||
public string? TweetUrl { get; }
|
||||
public string TweetUrl { get; }
|
||||
public string? QuoteUrl { get; }
|
||||
|
||||
public Notification(string? tweetUrl, string? quoteUrl) {
|
||||
public Notification(string tweetUrl, string? quoteUrl) {
|
||||
TweetUrl = tweetUrl;
|
||||
QuoteUrl = quoteUrl;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
@ -13,11 +13,13 @@ public static Server CreateServer() {
|
||||
|
||||
public static Client CreateClient(string token) {
|
||||
int space = token.IndexOf(' ');
|
||||
return new Client(token[..space], token[(space + 1)..]);
|
||||
return new Client(token.Substring(0, space), token.Substring(space + 1));
|
||||
}
|
||||
|
||||
private readonly PipeStream pipeIn;
|
||||
private readonly PipeStream pipeOut;
|
||||
|
||||
private readonly Thread readerThread;
|
||||
private readonly StreamWriter writerStream;
|
||||
|
||||
public event EventHandler<PipeReadEventArgs>? DataIn;
|
||||
@ -25,20 +27,21 @@ public static Client CreateClient(string token) {
|
||||
private DuplexPipe(PipeStream pipeIn, PipeStream pipeOut) {
|
||||
this.pipeIn = pipeIn;
|
||||
this.pipeOut = pipeOut;
|
||||
this.writerStream = new StreamWriter(this.pipeOut);
|
||||
|
||||
new Thread(ReaderThread) { IsBackground = true }.Start();
|
||||
this.readerThread = new Thread(ReaderThread) {
|
||||
IsBackground = true
|
||||
};
|
||||
|
||||
this.readerThread.Start();
|
||||
this.writerStream = new StreamWriter(this.pipeOut);
|
||||
}
|
||||
|
||||
private void ReaderThread() {
|
||||
using StreamReader read = new StreamReader(pipeIn);
|
||||
string? data;
|
||||
|
||||
try {
|
||||
while (read.ReadLine() is {} data) {
|
||||
DataIn?.Invoke(this, new PipeReadEventArgs(data));
|
||||
}
|
||||
} catch (ObjectDisposedException) {
|
||||
// expected
|
||||
while ((data = read.ReadLine()) != null) {
|
||||
DataIn?.Invoke(this, new PipeReadEventArgs(data));
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +56,12 @@ public void Write(string key, string data) {
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
try {
|
||||
readerThread.Abort();
|
||||
} catch {
|
||||
// /shrug
|
||||
}
|
||||
|
||||
pipeIn.Dispose();
|
||||
writerStream.Dispose();
|
||||
}
|
||||
@ -89,8 +98,8 @@ internal PipeReadEventArgs(string line) {
|
||||
Data = string.Empty;
|
||||
}
|
||||
else {
|
||||
Key = line[..separatorIndex];
|
||||
Data = line[(separatorIndex + 1)..];
|
||||
Key = line.Substring(0, separatorIndex);
|
||||
Data = line.Substring(separatorIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
@ -110,11 +110,11 @@ private static T Validate<T>(T? obj, string name) where T : class {
|
||||
}
|
||||
|
||||
public sealed class AppBuilder {
|
||||
public IAppSetup? Setup { get; init; }
|
||||
public IAppErrorHandler? ErrorHandler { get; init; }
|
||||
public IAppSystemHandler? SystemHandler { get; init; }
|
||||
public IAppMessageDialogs? MessageDialogs { get; init; }
|
||||
public IAppFileDialogs? FileDialogs { get; init; }
|
||||
public IAppSetup? Setup { get; set; }
|
||||
public IAppErrorHandler? ErrorHandler { get; set; }
|
||||
public IAppSystemHandler? SystemHandler { get; set; }
|
||||
public IAppMessageDialogs? MessageDialogs { get; set; }
|
||||
public IAppFileDialogs? FileDialogs { get; set; }
|
||||
|
||||
internal static AppBuilder? Instance { get; private set; }
|
||||
|
||||
|
@ -34,7 +34,7 @@ public static void LoadResourceRewriteRules(string rules) {
|
||||
if (resourceType is ResourceType.Script or ResourceType.Stylesheet && TweetDeckHashes.Count > 0) {
|
||||
Match match = TweetDeckResourceUrl.Match(url);
|
||||
|
||||
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out var hash)) {
|
||||
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)) {
|
||||
if (match.Groups[2].Value == hash) {
|
||||
App.Logger.Debug("[RequestHandlerBase] Accepting " + url);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public void SaveImages(string[] urls, string? author) {
|
||||
var settings = new SaveFileDialogSettings {
|
||||
DialogTitle = oneImage ? "Save Image" : "Save Images",
|
||||
OverwritePrompt = oneImage,
|
||||
FileName = qualityIndex == -1 ? filename : $"{author} {Path.ChangeExtension(filename, null)} {firstImageLink[(qualityIndex + 1)..]}".Trim() + ext,
|
||||
FileName = qualityIndex == -1 ? filename : $"{author} {Path.ChangeExtension(filename, null)} {firstImageLink.Substring(qualityIndex + 1)}".Trim() + ext,
|
||||
Filters = new [] { new FileDialogFilter(oneImage ? "Image" : "Images", string.IsNullOrEmpty(ext) ? Array.Empty<string>() : new [] { ext }) }
|
||||
};
|
||||
|
||||
|
@ -56,9 +56,7 @@ public override void Show(IContextMenuBuilder menu, Context context) {
|
||||
menu.AddSeparator();
|
||||
|
||||
if (context.Notification is {} notification) {
|
||||
if (!string.IsNullOrEmpty(notification.TweetUrl)) {
|
||||
AddCopyAction(menu, "Copy tweet address", notification.TweetUrl);
|
||||
}
|
||||
AddCopyAction(menu, "Copy tweet address", notification.TweetUrl);
|
||||
|
||||
if (!string.IsNullOrEmpty(notification.QuoteUrl)) {
|
||||
AddCopyAction(menu, "Copy quoted tweet address", notification.QuoteUrl!);
|
||||
@ -74,7 +72,7 @@ public override void Dispose() {
|
||||
this.browserComponent.PageLoadEnd -= BrowserComponentOnPageLoadEnd;
|
||||
}
|
||||
|
||||
private void BrowserComponentOnPageLoadEnd(object? sender, PageLoadEventArgs e) {
|
||||
private void BrowserComponentOnPageLoadEnd(object sender, PageLoadEventArgs e) {
|
||||
string url = e.Url;
|
||||
|
||||
if (TwitterUrls.IsTweetDeck(url) && url != BlankURL) {
|
||||
|
@ -93,7 +93,7 @@ public override int GetHashCode() {
|
||||
return Identifier.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) {
|
||||
public override bool Equals(object obj) {
|
||||
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||
}
|
||||
|
||||
|
@ -48,17 +48,17 @@ internal int GetTokenFromPlugin(Plugin plugin) {
|
||||
}
|
||||
|
||||
internal Plugin? GetPluginFromToken(int token) {
|
||||
return tokens.TryGetValue(token, out var plugin) ? plugin : null;
|
||||
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
private void manager_Reloaded(object? sender, PluginErrorEventArgs e) {
|
||||
private void manager_Reloaded(object sender, PluginErrorEventArgs e) {
|
||||
tokens.Clear();
|
||||
fileCache.Clear();
|
||||
}
|
||||
|
||||
private void Config_PluginChangedState(object? sender, PluginChangedStateEventArgs e) {
|
||||
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e) {
|
||||
if (!e.IsEnabled) {
|
||||
int token = GetTokenFromPlugin(e.Plugin);
|
||||
|
||||
@ -88,7 +88,7 @@ private string GetFullPathOrThrow(int token, PluginFolder folder, string path) {
|
||||
private string ReadFileUnsafe(int token, PluginFolder folder, string path, bool readCached) {
|
||||
string fullPath = GetFullPathOrThrow(token, folder, path);
|
||||
|
||||
if (readCached && fileCache.TryGetValue(token, folder, path, out var cachedContents)) {
|
||||
if (readCached && fileCache.TryGetValue(token, folder, path, out string cachedContents)) {
|
||||
return cachedContents;
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ public void Clear() {
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
public bool TryGetValue(int token, PluginFolder folder, string path, [MaybeNullWhen(false)] out string contents) {
|
||||
public bool TryGetValue(int token, PluginFolder folder, string path, out string contents) {
|
||||
return cache.TryGetValue(token, Key(folder, path), out contents);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ public static IEnumerable<Result<Plugin>> AllInFolder(string pluginFolder, strin
|
||||
private static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group) {
|
||||
Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData);
|
||||
|
||||
foreach (var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName!)) {
|
||||
foreach (var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName)) {
|
||||
builder.AddEnvironment(environment);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ private static Plugin FromFolder(string name, string pathRoot, string pathData,
|
||||
string currentContents = string.Empty;
|
||||
|
||||
foreach (string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(static line => line.TrimEnd()).Where(static line => line.Length > 0)) {
|
||||
if (line[0] == '[' && line[^1] == ']') {
|
||||
if (line[0] == '[' && line[line.Length - 1] == ']') {
|
||||
if (currentTag != null) {
|
||||
SetProperty(builder, currentTag, currentContents);
|
||||
}
|
||||
@ -106,7 +106,7 @@ private static void SetProperty(Plugin.Builder builder, string tag, string value
|
||||
builder.ConfigDefault = value;
|
||||
break;
|
||||
case "REQUIRES":
|
||||
builder.RequiredVersion = Version.TryParse(value, out var version) ? version : throw new FormatException($"Invalid required minimum version: {value}");
|
||||
builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException($"Invalid required minimum version: {value}");
|
||||
break;
|
||||
default:
|
||||
throw new FormatException($"Invalid metadata tag: {tag}");
|
||||
|
@ -102,7 +102,7 @@ internal void Execute(PluginEnvironment environment, IScriptExecutor executor) {
|
||||
Executed?.Invoke(this, new PluginErrorEventArgs(errors));
|
||||
}
|
||||
|
||||
private void Config_PluginChangedState(object? sender, PluginChangedStateEventArgs e) {
|
||||
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e) {
|
||||
browserExecutor?.RunFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||
}
|
||||
|
||||
|
@ -71,12 +71,12 @@ public override void Dispose() {
|
||||
App.UserConfiguration.SoundNotificationChanged -= UserConfiguration_SoundNotificationChanged;
|
||||
}
|
||||
|
||||
private void browserComponent_BrowserLoaded(object? sender, BrowserLoadedEventArgs e) {
|
||||
private void browserComponent_BrowserLoaded(object sender, BrowserLoadedEventArgs e) {
|
||||
e.AddDictionaryWords("tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD");
|
||||
isBrowserReady = true;
|
||||
}
|
||||
|
||||
private void browserComponent_PageLoadStart(object? sender, PageLoadEventArgs e) {
|
||||
private void browserComponent_PageLoadStart(object sender, PageLoadEventArgs e) {
|
||||
string url = e.Url;
|
||||
|
||||
if (TwitterUrls.IsTweetDeck(url) || (TwitterUrls.IsTwitter(url) && !TwitterUrls.IsTwitterLogin2Factor(url))) {
|
||||
@ -84,7 +84,7 @@ private void browserComponent_PageLoadStart(object? sender, PageLoadEventArgs e)
|
||||
}
|
||||
}
|
||||
|
||||
private void browserComponent_PageLoadEnd(object? sender, PageLoadEventArgs e) {
|
||||
private void browserComponent_PageLoadEnd(object sender, PageLoadEventArgs e) {
|
||||
string url = e.Url;
|
||||
|
||||
if (TwitterUrls.IsTweetDeck(url)) {
|
||||
@ -105,7 +105,7 @@ private void browserComponent_PageLoadEnd(object? sender, PageLoadEventArgs e) {
|
||||
browserComponent.RunBootstrap("update");
|
||||
}
|
||||
|
||||
private void pluginManager_Reloaded(object? sender, PluginErrorEventArgs e) {
|
||||
private void pluginManager_Reloaded(object sender, PluginErrorEventArgs e) {
|
||||
if (e.HasErrors) {
|
||||
App.MessageDialogs.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n" + string.Join("\n\n", e.Errors));
|
||||
}
|
||||
@ -115,14 +115,14 @@ private void pluginManager_Reloaded(object? sender, PluginErrorEventArgs e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void pluginManager_Executed(object? sender, PluginErrorEventArgs e) {
|
||||
private void pluginManager_Executed(object sender, PluginErrorEventArgs e) {
|
||||
if (e.HasErrors) {
|
||||
App.MessageDialogs.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n" + string.Join("\n\n", e.Errors));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChecker_CheckFinished(object? sender, UpdateCheckEventArgs e) {
|
||||
var updateChecker = (UpdateChecker) sender!;
|
||||
private void updateChecker_CheckFinished(object sender, UpdateCheckEventArgs e) {
|
||||
var updateChecker = (UpdateChecker) sender;
|
||||
|
||||
e.Result.Handle(update => {
|
||||
string tag = update.VersionTag;
|
||||
@ -144,7 +144,7 @@ private void updateChecker_CheckFinished(object? sender, UpdateCheckEventArgs e)
|
||||
ignoreUpdateCheckError = true;
|
||||
}
|
||||
|
||||
private void UserConfiguration_GeneralEventHandler(object? sender, EventArgs e) {
|
||||
private void UserConfiguration_GeneralEventHandler(object sender, EventArgs e) {
|
||||
UpdatePropertyObject();
|
||||
}
|
||||
|
||||
@ -236,11 +236,6 @@ private sealed class ResourceRequestHandler : BaseResourceRequestHandler {
|
||||
private const string UrlVersionCheck = "/web/dist/version.json";
|
||||
|
||||
public override RequestHandleResult? Handle(string url, ResourceType resourceType) {
|
||||
var result = base.Handle(url, resourceType);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (resourceType) {
|
||||
case ResourceType.MainFrame when url.EndsWithOrdinal("://twitter.com/"):
|
||||
return new RequestHandleResult.Redirect(TwitterUrls.TweetDeck); // redirect plain twitter.com requests, fixes bugs with login 2FA
|
||||
@ -261,7 +256,7 @@ private sealed class ResourceRequestHandler : BaseResourceRequestHandler {
|
||||
return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1"));
|
||||
|
||||
default:
|
||||
return null;
|
||||
return base.Handle(url, resourceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,15 @@ public void SetLink(string type, string? url) {
|
||||
|
||||
switch (type) {
|
||||
case "link":
|
||||
Link = new Link(url, url);
|
||||
Link = new Link(url!, url!);
|
||||
break;
|
||||
|
||||
case "image":
|
||||
Media = new Media(Type.Image, TwitterUrls.GetMediaLink(url, App.UserConfiguration.TwitterImageQuality));
|
||||
Media = new Media(Type.Image, TwitterUrls.GetMediaLink(url!, App.UserConfiguration.TwitterImageQuality));
|
||||
break;
|
||||
|
||||
case "video":
|
||||
Media = new Media(Type.Video, url);
|
||||
Media = new Media(Type.Video, url!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public static bool TryParse(string url, out ImageUrl obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string originalUrl = url[..question];
|
||||
string originalUrl = url.Substring(0, question);
|
||||
|
||||
obj = new ImageUrl(Path.HasExtension(originalUrl) ? originalUrl : originalUrl + imageExtension, imageQuality);
|
||||
return true;
|
||||
|
@ -49,11 +49,11 @@ public enum UrlType {
|
||||
}
|
||||
|
||||
public static UrlType Check(string url) {
|
||||
if (url.Contains('"')) {
|
||||
if (url.Contains("\"")) {
|
||||
return UrlType.Invalid;
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out var uri)) {
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) {
|
||||
string scheme = uri.Scheme;
|
||||
|
||||
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto) {
|
||||
|
@ -16,7 +16,7 @@ static ConfigManager() {
|
||||
ConverterRegistry.Register(typeof(WindowState), new BasicTypeConverter<WindowState> {
|
||||
ConvertToString = static value => $"{(value.IsMaximized ? 'M' : '_')}{value.Bounds.X} {value.Bounds.Y} {value.Bounds.Width} {value.Bounds.Height}",
|
||||
ConvertToObject = static value => {
|
||||
int[] elements = StringUtils.ParseInts(value[1..], ' ');
|
||||
int[] elements = StringUtils.ParseInts(value.Substring(1), ' ');
|
||||
|
||||
return new WindowState {
|
||||
Bounds = new Rectangle(elements[0], elements[1], elements[2], elements[3]),
|
||||
|
@ -43,7 +43,7 @@ private bool Log(string level, string message) {
|
||||
build.Append("Please, report all issues to: ").Append(Lib.IssueTrackerUrl).Append("\r\n\r\n");
|
||||
}
|
||||
|
||||
build.Append('[').Append(DateTime.Now.ToString("G", Lib.Culture)).Append("] ").Append(level).Append("\r\n");
|
||||
build.Append("[").Append(DateTime.Now.ToString("G", Lib.Culture)).Append("] ").Append(level).Append("\r\n");
|
||||
build.Append(message).Append("\r\n\r\n");
|
||||
|
||||
try {
|
||||
|
@ -37,7 +37,7 @@ public void Dispose() {
|
||||
InteractionManager.Dispose();
|
||||
}
|
||||
|
||||
private void timer_Elapsed(object? sender, ElapsedEventArgs e) {
|
||||
private void timer_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
Check(false);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ public void ClearUpdate() {
|
||||
nextUpdate = null;
|
||||
}
|
||||
|
||||
private void updates_CheckFinished(object? sender, UpdateCheckEventArgs e) {
|
||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e) {
|
||||
UpdateInfo? foundUpdate = e.Result.HasValue ? e.Result.Value : null;
|
||||
|
||||
if (nextUpdate != null && !nextUpdate.Equals(foundUpdate)) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
@ -57,7 +57,7 @@ public void SetValue(string key, string value) {
|
||||
}
|
||||
|
||||
public string? GetValue(string key) {
|
||||
return values.TryGetValue(key.ToLower(), out var val) ? val : null;
|
||||
return values.TryGetValue(key.ToLower(), out string val) ? val : null;
|
||||
}
|
||||
|
||||
public void RemoveValue(string key) {
|
||||
|
@ -11,7 +11,7 @@ namespace TweetLib.Utils.Collections {
|
||||
/// <typeparam name="V">The type of the values.</typeparam>
|
||||
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class TwoKeyDictionary<K1, K2, V> where K1 : notnull where K2 : notnull {
|
||||
public sealed class TwoKeyDictionary<K1, K2, V> {
|
||||
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
||||
private readonly int innerCapacity;
|
||||
|
||||
@ -41,7 +41,7 @@ public TwoKeyDictionary(int outerCapacity, int innerCapacity) {
|
||||
}
|
||||
|
||||
set {
|
||||
if (!dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict)) {
|
||||
if (!dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)) {
|
||||
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ public IEnumerable<V> InnerValues {
|
||||
/// Throws if the key pair already exists.
|
||||
/// </summary>
|
||||
public void Add(K1 outerKey, K2 innerKey, V value) {
|
||||
if (!dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict)) {
|
||||
if (!dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)) {
|
||||
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public bool Contains(K1 outerKey) {
|
||||
/// Determines whether the dictionary contains the key pair.
|
||||
/// </summary>
|
||||
public bool Contains(K1 outerKey, K2 innerKey) {
|
||||
return dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict) && innerDict.ContainsKey(innerKey);
|
||||
return dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict) && innerDict.ContainsKey(innerKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -122,8 +122,8 @@ public int Count(K1 outerKey) {
|
||||
/// Gets the value associated with the key pair.
|
||||
/// Returns true if the key pair was present.
|
||||
/// </summary>
|
||||
public bool TryGetValue(K1 outerKey, K2 innerKey, [MaybeNullWhen(false)] out V value) {
|
||||
if (dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict)) {
|
||||
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value) {
|
||||
if (dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)) {
|
||||
return innerDict.TryGetValue(innerKey, out value);
|
||||
}
|
||||
else {
|
||||
@ -145,7 +145,7 @@ public bool Remove(K1 outerKey) {
|
||||
/// Returns true if the key pair was present.
|
||||
/// </summary>
|
||||
public bool Remove(K1 outerKey, K2 innerKey) {
|
||||
if (dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict) && innerDict.Remove(innerKey)) {
|
||||
if (dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict) && innerDict.Remove(innerKey)) {
|
||||
if (innerDict.Count == 0) {
|
||||
dict.Remove(outerKey);
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace TweetLib.Utils.Dialogs {
|
||||
public sealed class SaveFileDialogSettings {
|
||||
public string DialogTitle { get; init; } = "Save File";
|
||||
public bool OverwritePrompt { get; init; } = true;
|
||||
public string? FileName { get; init; }
|
||||
public IReadOnlyList<FileDialogFilter>? Filters { get; init; }
|
||||
public string DialogTitle { get; set; } = "Save File";
|
||||
public bool OverwritePrompt { get; set; } = true;
|
||||
public string? FileName { get; set; }
|
||||
public IReadOnlyList<FileDialogFilter>? Filters { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public Language(string code, string? alt = null) {
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) {
|
||||
public override bool Equals(object obj) {
|
||||
return obj is Language other && Code.Equals(other.Code, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@ -38,8 +38,8 @@ public override string ToString() {
|
||||
return cultureInfo.DisplayName == cultureInfo.NativeName ? capitalizedName : $"{capitalizedName}, {cultureInfo.DisplayName}";
|
||||
}
|
||||
|
||||
public int CompareTo(Language? other) {
|
||||
return string.Compare(Name, other?.Name, false, CultureInfo.InvariantCulture);
|
||||
public int CompareTo(Language other) {
|
||||
return string.Compare(Name, other.Name, false, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ public sealed class Entry {
|
||||
public string[] KeyValue {
|
||||
get {
|
||||
int index = Identifier.IndexOf(KeySeparator);
|
||||
return index == -1 ? StringUtils.EmptyArray : Identifier[(index + 1)..].Split(KeySeparator);
|
||||
return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index + 1).Split(KeySeparator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
namespace TweetLib.Utils.Serialization.Converters {
|
||||
public sealed class BasicTypeConverter<T> : ITypeConverter {
|
||||
public Func<T, string>? ConvertToString { get; init; }
|
||||
public Func<string, T>? ConvertToObject { get; init; }
|
||||
public Func<T, string>? ConvertToString { get; set; }
|
||||
public Func<string, T>? ConvertToObject { get; set; }
|
||||
|
||||
bool ITypeConverter.TryWriteType(Type type, object? value, out string? converted) {
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string? converted) {
|
||||
try {
|
||||
converted = ConvertToString!((T) value!);
|
||||
converted = ConvertToString!((T) value);
|
||||
return true;
|
||||
} catch {
|
||||
converted = null;
|
||||
|
@ -6,17 +6,18 @@ sealed class ClrTypeConverter : ITypeConverter {
|
||||
|
||||
private ClrTypeConverter() {}
|
||||
|
||||
bool ITypeConverter.TryWriteType(Type type, object? value, out string? converted) {
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string? converted) {
|
||||
switch (Type.GetTypeCode(type)) {
|
||||
case TypeCode.Boolean:
|
||||
converted = value!.ToString();
|
||||
converted = value.ToString();
|
||||
return true;
|
||||
|
||||
case TypeCode.Int32:
|
||||
converted = ((int) value!).ToString(); // cast required for enums
|
||||
converted = ((int) value).ToString(); // cast required for enums
|
||||
return true;
|
||||
|
||||
case TypeCode.String:
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
converted = value?.ToString();
|
||||
return true;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace TweetLib.Utils.Serialization {
|
||||
public interface ITypeConverter {
|
||||
bool TryWriteType(Type type, object? value, out string? converted);
|
||||
bool TryWriteType(Type type, object value, out string? converted);
|
||||
bool TryReadType(Type type, string value, out object? converted);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ private static string UnescapeStream(StreamReader reader) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
build.Append(data[index..nextIndex]);
|
||||
build.Append(data.Substring(index, nextIndex - index));
|
||||
|
||||
char next = data[nextIndex + 1];
|
||||
|
||||
@ -47,7 +47,7 @@ private static string UnescapeStream(StreamReader reader) {
|
||||
}
|
||||
}
|
||||
|
||||
return build.Append(data[index..]).ToString();
|
||||
return build.Append(data.Substring(index)).ToString();
|
||||
}
|
||||
|
||||
private readonly TypeConverterRegistry converterRegistry;
|
||||
@ -114,7 +114,7 @@ public void Read(string file, T obj) {
|
||||
int nextPos = contents.IndexOf(NewLineReal, currentPos);
|
||||
|
||||
if (nextPos == -1) {
|
||||
line = contents[currentPos..];
|
||||
line = contents.Substring(currentPos);
|
||||
currentPos = -1;
|
||||
|
||||
if (string.IsNullOrEmpty(line)) {
|
||||
@ -133,10 +133,10 @@ public void Read(string file, T obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string property = line[..space];
|
||||
string value = UnescapeLine(line[(space + 1)..]);
|
||||
string property = line.Substring(0, space);
|
||||
string value = UnescapeLine(line.Substring(space + 1));
|
||||
|
||||
if (props.TryGetValue(property, out var info)) {
|
||||
if (props.TryGetValue(property, out PropertyInfo info)) {
|
||||
var type = info.PropertyType;
|
||||
var converter = converterRegistry.TryGet(type) ?? ClrTypeConverter.Instance;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using TweetLib.Utils.Static;
|
||||
@ -90,6 +91,7 @@ public UnlockResult Unlock() {
|
||||
return UnlockResult.Success;
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
||||
private LockResult DetermineLockingProcessOrFail(Exception originalException) {
|
||||
try {
|
||||
int pid;
|
||||
@ -108,7 +110,7 @@ private LockResult DetermineLockingProcessOrFail(Exception originalException) {
|
||||
var foundProcess = Process.GetProcessById(pid);
|
||||
using var currentProcess = Process.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.MainModule!.FileVersionInfo.InternalName == foundProcess.MainModule!.FileVersionInfo.InternalName) {
|
||||
if (currentProcess.MainModule.FileVersionInfo.InternalName == foundProcess.MainModule.FileVersionInfo.InternalName) {
|
||||
return new LockResult.HasProcess(foundProcess);
|
||||
}
|
||||
else {
|
||||
|
@ -40,7 +40,7 @@ public static (string before, string after)? SplitInTwo(string str, char search,
|
||||
return null;
|
||||
}
|
||||
|
||||
return (str[..index], str[(index + 1)..]);
|
||||
return (str.Substring(0, index), str.Substring(index + 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -49,7 +49,7 @@ public static (string before, string after)? SplitInTwo(string str, char search,
|
||||
/// </summary>
|
||||
public static string ExtractBefore(string str, char search, int startIndex = 0) {
|
||||
int index = str.IndexOf(search, startIndex);
|
||||
return index == -1 ? str : str[..index];
|
||||
return index == -1 ? str : str.Substring(0, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -10,12 +10,10 @@ public static class WebUtils {
|
||||
private static bool hasMicrosoftBeenBroughtTo2008Yet;
|
||||
private static bool hasSystemProxyBeenEnabled;
|
||||
|
||||
private static void EnsureModernTLS() {
|
||||
private static void EnsureTLS12() {
|
||||
if (!hasMicrosoftBeenBroughtTo2008Yet) {
|
||||
#pragma warning disable CS0618
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
|
||||
#pragma warning restore CS0618
|
||||
hasMicrosoftBeenBroughtTo2008Yet = true;
|
||||
}
|
||||
}
|
||||
@ -30,7 +28,7 @@ public static void EnableSystemProxy() {
|
||||
}
|
||||
|
||||
public static WebClient NewClient(string? userAgent = null) {
|
||||
EnsureModernTLS();
|
||||
EnsureTLS12();
|
||||
|
||||
WebClient client = new WebClient();
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
@ -1,9 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<RuntimeIdentifiers>win7-x86;linux-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -47,7 +47,7 @@ module RegexAccount =
|
||||
Assert.True(isMatch("https://twitter.com/" + name))
|
||||
|
||||
module Match =
|
||||
let extract str = TwitterUrls.RegexAccount.Match(str).Groups[1].Value
|
||||
let extract str = TwitterUrls.RegexAccount.Match(str).Groups.[1].Value
|
||||
|
||||
[<Fact>]
|
||||
let ``extracts account name from simple URL`` () =
|
||||
|
@ -1,9 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<RuntimeIdentifiers>win7-x86;linux-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user