1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 19:32:10 +02:00

Compare commits

...

44 Commits

Author SHA1 Message Date
293683fbdc Release 1.26 2023-07-17 12:49:49 +02:00
57111212ea Fix warning message about TweetDeck Preview appearing on login screen 2023-07-17 12:49:49 +02:00
a58a1b347a Show a deprecation notice on launch
References #360
2023-07-17 01:04:49 +02:00
37303ddf02 Fix doubled border in Settings dialog 2023-07-17 00:58:50 +02:00
f8417706e1 Update CefSharp to 109 2023-07-16 21:40:55 +02:00
c5a42e74d9 Release 1.25.4 2023-05-09 23:03:38 +02:00
23ebd58da0 Fix video player showing controls when cursor is above a different window
Closes #357
2023-03-18 13:48:29 +01:00
9a6394d079 Release 1.25.3 2023-03-09 20:28:03 +01:00
88a55c8795 Work around NullReferenceException when browser frame load events have a null URL
Closes #354
2023-03-09 20:28:03 +01:00
d406866a02 Hide button that enables the TweetDeck Preview 2023-03-09 20:18:40 +01:00
c2094bd2c0 Show error message if TweetDuck features fail to load, for example due to TweetDeck Preview
Closes #356
2023-03-09 20:17:59 +01:00
065b5a751b Release 1.25.2 2022-11-21 20:24:54 +01:00
0dc454e61f Re-add UCRT DLLs 2022-11-21 20:22:04 +01:00
416a43f0b1 Fix update installer not running as administrator due to breaking change in .NET 2022-11-21 20:07:23 +01:00
7a338076db Release 1.25.1 2022-11-21 08:09:55 +01:00
54bf1c2012 Make update installer delete unused files and folders 2022-11-21 08:09:55 +01:00
32681259f6 Re-add VC++ DLLs 2022-11-21 07:59:20 +01:00
1c1aa5ea44 Fix crash when right-clicking tray icon
Closes #350
2022-11-20 20:52:10 +01:00
da54af221c Release 1.25 2022-11-20 14:38:40 +01:00
6c8d518e0d Extract processor architecture to a variable in installer scripts 2022-11-20 14:38:40 +01:00
697f4f1569 Fix notification timer stopping permanently when the screen is locked while a notification is visible 2022-11-20 14:38:40 +01:00
15d4ec3228 Fix subprocess crashing because MSBuild is somehow copying wrong (not Large Address Aware) exe
Closes #342
2022-11-20 13:33:51 +01:00
c303346bc3 Halt reloading after subprocess crash if it happens too often 2022-11-20 08:04:53 +01:00
b9af966849 Update CefSharp to 107 2022-11-20 05:29:23 +01:00
0a7459b72e Update to .NET 7 2022-11-20 05:25:29 +01:00
9953f06ab1 Release 1.24 2022-09-14 02:19:02 +02:00
0c8159aa79 Update CefSharp to 105 2022-09-14 01:55:18 +02:00
c785a7ed8c Update test project dependencies 2022-09-01 01:32:15 +02:00
b1328e5b1f Release 1.23 2022-08-20 13:28:16 +02:00
cb94f0c81e Fix resource freezing not working for vendor.js 2022-08-20 13:26:01 +02:00
8de2989f12 Add icon to browser executable 2022-08-20 13:26:01 +02:00
1cf7d13873 Update installers to Inno Setup 6 & remove references to .NET Framework 2022-08-20 13:26:01 +02:00
35c2ee3673 Work around broken bottom anchor in PluginControl 2022-08-20 13:26:01 +02:00
a1b4c31450 Fix increased size of windows and dialogs from moving to .NET 6 2022-08-20 13:26:01 +02:00
ea95e5cbac Re-add ContextMenu that was removed in .NET Core 3.1 2022-08-20 13:26:00 +02:00
2927097e8e Add TLS 1.3 to default security protocols 2022-08-20 13:26:00 +02:00
b5bffdb95b Update all projects to C# 10 2022-08-20 13:26:00 +02:00
bee894bfbb Fix compiler and IDE warnings for .NET 6 2022-08-20 13:26:00 +02:00
96d2e7cc7c Migrate to .NET 6 & update CefSharp to 102 2022-08-20 13:26:00 +02:00
b58c8f65fe Release 1.22.1 2022-06-26 11:56:13 +02:00
2c69289785 Disable TweetDeck preview for accounts that have it enabled
Closes #337
2022-06-26 11:56:12 +02:00
dc0fc06673 Add instructions for configuring DevEnvDir MSBuild property when developing with Rider 2022-05-16 19:35:08 +02:00
3114b489b6 Fix README headings 2022-03-31 15:39:43 +02:00
8e5934bd84 Fix possible error when focusing DM input field 2022-03-03 12:40:49 +01:00
243 changed files with 2021 additions and 1448 deletions

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ bld/*
# Rider # Rider
**/.idea/dictionaries **/.idea/dictionaries
**/.idea/misc.xml **/.idea/misc.xml
**/.idea/riderMarkupCache.xml
# User-specific files # User-specific files
*.suo *.suo

View File

@@ -11,8 +11,8 @@
<option name="PROJECT_EXE_PATH_TRACKING" value="1" /> <option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="Console" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.2" /> <option name="PROJECT_TFM" value="net7.0-windows" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

View File

@@ -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: 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 desktop development**
* .NET Framework 4.7.2 targeting pack * .NET SDK
* F# desktop language support * F# desktop language support
* **Desktop development with C++** * **Desktop development with C++**
* MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20 / Latest) * 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. 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.
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. 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.
### Editors ### 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. > 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: 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 5.6.1](https://files.jrsoftware.org/is/5/innosetup-5.6.1.exe) with Preprocessor support * [InnoSetup 6.2.1](https://files.jrsoftware.org/is/6/innosetup-6.2.1.exe)
* [Inno Download Plugin 1.5.0](https://drive.google.com/folderview?id=0Bzw1xBVt0mokSXZrUEFIanV4azA&usp=sharing#list) * [Inno Download Plugin 1.5.1](https://drive.google.com/folderview?id=0Bzw1xBVt0mokSXZrUEFIanV4azA&usp=sharing#list)
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). 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.
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**: 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 5` * `C:\Program Files (x86)\Inno Setup 6`
You may need to restart Visual Studio after changing `PATH` for the change to take place. You may need to restart Visual Studio or Rider after changing `PATH` for the change to take place.
## Solution Overview ## 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. 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. 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#`.
Projects are organized into folders: Projects are organized into folders:
* Windows projects are in the `windows/` folder, and target `.NET Framework 4.7.2` + `C# 8.0` * Windows projects are in the `windows/` folder
* Linux projects are in the `linux/` folder, and target `.NET 5` + `C#` * Linux projects are in the `linux/` folder
* Libraries (`TweetLib.*`) are in the `lib/` folder, and target `.NET Standard 2.0` + `C# 9.0` * Libraries (`TweetLib.*`) are in the `lib/` folder
* Tests (`TweetTest.*`) are also in the `lib/` folder, and target `.NET Framework 4.7.2` + `F#` * Tests (`TweetTest.*`) are also in the `lib/` folder
Here are a few things to keep in mind: Here are a few things to keep in mind:
* Executable projects have their entry points in `Program.cs` * 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 #### TweetDuck.Browser
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. Windows executable that hosts various Chromium processes. It has a dependency on [CefSharp](https://github.com/cefsharp/CefSharp/).
#### TweetDuck.Video #### TweetDuck.Video
@@ -142,6 +142,10 @@ 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. 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 ### Linux Projects
#### TweetDuck #### TweetDuck
@@ -168,7 +172,13 @@ These are F# projects with automated tests.
# Development (Windows) # Development (Windows)
When developing with [Rider](https://www.jetbrains.com/rider/), it must be configured to use MSBuild from Visual Studio. You can set it in **File | Settings | Build, Execution, Deployment | Toolset and Build** with the `Use MSBuild version` drop-down. When developing with [Rider](https://www.jetbrains.com/rider/), it must be configured to use MSBuild from Visual Studio, and the `DevEnvDir` property must be set to the full path to the `Common7\IDE` folder which is inside Visual Studio's installation folder. You can set both in **File | Settings | Build, Execution, Deployment | Toolset and Build**:
1. Click the `MSBuild version` drop-down, and select the path that includes the Visual Studio installation folder.
2. Click the Edit button next to `MSBuild global properties`.
3. Add a new property named `DevEnvDir`, and set its value to the full path to `Common7\IDE`. For example:
- `VS 2019 Community` - `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE`
- `VS 2022 Community` - `C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE`
## Building ## Building
@@ -215,11 +225,11 @@ If you plan to distribute your own installers, you can change the variables in t
> Running `GEN INSTALLERS.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`. > Running `GEN INSTALLERS.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`.
## Development (Linux) # Development (Linux)
Unfortunately the development experience on Linux is terrible, likely due to mixed C# and native code. The .NET debugger seems to crash the moment it enters native code, so the only way to run the app is without the debugger attached. If any C# code throws an exception, it will crash the whole application with no usable stack trace or error message. Please let me know if you find a way to make this better. Unfortunately the development experience on Linux is terrible, likely due to mixed C# and native code. The .NET debugger seems to crash the moment it enters native code, so the only way to run the app is without the debugger attached. If any C# code throws an exception, it will crash the whole application with no usable stack trace or error message. Please let me know if you find a way to make this better.
### Building ## Building
The `linux/TweetDuck/TweetDuck.csproj` project file has several tasks (targets) that run after a build: The `linux/TweetDuck/TweetDuck.csproj` project file has several tasks (targets) that run after a build:
@@ -227,7 +237,7 @@ The `linux/TweetDuck/TweetDuck.csproj` project file has several tasks (targets)
* `FinalizeDebug` copies a debug plugin (`Resources/Plugins/.debug`) into the build folder (Debug only) * `FinalizeDebug` copies a debug plugin (`Resources/Plugins/.debug`) into the build folder (Debug only)
* `FinalizeRelease` prepares the build folder for publishing (Release only) * `FinalizeRelease` prepares the build folder for publishing (Release only)
### Release ## Release
To change the application version before a release, search for the `<Version>` tag in every `.csproj` file in the `linux/` folder and modify it. To change the application version before a release, search for the `<Version>` tag in every `.csproj` file in the `linux/` folder and modify it.

View File

@@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "windows\
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetImpl.CefSharp", "windows\TweetImpl.CefSharp\TweetImpl.CefSharp.csproj", "{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetImpl.CefSharp", "windows\TweetImpl.CefSharp\TweetImpl.CefSharp.csproj", "{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}"
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
@@ -48,6 +50,10 @@ Global
{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Debug|x86.Build.0 = Debug|x86 {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.ActiveCfg = Release|x86
{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Release|x86.Build.0 = 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.ActiveCfg = Debug|x86
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86 {72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86 {72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86

View File

@@ -6,6 +6,6 @@ using TweetDuck;
namespace TweetDuck { namespace TweetDuck {
internal static class Version { internal static class Version {
public const string Tag = "1.22.0.1"; public const string Tag = "1.26";
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +1,21 @@
; Script generated by the Inno Script Studio Wizard. ; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
#define MyAppName "TweetDuck" #define MyAppName "TweetDuck"
#define MyAppPublisher "chylex" #define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com" #define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com" #define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe" #define MyAppExeName "TweetDuck.exe"
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe") #define MyAppArchitecture "x86"
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\" + MyAppArchitecture + "\Release\TweetDuck.exe")
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
[Setup] [Setup]
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06} AppId={{{#MyAppID}}
AppName={#MyAppName} AppName={#MyAppName}
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion} AppVerName={#MyAppName} {#MyAppVersion}
@@ -29,13 +34,11 @@ Uninstallable=TDIsUninstallable
UninstallDisplayName={#MyAppName} UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName} UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma2/ultra Compression=lzma2/ultra
LZMADictionarySize=15360 LZMADictionarySize=32768
SolidCompression=yes SolidCompression=yes
InternalCompressLevel=normal InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss>
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -43,8 +46,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalTasks}"; Flags: unchecked Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalTasks}"; Flags: unchecked
[Files] [Files]
Source: "..\windows\TweetDuck\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
@@ -68,20 +71,11 @@ AdditionalTasks=Additional shortcuts and components:
var UpdatePath: String; var UpdatePath: String;
var VisitedTasksPage: Boolean; var VisitedTasksPage: Boolean;
function TDGetNetFrameworkVersion: Cardinal; forward; { Prepare installation variables. }
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
function InitializeSetup: Boolean; function InitializeSetup: Boolean;
begin begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}') UpdatePath := ExpandConstant('{param:UPDATEPATH}')
VisitedTasksPage := False 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 Result := True
end; end;
@@ -140,17 +134,3 @@ function TDIsUninstallable: Boolean;
begin begin
Result := (UpdatePath = '') Result := (UpdatePath = '')
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;

View File

@@ -1,16 +1,21 @@
; Script generated by the Inno Script Studio Wizard. ; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
#define MyAppName "TweetDuck" #define MyAppName "TweetDuck"
#define MyAppPublisher "chylex" #define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com" #define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com" #define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe" #define MyAppExeName "TweetDuck.exe"
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe") #define MyAppArchitecture "x86"
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\" + MyAppArchitecture + "\Release\TweetDuck.exe")
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
[Setup] [Setup]
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06} AppId={{{#MyAppID}}
AppName={#MyAppName} Portable AppName={#MyAppName} Portable
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion} AppVerName={#MyAppName} {#MyAppVersion}
@@ -29,19 +34,17 @@ Uninstallable=no
UsePreviousAppDir=no UsePreviousAppDir=no
PrivilegesRequired=lowest PrivilegesRequired=lowest
Compression=lzma2/ultra Compression=lzma2/ultra
LZMADictionarySize=15360 LZMADictionarySize=32768
SolidCompression=yes SolidCompression=yes
InternalCompressLevel=normal InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss>
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
[Files] [Files]
Source: "..\windows\TweetDuck\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Run] [Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
@@ -52,19 +55,10 @@ AdditionalTasks=Additional components:
[Code] [Code]
var UpdatePath: String; var UpdatePath: String;
function TDGetNetFrameworkVersion: Cardinal; forward; { Prepare installation variables. }
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
function InitializeSetup: Boolean; function InitializeSetup: Boolean;
begin begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}') 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 Result := True
end; end;
@@ -102,17 +96,3 @@ begin
end; end;
end; 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;

View File

@@ -1,15 +1,19 @@
; Script generated by the Inno Script Studio Wizard. ; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
#define MyAppName "TweetDuck" #define MyAppName "TweetDuck"
#define MyAppPublisher "chylex" #define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com" #define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com" #define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe" #define MyAppExeName "TweetDuck.exe"
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06" #define MyAppArchitecture "x86"
#define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\TweetDuck.exe") #define MyAppVersion GetFileVersion("..\windows\TweetDuck\bin\" + MyAppArchitecture + "\Release\TweetDuck.exe")
#define CefVersion GetFileVersion("..\windows\TweetDuck\bin\x86\Release\libcef.dll") #define CefVersion GetFileVersion("..\windows\TweetDuck\bin\" + MyAppArchitecture + "\Release\libcef.dll")
#include ReadReg(HKLM, "Software\Mitrich Software\Inno Download Plugin", "InstallDir") + "\idp.iss"
[Setup] [Setup]
AppId={{{#MyAppID}} AppId={{{#MyAppID}}
@@ -37,19 +41,17 @@ SolidCompression=True
InternalCompressLevel=normal InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss>
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
[Files] [Files]
Source: "..\windows\TweetDuck\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\TweetDuck.*"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\TweetDuck.*"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\TweetImpl.*"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\TweetImpl.*"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\TweetLib.*"; DestDir: "{app}"; Flags: ignoreversion Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\TweetLib.*"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\windows\TweetDuck\bin\x86\Release\guide\*.*"; DestDir: "{app}\guide"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\guide\*.*"; DestDir: "{app}\guide"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\windows\TweetDuck\bin\x86\Release\resources\*.*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\resources\*.*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\windows\TweetDuck\bin\x86\Release\plugins\*.*"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\windows\TweetDuck\bin\{#MyAppArchitecture}\Release\plugins\*.*"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
@@ -69,22 +71,24 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
Type: files; Name: "{app}\CEFSHARP-LICENSE.txt" Type: files; Name: "{app}\CEFSHARP-LICENSE.txt"
Type: files; Name: "{app}\LICENSE.txt" Type: files; Name: "{app}\LICENSE.txt"
Type: files; Name: "{app}\README.txt" Type: files; Name: "{app}\README.txt"
Type: files; Name: "{app}\natives_blob.bin"
Type: files; Name: "{app}\cef.pak" Type: files; Name: "{app}\cef.pak"
Type: files; Name: "{app}\cef_100_percent.pak" Type: files; Name: "{app}\cef_100_percent.pak"
Type: files; Name: "{app}\cef_200_percent.pak" Type: files; Name: "{app}\cef_200_percent.pak"
Type: files; Name: "{app}\cef_extensions.pak" Type: files; Name: "{app}\cef_extensions.pak"
Type: files; Name: "{app}\devtools_resources.pak" Type: files; Name: "{app}\devtools_resources.pak"
Type: files; Name: "{app}\natives_blob.bin"
Type: files; Name: "{app}\dbgshim.dll"
Type: files; Name: "{app}\mscordaccore_x86_x86_6.*.dll"
Type: filesandordirs; Name: "{app}\guide" Type: filesandordirs; Name: "{app}\guide"
Type: filesandordirs; Name: "{app}\plugins\official" Type: filesandordirs; Name: "{app}\plugins\official"
Type: filesandordirs; Name: "{app}\resources" Type: filesandordirs; Name: "{app}\resources"
Type: filesandordirs; Name: "{app}\scripts" Type: filesandordirs; Name: "{app}\scripts"
Type: filesandordirs; Name: "{app}\swiftshader"
[Code] [Code]
function TDIsUninstallable: Boolean; forward; function TDIsUninstallable: Boolean; forward;
function TDGetRunArgs(Param: String): String; forward; function TDGetRunArgs(Param: String): String; forward;
function TDFindUpdatePath: String; forward; function TDFindUpdatePath: String; forward;
function TDGetNetFrameworkVersion: Cardinal; forward;
function TDGetAppVersionClean: String; forward; function TDGetAppVersionClean: String; forward;
function TDGetFullDownloadFileName: String; forward; function TDGetFullDownloadFileName: String; forward;
function TDIsMatchingCEFVersion: Boolean; forward; function TDIsMatchingCEFVersion: Boolean; forward;
@@ -93,7 +97,7 @@ procedure TDExecuteFullDownload; forward;
var IsPortable: Boolean; var IsPortable: Boolean;
var UpdatePath: String; var UpdatePath: String;
{ 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. } { Prepare update installation, and the full download package if required. }
function InitializeSetup: Boolean; function InitializeSetup: Boolean;
begin begin
IsPortable := ExpandConstant('{param:PORTABLE}') = '1' IsPortable := ExpandConstant('{param:PORTABLE}') = '1'
@@ -111,12 +115,6 @@ begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe')) idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'))
end; 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 Result := True
end; end;
@@ -211,20 +209,6 @@ begin
Result := Path Result := Path
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;
{ Return the name of the full installer file to download from GitHub. } { Return the name of the full installer file to download from GitHub. }
function TDGetFullDownloadFileName: String; function TDGetFullDownloadFileName: String;
begin begin

7
global.json Normal file
View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "7.0.0",
"rollForward": "latestMinor",
"allowPrerelease": false
}
}

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetLib.Browser.CEF.Data { namespace TweetLib.Browser.CEF.Data {
abstract class ContextMenuActionRegistry<T> { abstract class ContextMenuActionRegistry<T> where T : notnull {
private readonly Dictionary<T, Action> actions = new (); private readonly Dictionary<T, Action> actions = new ();
protected abstract T NextId(int n); protected abstract T NextId(int n);

View File

@@ -21,7 +21,7 @@ namespace TweetLib.Browser.CEF.Data {
} }
private void Register(string url, Func<TResourceHandler> factory) { private void Register(string url, Func<TResourceHandler> factory) {
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) { if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) {
throw new ArgumentException("Resource handler URL must be absolute!"); throw new ArgumentException("Resource handler URL must be absolute!");
} }

View File

@@ -29,7 +29,7 @@ namespace TweetLib.Browser.CEF.Logic {
}; };
fileDialogOpener.OpenFile("Open Files", multiple, filters, files => { 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.Continue(callback, Array.FindIndex(supportedExtensions, filter => ParseFileType(filter).Contains(ext)), files);
callbackAdapter.Dispose(callback); callbackAdapter.Dispose(callback);
}, () => { }, () => {

View File

@@ -9,7 +9,7 @@ namespace TweetLib.Browser.CEF.Logic {
int pipe = text.IndexOf('|'); int pipe = text.IndexOf('|');
if (pipe != -1) { if (pipe != -1) {
type = text.Substring(0, pipe) switch { type = text[..pipe] switch {
"error" => MessageDialogType.Error, "error" => MessageDialogType.Error,
"warning" => MessageDialogType.Warning, "warning" => MessageDialogType.Warning,
"info" => MessageDialogType.Information, "info" => MessageDialogType.Information,
@@ -18,7 +18,7 @@ namespace TweetLib.Browser.CEF.Logic {
}; };
if (type != MessageDialogType.None) { if (type != MessageDialogType.None) {
text = text.Substring(pipe + 1); text = text[(pipe + 1)..];
} }
} }

View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>x86;x64</Platforms> <Platforms>x86;x64</Platforms>
<LangVersion>9</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>

View File

@@ -8,7 +8,7 @@ namespace TweetLib.Browser.CEF.Utils {
return Path.Combine(storagePath, "Cache"); return Path.Combine(storagePath, "Cache");
} }
public static CommandLineArgs ParseCommandLineArguments(string argumentString) { public static CommandLineArgs ParseCommandLineArguments(string? argumentString) {
CommandLineArgs args = new CommandLineArgs(); CommandLineArgs args = new CommandLineArgs();
if (string.IsNullOrWhiteSpace(argumentString)) { if (string.IsNullOrWhiteSpace(argumentString)) {
@@ -26,8 +26,8 @@ namespace TweetLib.Browser.CEF.Utils {
value = "1"; value = "1";
} }
else { else {
key = matchValue.Substring(0, indexEquals).TrimStart('-'); key = matchValue[..indexEquals].TrimStart('-');
value = matchValue.Substring(indexEquals + 1).Trim('"'); value = matchValue[(indexEquals + 1)..].Trim('"');
} }
if (key.Length != 0) { if (key.Length != 0) {

View File

@@ -1,9 +1,9 @@
namespace TweetLib.Browser.Contexts { namespace TweetLib.Browser.Contexts {
public struct Notification { public struct Notification {
public string TweetUrl { get; } public string? TweetUrl { get; }
public string? QuoteUrl { get; } public string? QuoteUrl { get; }
public Notification(string tweetUrl, string? quoteUrl) { public Notification(string? tweetUrl, string? quoteUrl) {
TweetUrl = tweetUrl; TweetUrl = tweetUrl;
QuoteUrl = quoteUrl; QuoteUrl = quoteUrl;
} }

View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>x86;x64</Platforms> <Platforms>x86;x64</Platforms>
<LangVersion>9</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>

View File

@@ -13,13 +13,11 @@ namespace TweetLib.Communication.Pipe {
public static Client CreateClient(string token) { public static Client CreateClient(string token) {
int space = token.IndexOf(' '); int space = token.IndexOf(' ');
return new Client(token.Substring(0, space), token.Substring(space + 1)); return new Client(token[..space], token[(space + 1)..]);
} }
private readonly PipeStream pipeIn; private readonly PipeStream pipeIn;
private readonly PipeStream pipeOut; private readonly PipeStream pipeOut;
private readonly Thread readerThread;
private readonly StreamWriter writerStream; private readonly StreamWriter writerStream;
public event EventHandler<PipeReadEventArgs>? DataIn; public event EventHandler<PipeReadEventArgs>? DataIn;
@@ -27,21 +25,20 @@ namespace TweetLib.Communication.Pipe {
private DuplexPipe(PipeStream pipeIn, PipeStream pipeOut) { private DuplexPipe(PipeStream pipeIn, PipeStream pipeOut) {
this.pipeIn = pipeIn; this.pipeIn = pipeIn;
this.pipeOut = pipeOut; this.pipeOut = pipeOut;
this.readerThread = new Thread(ReaderThread) {
IsBackground = true
};
this.readerThread.Start();
this.writerStream = new StreamWriter(this.pipeOut); this.writerStream = new StreamWriter(this.pipeOut);
new Thread(ReaderThread) { IsBackground = true }.Start();
} }
private void ReaderThread() { private void ReaderThread() {
using StreamReader read = new StreamReader(pipeIn); using StreamReader read = new StreamReader(pipeIn);
string? data;
while ((data = read.ReadLine()) != null) { try {
DataIn?.Invoke(this, new PipeReadEventArgs(data)); while (read.ReadLine() is {} data) {
DataIn?.Invoke(this, new PipeReadEventArgs(data));
}
} catch (ObjectDisposedException) {
// expected
} }
} }
@@ -56,12 +53,6 @@ namespace TweetLib.Communication.Pipe {
} }
public void Dispose() { public void Dispose() {
try {
readerThread.Abort();
} catch {
// /shrug
}
pipeIn.Dispose(); pipeIn.Dispose();
writerStream.Dispose(); writerStream.Dispose();
} }
@@ -98,8 +89,8 @@ namespace TweetLib.Communication.Pipe {
Data = string.Empty; Data = string.Empty;
} }
else { else {
Key = line.Substring(0, separatorIndex); Key = line[..separatorIndex];
Data = line.Substring(separatorIndex + 1); Data = line[(separatorIndex + 1)..];
} }
} }
} }

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Platforms>x86</Platforms> <Platforms>x86</Platforms>
<LangVersion>9</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>

View File

@@ -110,11 +110,11 @@ namespace TweetLib.Core {
} }
public sealed class AppBuilder { public sealed class AppBuilder {
public IAppSetup? Setup { get; set; } public IAppSetup? Setup { get; init; }
public IAppErrorHandler? ErrorHandler { get; set; } public IAppErrorHandler? ErrorHandler { get; init; }
public IAppSystemHandler? SystemHandler { get; set; } public IAppSystemHandler? SystemHandler { get; init; }
public IAppMessageDialogs? MessageDialogs { get; set; } public IAppMessageDialogs? MessageDialogs { get; init; }
public IAppFileDialogs? FileDialogs { get; set; } public IAppFileDialogs? FileDialogs { get; init; }
internal static AppBuilder? Instance { get; private set; } internal static AppBuilder? Instance { get; private set; }

View File

@@ -34,7 +34,7 @@ namespace TweetLib.Core.Features {
if (resourceType is ResourceType.Script or ResourceType.Stylesheet && TweetDeckHashes.Count > 0) { if (resourceType is ResourceType.Script or ResourceType.Stylesheet && TweetDeckHashes.Count > 0) {
Match match = TweetDeckResourceUrl.Match(url); Match match = TweetDeckResourceUrl.Match(url);
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)) { if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out var hash)) {
if (match.Groups[2].Value == hash) { if (match.Groups[2].Value == hash) {
App.Logger.Debug("[RequestHandlerBase] Accepting " + url); App.Logger.Debug("[RequestHandlerBase] Accepting " + url);
} }

View File

@@ -84,7 +84,7 @@ namespace TweetLib.Core.Features {
var settings = new SaveFileDialogSettings { var settings = new SaveFileDialogSettings {
DialogTitle = oneImage ? "Save Image" : "Save Images", DialogTitle = oneImage ? "Save Image" : "Save Images",
OverwritePrompt = oneImage, OverwritePrompt = oneImage,
FileName = qualityIndex == -1 ? filename : $"{author} {Path.ChangeExtension(filename, null)} {firstImageLink.Substring(qualityIndex + 1)}".Trim() + ext, FileName = qualityIndex == -1 ? filename : $"{author} {Path.ChangeExtension(filename, null)} {firstImageLink[(qualityIndex + 1)..]}".Trim() + ext,
Filters = new [] { new FileDialogFilter(oneImage ? "Image" : "Images", string.IsNullOrEmpty(ext) ? Array.Empty<string>() : new [] { ext }) } Filters = new [] { new FileDialogFilter(oneImage ? "Image" : "Images", string.IsNullOrEmpty(ext) ? Array.Empty<string>() : new [] { ext }) }
}; };

View File

@@ -56,7 +56,9 @@ namespace TweetLib.Core.Features.Notifications {
menu.AddSeparator(); menu.AddSeparator();
if (context.Notification is {} notification) { if (context.Notification is {} notification) {
AddCopyAction(menu, "Copy tweet address", notification.TweetUrl); if (!string.IsNullOrEmpty(notification.TweetUrl)) {
AddCopyAction(menu, "Copy tweet address", notification.TweetUrl);
}
if (!string.IsNullOrEmpty(notification.QuoteUrl)) { if (!string.IsNullOrEmpty(notification.QuoteUrl)) {
AddCopyAction(menu, "Copy quoted tweet address", notification.QuoteUrl!); AddCopyAction(menu, "Copy quoted tweet address", notification.QuoteUrl!);
@@ -72,7 +74,7 @@ namespace TweetLib.Core.Features.Notifications {
this.browserComponent.PageLoadEnd -= BrowserComponentOnPageLoadEnd; this.browserComponent.PageLoadEnd -= BrowserComponentOnPageLoadEnd;
} }
private void BrowserComponentOnPageLoadEnd(object sender, PageLoadEventArgs e) { private void BrowserComponentOnPageLoadEnd(object? sender, PageLoadEventArgs e) {
string url = e.Url; string url = e.Url;
if (TwitterUrls.IsTweetDeck(url) && url != BlankURL) { if (TwitterUrls.IsTweetDeck(url) && url != BlankURL) {

View File

@@ -93,7 +93,7 @@ namespace TweetLib.Core.Features.Plugins {
return Identifier.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); return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
} }

View File

@@ -48,17 +48,17 @@ namespace TweetLib.Core.Features.Plugins {
} }
internal Plugin? GetPluginFromToken(int token) { internal Plugin? GetPluginFromToken(int token) {
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null; return tokens.TryGetValue(token, out var plugin) ? plugin : null;
} }
// Event handlers // Event handlers
private void manager_Reloaded(object sender, PluginErrorEventArgs e) { private void manager_Reloaded(object? sender, PluginErrorEventArgs e) {
tokens.Clear(); tokens.Clear();
fileCache.Clear(); fileCache.Clear();
} }
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e) { private void Config_PluginChangedState(object? sender, PluginChangedStateEventArgs e) {
if (!e.IsEnabled) { if (!e.IsEnabled) {
int token = GetTokenFromPlugin(e.Plugin); int token = GetTokenFromPlugin(e.Plugin);
@@ -88,7 +88,7 @@ namespace TweetLib.Core.Features.Plugins {
private string ReadFileUnsafe(int token, PluginFolder folder, string path, bool readCached) { private string ReadFileUnsafe(int token, PluginFolder folder, string path, bool readCached) {
string fullPath = GetFullPathOrThrow(token, folder, path); string fullPath = GetFullPathOrThrow(token, folder, path);
if (readCached && fileCache.TryGetValue(token, folder, path, out string cachedContents)) { if (readCached && fileCache.TryGetValue(token, folder, path, out var cachedContents)) {
return cachedContents; return cachedContents;
} }
@@ -161,7 +161,7 @@ namespace TweetLib.Core.Features.Plugins {
cache.Clear(); cache.Clear();
} }
public bool TryGetValue(int token, PluginFolder folder, string path, out string contents) { public bool TryGetValue(int token, PluginFolder folder, string path, [MaybeNullWhen(false)] out string contents) {
return cache.TryGetValue(token, Key(folder, path), out contents); return cache.TryGetValue(token, Key(folder, path), out contents);
} }

View File

@@ -41,7 +41,7 @@ namespace TweetLib.Core.Features.Plugins {
private static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group) { private static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group) {
Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData); 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); builder.AddEnvironment(environment);
} }
@@ -55,7 +55,7 @@ namespace TweetLib.Core.Features.Plugins {
string currentContents = string.Empty; 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)) { 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[line.Length - 1] == ']') { if (line[0] == '[' && line[^1] == ']') {
if (currentTag != null) { if (currentTag != null) {
SetProperty(builder, currentTag, currentContents); SetProperty(builder, currentTag, currentContents);
} }
@@ -106,7 +106,7 @@ namespace TweetLib.Core.Features.Plugins {
builder.ConfigDefault = value; builder.ConfigDefault = value;
break; break;
case "REQUIRES": case "REQUIRES":
builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException($"Invalid required minimum version: {value}"); builder.RequiredVersion = Version.TryParse(value, out var version) ? version : throw new FormatException($"Invalid required minimum version: {value}");
break; break;
default: default:
throw new FormatException($"Invalid metadata tag: {tag}"); throw new FormatException($"Invalid metadata tag: {tag}");

View File

@@ -102,7 +102,7 @@ namespace TweetLib.Core.Features.Plugins {
Executed?.Invoke(this, new PluginErrorEventArgs(errors)); 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); browserExecutor?.RunFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
} }

View File

@@ -71,12 +71,12 @@ namespace TweetLib.Core.Features.TweetDeck {
App.UserConfiguration.SoundNotificationChanged -= UserConfiguration_SoundNotificationChanged; 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"); e.AddDictionaryWords("tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD");
isBrowserReady = true; isBrowserReady = true;
} }
private void browserComponent_PageLoadStart(object sender, PageLoadEventArgs e) { private void browserComponent_PageLoadStart(object? sender, PageLoadEventArgs e) {
string url = e.Url; string url = e.Url;
if (TwitterUrls.IsTweetDeck(url) || (TwitterUrls.IsTwitter(url) && !TwitterUrls.IsTwitterLogin2Factor(url))) { if (TwitterUrls.IsTweetDeck(url) || (TwitterUrls.IsTwitter(url) && !TwitterUrls.IsTwitterLogin2Factor(url))) {
@@ -84,7 +84,7 @@ namespace TweetLib.Core.Features.TweetDeck {
} }
} }
private void browserComponent_PageLoadEnd(object sender, PageLoadEventArgs e) { private void browserComponent_PageLoadEnd(object? sender, PageLoadEventArgs e) {
string url = e.Url; string url = e.Url;
if (TwitterUrls.IsTweetDeck(url)) { if (TwitterUrls.IsTweetDeck(url)) {
@@ -105,7 +105,7 @@ namespace TweetLib.Core.Features.TweetDeck {
browserComponent.RunBootstrap("update"); browserComponent.RunBootstrap("update");
} }
private void pluginManager_Reloaded(object sender, PluginErrorEventArgs e) { private void pluginManager_Reloaded(object? sender, PluginErrorEventArgs e) {
if (e.HasErrors) { 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)); 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 @@ namespace TweetLib.Core.Features.TweetDeck {
} }
} }
private void pluginManager_Executed(object sender, PluginErrorEventArgs e) { private void pluginManager_Executed(object? sender, PluginErrorEventArgs e) {
if (e.HasErrors) { if (e.HasErrors) {
App.MessageDialogs.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n" + string.Join("\n\n", e.Errors)); 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) { private void updateChecker_CheckFinished(object? sender, UpdateCheckEventArgs e) {
var updateChecker = (UpdateChecker) sender; var updateChecker = (UpdateChecker) sender!;
e.Result.Handle(update => { e.Result.Handle(update => {
string tag = update.VersionTag; string tag = update.VersionTag;
@@ -144,7 +144,7 @@ namespace TweetLib.Core.Features.TweetDeck {
ignoreUpdateCheckError = true; ignoreUpdateCheckError = true;
} }
private void UserConfiguration_GeneralEventHandler(object sender, EventArgs e) { private void UserConfiguration_GeneralEventHandler(object? sender, EventArgs e) {
UpdatePropertyObject(); UpdatePropertyObject();
} }
@@ -236,6 +236,11 @@ namespace TweetLib.Core.Features.TweetDeck {
private const string UrlVersionCheck = "/web/dist/version.json"; private const string UrlVersionCheck = "/web/dist/version.json";
public override RequestHandleResult? Handle(string url, ResourceType resourceType) { public override RequestHandleResult? Handle(string url, ResourceType resourceType) {
var result = base.Handle(url, resourceType);
if (result != null) {
return result;
}
switch (resourceType) { switch (resourceType) {
case ResourceType.MainFrame when url.EndsWithOrdinal("://twitter.com/"): case ResourceType.MainFrame when url.EndsWithOrdinal("://twitter.com/"):
return new RequestHandleResult.Redirect(TwitterUrls.TweetDeck); // redirect plain twitter.com requests, fixes bugs with login 2FA return new RequestHandleResult.Redirect(TwitterUrls.TweetDeck); // redirect plain twitter.com requests, fixes bugs with login 2FA
@@ -256,7 +261,7 @@ namespace TweetLib.Core.Features.TweetDeck {
return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1")); return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1"));
default: default:
return base.Handle(url, resourceType); return null;
} }
} }
} }

View File

@@ -24,15 +24,15 @@ namespace TweetLib.Core.Features.TweetDeck {
switch (type) { switch (type) {
case "link": case "link":
Link = new Link(url!, url!); Link = new Link(url, url);
break; break;
case "image": 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; break;
case "video": case "video":
Media = new Media(Type.Video, url!); Media = new Media(Type.Video, url);
break; break;
} }
} }

View File

@@ -57,7 +57,7 @@ namespace TweetLib.Core.Features.Twitter {
return false; return false;
} }
string originalUrl = url.Substring(0, question); string originalUrl = url[..question];
obj = new ImageUrl(Path.HasExtension(originalUrl) ? originalUrl : originalUrl + imageExtension, imageQuality); obj = new ImageUrl(Path.HasExtension(originalUrl) ? originalUrl : originalUrl + imageExtension, imageQuality);
return true; return true;

View File

@@ -49,11 +49,11 @@ namespace TweetLib.Core.Features.Twitter {
} }
public static UrlType Check(string url) { public static UrlType Check(string url) {
if (url.Contains("\"")) { if (url.Contains('"')) {
return UrlType.Invalid; return UrlType.Invalid;
} }
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) { if (Uri.TryCreate(url, UriKind.Absolute, out var uri)) {
string scheme = uri.Scheme; string scheme = uri.Scheme;
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto) { if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto) {

View File

@@ -16,7 +16,7 @@ namespace TweetLib.Core.Systems.Configuration {
ConverterRegistry.Register(typeof(WindowState), new BasicTypeConverter<WindowState> { 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}", ConvertToString = static value => $"{(value.IsMaximized ? 'M' : '_')}{value.Bounds.X} {value.Bounds.Y} {value.Bounds.Width} {value.Bounds.Height}",
ConvertToObject = static value => { ConvertToObject = static value => {
int[] elements = StringUtils.ParseInts(value.Substring(1), ' '); int[] elements = StringUtils.ParseInts(value[1..], ' ');
return new WindowState { return new WindowState {
Bounds = new Rectangle(elements[0], elements[1], elements[2], elements[3]), Bounds = new Rectangle(elements[0], elements[1], elements[2], elements[3]),

View File

@@ -43,7 +43,7 @@ namespace TweetLib.Core.Systems.Logging {
build.Append("Please, report all issues to: ").Append(Lib.IssueTrackerUrl).Append("\r\n\r\n"); 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"); build.Append(message).Append("\r\n\r\n");
try { try {

View File

@@ -37,7 +37,7 @@ namespace TweetLib.Core.Systems.Updates {
InteractionManager.Dispose(); InteractionManager.Dispose();
} }
private void timer_Elapsed(object sender, ElapsedEventArgs e) { private void timer_Elapsed(object? sender, ElapsedEventArgs e) {
Check(false); Check(false);
} }

View File

@@ -26,7 +26,7 @@ namespace TweetLib.Core.Systems.Updates {
nextUpdate = null; 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; UpdateInfo? foundUpdate = e.Result.HasValue ? e.Result.Value : null;
if (nextUpdate != null && !nextUpdate.Equals(foundUpdate)) { if (nextUpdate != null && !nextUpdate.Equals(foundUpdate)) {

View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>x86;x64</Platforms> <Platforms>x86;x64</Platforms>
<LangVersion>9</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>

View File

@@ -57,7 +57,7 @@ namespace TweetLib.Utils.Collections {
} }
public string? GetValue(string key) { public string? GetValue(string key) {
return values.TryGetValue(key.ToLower(), out string val) ? val : null; return values.TryGetValue(key.ToLower(), out var val) ? val : null;
} }
public void RemoveValue(string key) { public void RemoveValue(string key) {

View File

@@ -11,7 +11,7 @@ namespace TweetLib.Utils.Collections {
/// <typeparam name="V">The type of the values.</typeparam> /// <typeparam name="V">The type of the values.</typeparam>
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class TwoKeyDictionary<K1, K2, V> { public sealed class TwoKeyDictionary<K1, K2, V> where K1 : notnull where K2 : notnull {
private readonly Dictionary<K1, Dictionary<K2, V>> dict; private readonly Dictionary<K1, Dictionary<K2, V>> dict;
private readonly int innerCapacity; private readonly int innerCapacity;
@@ -41,7 +41,7 @@ namespace TweetLib.Utils.Collections {
} }
set { 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)); dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
} }
@@ -67,7 +67,7 @@ namespace TweetLib.Utils.Collections {
/// Throws if the key pair already exists. /// Throws if the key pair already exists.
/// </summary> /// </summary>
public void Add(K1 outerKey, K2 innerKey, V value) { 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)); dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
} }
@@ -100,7 +100,7 @@ namespace TweetLib.Utils.Collections {
/// Determines whether the dictionary contains the key pair. /// Determines whether the dictionary contains the key pair.
/// </summary> /// </summary>
public bool Contains(K1 outerKey, K2 innerKey) { 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> /// <summary>
@@ -122,8 +122,8 @@ namespace TweetLib.Utils.Collections {
/// Gets the value associated with the key pair. /// Gets the value associated with the key pair.
/// Returns true if the key pair was present. /// Returns true if the key pair was present.
/// </summary> /// </summary>
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value) { public bool TryGetValue(K1 outerKey, K2 innerKey, [MaybeNullWhen(false)] out V value) {
if (dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)) { if (dict.TryGetValue(outerKey, out Dictionary<K2, V>? innerDict)) {
return innerDict.TryGetValue(innerKey, out value); return innerDict.TryGetValue(innerKey, out value);
} }
else { else {
@@ -145,7 +145,7 @@ namespace TweetLib.Utils.Collections {
/// Returns true if the key pair was present. /// Returns true if the key pair was present.
/// </summary> /// </summary>
public bool Remove(K1 outerKey, K2 innerKey) { 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) { if (innerDict.Count == 0) {
dict.Remove(outerKey); dict.Remove(outerKey);
} }

View File

@@ -2,9 +2,9 @@ using System.Collections.Generic;
namespace TweetLib.Utils.Dialogs { namespace TweetLib.Utils.Dialogs {
public sealed class SaveFileDialogSettings { public sealed class SaveFileDialogSettings {
public string DialogTitle { get; set; } = "Save File"; public string DialogTitle { get; init; } = "Save File";
public bool OverwritePrompt { get; set; } = true; public bool OverwritePrompt { get; init; } = true;
public string? FileName { get; set; } public string? FileName { get; init; }
public IReadOnlyList<FileDialogFilter>? Filters { get; set; } public IReadOnlyList<FileDialogFilter>? Filters { get; init; }
} }
} }

View File

@@ -21,7 +21,7 @@ namespace TweetLib.Utils.Globalization {
} }
} }
public override bool Equals(object obj) { public override bool Equals(object? obj) {
return obj is Language other && Code.Equals(other.Code, StringComparison.OrdinalIgnoreCase); return obj is Language other && Code.Equals(other.Code, StringComparison.OrdinalIgnoreCase);
} }
@@ -38,8 +38,8 @@ namespace TweetLib.Utils.Globalization {
return cultureInfo.DisplayName == cultureInfo.NativeName ? capitalizedName : $"{capitalizedName}, {cultureInfo.DisplayName}"; return cultureInfo.DisplayName == cultureInfo.NativeName ? capitalizedName : $"{capitalizedName}, {cultureInfo.DisplayName}";
} }
public int CompareTo(Language other) { public int CompareTo(Language? other) {
return string.Compare(Name, other.Name, false, CultureInfo.InvariantCulture); return string.Compare(Name, other?.Name, false, CultureInfo.InvariantCulture);
} }
} }
} }

View File

@@ -134,7 +134,7 @@ namespace TweetLib.Utils.IO {
public string[] KeyValue { public string[] KeyValue {
get { get {
int index = Identifier.IndexOf(KeySeparator); int index = Identifier.IndexOf(KeySeparator);
return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index + 1).Split(KeySeparator); return index == -1 ? StringUtils.EmptyArray : Identifier[(index + 1)..].Split(KeySeparator);
} }
} }

View File

@@ -2,12 +2,12 @@
namespace TweetLib.Utils.Serialization.Converters { namespace TweetLib.Utils.Serialization.Converters {
public sealed class BasicTypeConverter<T> : ITypeConverter { public sealed class BasicTypeConverter<T> : ITypeConverter {
public Func<T, string>? ConvertToString { get; set; } public Func<T, string>? ConvertToString { get; init; }
public Func<string, T>? ConvertToObject { get; set; } public Func<string, T>? ConvertToObject { get; init; }
bool ITypeConverter.TryWriteType(Type type, object value, out string? converted) { bool ITypeConverter.TryWriteType(Type type, object? value, out string? converted) {
try { try {
converted = ConvertToString!((T) value); converted = ConvertToString!((T) value!);
return true; return true;
} catch { } catch {
converted = null; converted = null;

View File

@@ -6,18 +6,17 @@ namespace TweetLib.Utils.Serialization.Converters {
private ClrTypeConverter() {} 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)) { switch (Type.GetTypeCode(type)) {
case TypeCode.Boolean: case TypeCode.Boolean:
converted = value.ToString(); converted = value!.ToString();
return true; return true;
case TypeCode.Int32: case TypeCode.Int32:
converted = ((int) value).ToString(); // cast required for enums converted = ((int) value!).ToString(); // cast required for enums
return true; return true;
case TypeCode.String: case TypeCode.String:
// ReSharper disable once ConstantConditionalAccessQualifier
converted = value?.ToString(); converted = value?.ToString();
return true; return true;

View File

@@ -2,7 +2,7 @@
namespace TweetLib.Utils.Serialization { namespace TweetLib.Utils.Serialization {
public interface ITypeConverter { 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); bool TryReadType(Type type, string value, out object? converted);
} }
} }

View File

@@ -28,7 +28,7 @@ namespace TweetLib.Utils.Serialization {
break; break;
} }
else { else {
build.Append(data.Substring(index, nextIndex - index)); build.Append(data[index..nextIndex]);
char next = data[nextIndex + 1]; char next = data[nextIndex + 1];
@@ -47,7 +47,7 @@ namespace TweetLib.Utils.Serialization {
} }
} }
return build.Append(data.Substring(index)).ToString(); return build.Append(data[index..]).ToString();
} }
private readonly TypeConverterRegistry converterRegistry; private readonly TypeConverterRegistry converterRegistry;
@@ -114,7 +114,7 @@ namespace TweetLib.Utils.Serialization {
int nextPos = contents.IndexOf(NewLineReal, currentPos); int nextPos = contents.IndexOf(NewLineReal, currentPos);
if (nextPos == -1) { if (nextPos == -1) {
line = contents.Substring(currentPos); line = contents[currentPos..];
currentPos = -1; currentPos = -1;
if (string.IsNullOrEmpty(line)) { if (string.IsNullOrEmpty(line)) {
@@ -133,10 +133,10 @@ namespace TweetLib.Utils.Serialization {
continue; continue;
} }
string property = line.Substring(0, space); string property = line[..space];
string value = UnescapeLine(line.Substring(space + 1)); string value = UnescapeLine(line[(space + 1)..]);
if (props.TryGetValue(property, out PropertyInfo info)) { if (props.TryGetValue(property, out var info)) {
var type = info.PropertyType; var type = info.PropertyType;
var converter = converterRegistry.TryGet(type) ?? ClrTypeConverter.Instance; var converter = converterRegistry.TryGet(type) ?? ClrTypeConverter.Instance;

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using TweetLib.Utils.Static; using TweetLib.Utils.Static;
@@ -91,7 +90,6 @@ namespace TweetLib.Utils.Startup {
return UnlockResult.Success; return UnlockResult.Success;
} }
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
private LockResult DetermineLockingProcessOrFail(Exception originalException) { private LockResult DetermineLockingProcessOrFail(Exception originalException) {
try { try {
int pid; int pid;
@@ -110,7 +108,7 @@ namespace TweetLib.Utils.Startup {
var foundProcess = Process.GetProcessById(pid); var foundProcess = Process.GetProcessById(pid);
using var currentProcess = Process.GetCurrentProcess(); 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); return new LockResult.HasProcess(foundProcess);
} }
else { else {

View File

@@ -40,7 +40,7 @@ namespace TweetLib.Utils.Static {
return null; return null;
} }
return (str.Substring(0, index), str.Substring(index + 1)); return (str[..index], str[(index + 1)..]);
} }
/// <summary> /// <summary>
@@ -49,7 +49,7 @@ namespace TweetLib.Utils.Static {
/// </summary> /// </summary>
public static string ExtractBefore(string str, char search, int startIndex = 0) { public static string ExtractBefore(string str, char search, int startIndex = 0) {
int index = str.IndexOf(search, startIndex); int index = str.IndexOf(search, startIndex);
return index == -1 ? str : str.Substring(0, index); return index == -1 ? str : str[..index];
} }
/// <summary> /// <summary>

View File

@@ -10,10 +10,12 @@ namespace TweetLib.Utils.Static {
private static bool hasMicrosoftBeenBroughtTo2008Yet; private static bool hasMicrosoftBeenBroughtTo2008Yet;
private static bool hasSystemProxyBeenEnabled; private static bool hasSystemProxyBeenEnabled;
private static void EnsureTLS12() { private static void EnsureModernTLS() {
if (!hasMicrosoftBeenBroughtTo2008Yet) { if (!hasMicrosoftBeenBroughtTo2008Yet) {
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; #pragma warning disable CS0618
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11); ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
#pragma warning restore CS0618
hasMicrosoftBeenBroughtTo2008Yet = true; hasMicrosoftBeenBroughtTo2008Yet = true;
} }
} }
@@ -28,7 +30,7 @@ namespace TweetLib.Utils.Static {
} }
public static WebClient NewClient(string? userAgent = null) { public static WebClient NewClient(string? userAgent = null) {
EnsureTLS12(); EnsureModernTLS();
WebClient client = new WebClient(); WebClient client = new WebClient();

Some files were not shown because too many files have changed in this diff Show More