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

Compare commits

..

128 Commits
1.13.6 ... 1.15

Author SHA1 Message Date
746eba185b Release 1.15 2018-07-09 14:06:13 +02:00
82dec92510 Fix wrong screenshot width on high DPI 2018-07-09 14:06:07 +02:00
2edc93df47 Update CefSharp to the newest build 2018-07-09 13:23:13 +02:00
c5a6738199 Fix a missing 'Clear columns' button after re-enabling the clear-columns plugin 2018-07-09 13:20:58 +02:00
dfd578165b Revert custom throttling, it is not ready due to issues w/ notifications 2018-07-09 13:04:55 +02:00
285d400f69 Refactor ScriptLoader to cache files on its own & change method signatures 2018-07-09 12:22:29 +02:00
47a3444ace Remove unnecessary styles and tweak formatting in black theme CSS 2018-07-09 10:21:44 +02:00
0ec227da52 Redesign Edit CSS dialog, add 'Open Dev Tools' button and better editor 2018-07-08 12:57:22 +02:00
f9cf582306 ... 2018-07-08 10:09:44 +02:00
b0883229bf Move HasDevTools to BrowserUtils 2018-07-08 10:06:40 +02:00
31b5d9a4c0 Protect code.js from uncaught errors & add custom 'throw if' statement 2018-07-08 09:53:42 +02:00
6841a78556 Rewrite clear-columns plugin to work w/o reload & fix broken Clear icon 2018-07-08 08:09:29 +02:00
9d2089a8ce Extract markup from introduction.js into an imported file & fix import regex 2018-07-08 06:14:33 +02:00
6d6bb79199 Massive JS formatting refactoring 2018-07-08 06:09:51 +02:00
937c8e22c4 Force UTF-8 in VS Code workspace settings 2018-07-08 05:16:31 +02:00
34c8d44dfe Project tweaks (make update installer on build, stopwatch resource swap, formatting) 2018-07-08 05:03:30 +02:00
2dbf778e56 Change the new DM limit behavior b/c older DMs don't load when scrolled to bottom 2018-07-06 22:09:09 +02:00
873fe2b393 Remove legacy HW acceleration support check & update tooltip 2018-07-06 09:37:25 +02:00
0c53bc6f32 Move icons and images to a separate folder 2018-07-06 07:26:22 +02:00
fa407e150b Add a quick way to import profile from the login page
Closes #228
2018-07-06 07:03:39 +02:00
4e52102c5c Add option for browser throttling w/ default when covered by larger window 2018-07-06 06:26:50 +02:00
0355a5c646 Add VS Code workspace file & fix some warnings 2018-07-06 06:04:10 +02:00
377d9c3554 Make an interface for dialogs to avoid hard-coding them in FormManager 2018-07-06 05:30:12 +02:00
20b1b3c895 Add more safety checks to code.js 2018-07-05 09:05:20 +02:00
41bbe7c51b Decrease amount of loaded DMs to lower performance hit from re-opening DMs 2018-07-05 07:39:26 +02:00
27fa6aefd3 Improve CSS minification 2018-07-05 06:32:45 +02:00
a63c3232da Update CefSharp to 67 (early) 2018-07-05 06:30:41 +02:00
146908a115 Include files for importing in the project to let VS detect changes 2018-07-04 17:27:49 +02:00
48b0f35fee Move template plugin HTML and CSS into a separate file 2018-07-04 17:24:32 +02:00
6c435ebe26 Extract styles from update.js into an imported CSS file 2018-07-04 07:03:48 +02:00
b8d0b721a2 Extract styles from introduction.js into an imported CSS file 2018-07-04 07:00:47 +02:00
a6d5957f46 Extract styles from twitter.js into imported CSS files 2018-07-04 06:55:10 +02:00
9e5f676e23 Move imported files into structured folder for easier handling & tweak syntax 2018-07-04 06:53:31 +02:00
414f3a1f9d Import shared plugin setup code into individual environment files 2018-07-04 06:26:41 +02:00
fedf9c60ee Hack up a simple import system in the build process 2018-07-04 06:25:22 +02:00
b6385d9622 Change unit test namespaces 2018-07-04 03:07:45 +02:00
76d25a712d Move old IO test project in preparation for future refactoring 2018-07-04 03:07:15 +02:00
2c6d935273 Rewrite unit tests for CommandLineArgs into xUnit 2018-07-04 02:45:11 +02:00
830d98a101 Rewrite unit tests for TwoKeyDictionary into xUnit 2018-07-03 21:23:34 +02:00
d7378bd75a Add leading slashes to URL strings in RequestHandlerBrowser 2018-07-02 20:32:23 +02:00
0f41cb9dbc Add response filter to restore global jQuery & unfreeze TweetDeck resources 2018-07-02 20:27:54 +02:00
77bc922d93 Add base class for response filters to safely modify intercepted files 2018-07-02 19:57:24 +02:00
c91b635132 Fix TDPF_createCustomStyle's remove function depending on jQuery 2018-07-01 16:51:57 +02:00
e5521de34a Split icon design files & update 256x256 icon 2018-07-01 15:45:33 +02:00
7c0f8d0f24 Remove temporarily forced update checking 2018-07-01 03:10:47 +02:00
f60d5f650f Add new program and tray icons to show when notifications are muted 2018-06-30 21:28:41 +02:00
ca67f2fe0a Redo app icon background as vector & update all icons 2018-06-30 19:39:16 +02:00
16cce8be1d Remove unnecessary code for disabling TweetDeck update notification 2018-06-30 15:02:46 +02:00
770619d948 Make update notification work without jQuery & optimize slide animation 2018-06-30 14:30:45 +02:00
cfedb7d6b1 Improve logged error message when update checking fails 2018-06-30 13:22:37 +02:00
738557b3a2 Add exception type that adds (potentially sensitive) details to error log 2018-06-30 13:20:25 +02:00
38b01deec1 Make it easier to freeze TweetDeck resources in the future 2018-06-30 11:49:46 +02:00
1a31e69ec9 Release 1.14.4.1 2018-06-29 19:56:00 +02:00
e065983c95 Delay the apocalypse (freeze TweetDeck JS resources, force update checking) 2018-06-29 19:41:02 +02:00
30a169171a Apparently deserializing @TryMyAwesomeApp sometimes causes a hidden crash 2018-06-29 17:10:11 +02:00
8d1900362e Update PostCefUpdate script to remove invalid build configurations (x64, AnyCPU) 2018-06-29 17:05:53 +02:00
e154189de1 Cleanup TweetDuck.csproj and fix names in TestResult unit test 2018-06-28 14:06:03 +02:00
b0f147de24 Fix misaligned 'Add column' icons when using old icon design 2018-06-28 08:10:48 +02:00
979b3548db Release 1.14.4 2018-06-28 07:54:38 +02:00
05d6c578b3 Move InjectedHTML unit tests to xUnit and rename Inject method 2018-06-26 11:19:44 +02:00
a117559063 Minor formatting tweaks 2018-06-26 10:05:53 +02:00
f87c649b09 Fix Twitter experiment causing crash in notifications and subsequent render corruption 2018-06-26 09:49:37 +02:00
6504dc9184 Add unit tests for Result and a few utility methods & fix edge case in StringUtils 2018-06-24 21:41:02 +02:00
25a8ddffd4 Rewrite and tweak existing Core namespace unit tests into xUnit 2018-06-24 19:29:24 +02:00
fa0f9b89cf Remove 'Release' configuration from UnitTests project 2018-06-24 16:16:11 +02:00
4d00a67891 Add a new F# xUnit test project 2018-06-24 16:09:21 +02:00
bd2c43e1f4 Fix analytics not counting applying ROT13 on non-editable text 2018-06-19 21:35:35 +02:00
c7279eaa34 Fix bug with falsely detecting symlinks in plugins if a file/folder doesn't exist 2018-06-19 21:32:21 +02:00
fd523e552c Symlinks/junctions in plugin folders can go to hell 2018-06-13 22:20:10 +02:00
cb877b8b23 Fix broken desktop notifications for retweets with sensitive media 2018-06-10 23:53:01 +02:00
ed1bee8b89 Release 1.14.3 2018-06-10 20:13:05 +02:00
a8e1492056 Push pin icon to the repository 2018-06-08 10:16:58 +02:00
5587216c01 Fix one more case of breaking overlays (account dialog) 2018-06-07 17:48:52 +02:00
86569261ad Add a visual response when hovering filter icons under column header 2018-06-07 14:52:39 +02:00
4a9049c7aa Fix CSS change in dialog overlays breaking some cases 2018-06-07 14:09:35 +02:00
75d60a8182 Work around browser redirection when dragging links into a scrolling column 2018-06-07 13:23:19 +02:00
14d4dc2ed9 Fix more instances of cut off badges 2018-06-06 16:04:15 +02:00
fd0e1740a5 Rename SetLastRightClickInfo bridge method and make it browser-only 2018-06-06 15:11:07 +02:00
70ca890bef Fix not stripping t.co in notifications when dragging & sometimes when copying 2018-06-06 15:10:52 +02:00
b9318dfd8e Minor visual fixes (Edit List modal, set minimum column width) 2018-06-05 04:25:05 +02:00
995642a719 Add support for 1 or 2 columns on screen in edit-design plugin 2018-06-05 03:48:40 +02:00
d14de4ac9e Lower minimum width of browser window & fix modals breaking in small windows 2018-06-05 02:51:21 +02:00
b7f325a241 Minor RequestHandlerBase refactoring 2018-06-05 02:05:04 +02:00
27c55718c2 Release 1.14.2.1 2018-06-04 13:01:44 +02:00
421ff0654b Temporarily work around buggy notification scrolling 2018-06-04 12:35:34 +02:00
ed947458f9 Update fallback HTML in desktop notifications 2018-06-04 12:09:02 +02:00
9cdb20ba84 Fix several broken column types in 'Add column' modal 2018-06-04 11:02:52 +02:00
d8774b735f Release 1.14.2 2018-06-04 08:59:32 +02:00
adcb42695f Update CEF so I don't have to workaround wrong dev tools version
Closes #220
2018-06-01 22:32:44 +02:00
dd77b5bcbb Revert smooth scrolling fix and rewrite horizontal scrolling fix to avoid column jumping 2018-06-01 21:48:26 +02:00
d2445be155 Fix missing GIF previews in tweet screenshots 2018-05-31 03:07:51 +02:00
10254c8af7 Fix tweet screenshots with Aero disabled by making the window visible
Closes #223
2018-05-31 02:58:25 +02:00
d7e830badf Slightly increase default notification height for larger font sizes 2018-05-31 01:11:17 +02:00
b445a3a9b8 Fix broken reply-account advanced selector arguments after a TweetDeck update 2018-05-31 00:52:08 +02:00
97f42ead66 Make it easier to debug screenshots 2018-05-30 19:50:11 +02:00
03730fafb9 ...because I can't be bothered 2018-05-29 15:34:02 +02:00
0be9465dca Fix column icons being hidden by title with clear-columns or edit-design features on 2018-05-29 15:23:41 +02:00
d7f1df4995 Release 1.14.1 2018-05-16 13:07:47 +02:00
3cb0f90706 Prevent an unlikely crash when showing an error message in context menu 2018-05-16 13:05:59 +02:00
a3e9b15a8a Add 'Apply ROT13' to non-editable selections to allow decoding tweets 2018-05-16 12:40:49 +02:00
00bfa68a57 Fix UI issues after recent TweetDeck updates 2018-05-16 07:48:47 +02:00
c311e24f08 Make it easier to update devtools file after updating CEF 2018-05-15 12:42:57 +02:00
1cdd4e4455 Update CefSharp to 66 (early) to fix memory leak 2018-05-15 10:10:03 +02:00
8078c0081a Add a script to fix browser project references after updating CEF 2018-05-15 10:04:08 +02:00
a867e1fc40 Optimize speed and memory usage of build process for app & installers 2018-05-08 20:20:02 +02:00
61da36ac1c Update README.md 2018-05-08 19:37:06 +02:00
720ca2a018 Update README.md 2018-05-08 19:36:08 +02:00
b39c593552 Update README.md 2018-05-08 06:28:07 +02:00
c808952a45 Update README.md 2018-05-08 00:03:08 +02:00
b468d7a766 Update README.md 2018-05-07 20:03:50 +02:00
28578f60be Include plugin files in the project & update README 2018-05-07 19:35:08 +02:00
92a39e2527 Push debug configuration start arguments (-datafolder TweetDuckDebug) 2018-05-07 18:11:56 +02:00
1bce5e4342 Release 1.14 2018-05-07 17:28:34 +02:00
68f586e104 Fix wrong info in the analytics dialog 2018-05-07 15:42:41 +02:00
d27a66202e Remove "Show this thread" in quoted tweets from notifications and screenshots 2018-05-07 03:01:24 +02:00
07de2f450c Fix broken notification scrolling in CEF 65 properly 2018-05-07 02:43:40 +02:00
3c03726634 Remove old and no longer necessary code from update installer 2018-05-06 22:47:58 +02:00
6fb2643063 Slightly increase installer compression level 2018-05-06 22:24:55 +02:00
5eef6c8196 Kill stubborn app processes when installing updates 2018-05-06 21:18:23 +02:00
829c332e13 Add a global function for mustache injection & fix broken clear-columns mustache 2018-05-05 11:47:19 +02:00
47eec14bca Fix freshly broken GIF previews in notifications 2018-05-04 21:07:33 +02:00
e7ee1d6be7 Revert "Fix broken notification scrolling in CEF 65"
This reverts commit 1029ea5840.
2018-05-04 14:00:41 +02:00
e41b5e5ff7 Replace generated license files with just one and include CEF license 2018-05-04 13:30:54 +02:00
ba1bacd08c Fix minor formatting and method modifier issues 2018-05-04 13:04:23 +02:00
1029ea5840 Fix broken notification scrolling in CEF 65 2018-05-04 11:41:20 +02:00
339eaf0195 Fix button appearance in introduction dialog and some plugins 2018-05-03 16:02:44 +02:00
aa1e1549d8 Fix Twitter's broken Cancel button when logging out 2018-05-03 15:04:09 +02:00
1f8ae9ef80 Update CefSharp to 65 (pre01) and fix blank example notification
* Update CefSharp to 65 (pre01)

* Fix blank example notification on first load
2018-05-03 14:05:56 +02:00
130 changed files with 3881 additions and 2282 deletions

View File

@@ -6,6 +6,8 @@ namespace TweetDuck.Configuration{
// public args // public args
public const string ArgDataFolder = "-datafolder"; public const string ArgDataFolder = "-datafolder";
public const string ArgLogging = "-log"; public const string ArgLogging = "-log";
public const string ArgIgnoreGDPR = "-nogdpr";
public const string ArgNotificationScrollWA = "-nscrollwa";
// internal args // internal args
public const string ArgRestart = "-restart"; public const string ArgRestart = "-restart";

View File

@@ -1,28 +1,17 @@
using System; using System;
using System.IO;
using TweetDuck.Data.Serialization; using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class SystemConfig{ sealed class SystemConfig{
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>(); private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
// CONFIGURATION DATA // CONFIGURATION DATA
private bool _hardwareAcceleration = true; public bool HardwareAcceleration { get; set; } = true;
public bool ClearCacheAutomatically { get; set; } = true; public bool ClearCacheAutomatically { get; set; } = true;
public int ClearCacheThreshold { get; set; } = 250; public int ClearCacheThreshold { get; set; } = 250;
// SPECIAL PROPERTIES
public bool HardwareAcceleration{
get => _hardwareAcceleration && IsHardwareAccelerationSupported;
set => _hardwareAcceleration = value;
}
// END OF CONFIG // END OF CONFIG
private readonly string file; private readonly string file;

View File

@@ -51,6 +51,10 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(form.OpenContextMenu); form.InvokeAsyncSafe(form.OpenContextMenu);
} }
public void OpenProfileImport(){
form.InvokeAsyncSafe(form.OpenProfileImport);
}
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){ public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
form.OnIntroductionClosed(showGuide, allowDataCollection); form.OnIntroductionClosed(showGuide, allowDataCollection);
@@ -64,6 +68,10 @@ namespace TweetDuck.Core.Bridge{
}); });
} }
public void SetRightClickedLink(string type, string url){
ContextInfo.SetLink(type, url);
}
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages); ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
} }
@@ -99,10 +107,6 @@ namespace TweetDuck.Core.Bridge{
// Global // Global
public void SetLastRightClickInfo(string type, string url){
ContextInfo.SetLink(type, url);
}
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
notification.InvokeAsyncSafe(() => { notification.InvokeAsyncSafe(() => {
form.OnTweetNotification(); form.OnTweetNotification();

View File

@@ -39,17 +39,17 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor; this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
this.ClientSize = new System.Drawing.Size(400, 386); this.ClientSize = new System.Drawing.Size(1008, 730);
this.Icon = Properties.Resources.icon; this.Icon = Properties.Resources.icon;
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation; this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
this.MinimumSize = new System.Drawing.Size(416, 424); this.MinimumSize = new System.Drawing.Size(348, 424);
this.Name = "FormBrowser"; this.Name = "FormBrowser";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Activated += new System.EventHandler(this.FormBrowser_Activated); this.Activated += new System.EventHandler(this.FormBrowser_Activated);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd); this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
this.Resize += new System.EventHandler(this.FormBrowser_Resize); this.Resize += new System.EventHandler(this.FormBrowser_Resize);
this.ResumeLayout(false); this.ResumeLayout(false);

View File

@@ -5,11 +5,13 @@ using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot; using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Analytics; using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
@@ -72,7 +74,7 @@ namespace TweetDuck.Core{
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification)); this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
this.contextMenu = ContextMenuBrowser.CreateMenu(this); this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.plugins.Register(browser, PluginEnvironment.Browser, true); this.plugins.Register(browser, PluginEnvironment.Browser, this, true);
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
@@ -95,7 +97,11 @@ namespace TweetDuck.Core{
this.trayIcon.ClickClose += trayIcon_ClickClose; this.trayIcon.ClickClose += trayIcon_ClickClose;
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged; Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
UpdateTrayIcon(); UpdateTray();
if (Config.MuteNotifications){
UpdateFormIcon();
}
this.updates = new UpdateHandler(browser, Program.InstallerPath); this.updates = new UpdateHandler(browser, Program.InstallerPath);
this.updates.CheckFinished += updates_CheckFinished; this.updates.CheckFinished += updates_CheckFinished;
@@ -127,7 +133,11 @@ namespace TweetDuck.Core{
isLoaded = true; isLoaded = true;
} }
private void UpdateTrayIcon(){ private void UpdateFormIcon(){ // TODO fix to show icon in taskbar too
Icon = Config.MuteNotifications ? Properties.Resources.icon_muted : Properties.Resources.icon;
}
private void UpdateTray(){
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon(); trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
} }
@@ -202,18 +212,19 @@ namespace TweetDuck.Core{
} }
private void Config_MuteToggled(object sender, EventArgs e){ private void Config_MuteToggled(object sender, EventArgs e){
UpdateFormIcon();
AnalyticsFile.NotificationMutes.Trigger(); AnalyticsFile.NotificationMutes.Trigger();
} }
private void Config_TrayBehaviorChanged(object sender, EventArgs e){ private void Config_TrayBehaviorChanged(object sender, EventArgs e){
UpdateTrayIcon(); UpdateTray();
} }
private void trayIcon_ClickRestore(object sender, EventArgs e){ private void trayIcon_ClickRestore(object sender, EventArgs e){
Show(); Show();
RestoreWindow(); RestoreWindow();
Activate(); Activate();
UpdateTrayIcon(); UpdateTray();
} }
private void trayIcon_ClickClose(object sender, EventArgs e){ private void trayIcon_ClickClose(object sender, EventArgs e){
@@ -360,6 +371,10 @@ namespace TweetDuck.Core{
AnalyticsFile.UsedROT13.Trigger(); AnalyticsFile.UsedROT13.Trigger();
} }
public void OpenDevTools(){
browser.OpenDevTools();
}
// callback handlers // callback handlers
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){ public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
@@ -447,6 +462,18 @@ namespace TweetDuck.Core{
} }
} }
public void OpenProfileImport(){
FormManager.TryFind<FormSettings>()?.Close();
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins, true)){
if (dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){
BrowserProcessHandler.UpdatePrefs();
FormManager.TryFind<FormPlugins>()?.Close();
plugins.Reload(); // also reloads the browser
}
}
}
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
if (Config.EnableTrayHighlight && !ContainsFocus){ if (Config.EnableTrayHighlight && !ContainsFocus){
trayIcon.HasNotifications = true; trayIcon.HasNotifications = true;

View File

@@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Other;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
static class FormManager{ static class FormManager{
@@ -18,12 +17,14 @@ namespace TweetDuck.Core{
else return false; else return false;
} }
public static bool HasAnyDialogs => Application.OpenForms.OfType<IAppDialog>().Any();
public static void CloseAllDialogs(){ public static void CloseAllDialogs(){
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){ foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){
if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){ ((Form)dialog).Close();
form.Close();
}
} }
} }
public interface IAppDialog{}
} }
} }

View File

@@ -15,8 +15,6 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality; private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500; private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
@@ -28,6 +26,7 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506; private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508; private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508;
private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509;
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
protected ContextInfo.LinkInfo LastLink { get; private set; } protected ContextInfo.LinkInfo LastLink { get; private set; }
@@ -57,6 +56,8 @@ namespace TweetDuck.Core.Handling{
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
model.AddItem(MenuSearchInBrowser, "Search in browser"); model.AddItem(MenuSearchInBrowser, "Search in browser");
model.AddSeparator(); model.AddSeparator();
model.AddItem(MenuReadApplyROT13, "Apply ROT13");
model.AddSeparator();
} }
bool hasTweetImage = LastLink.IsImage; bool hasTweetImage = LastLink.IsImage;
@@ -126,7 +127,7 @@ namespace TweetDuck.Core.Handling{
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
break; break;
case MenuViewImage: case MenuViewImage: {
void ViewImage(string path){ void ViewImage(string path){
string ext = Path.GetExtension(path); string ext = Path.GetExtension(path);
@@ -141,11 +142,12 @@ namespace TweetDuck.Core.Handling{
string url = LastLink.GetMediaSource(parameters); string url = LastLink.GetMediaSource(parameters);
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName()); string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
control.InvokeAsyncSafe(() => {
if (File.Exists(file)){ if (File.Exists(file)){
ViewImage(file); ViewImage(file);
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger); analytics.AnalyticsFile.ViewedImages.Trigger();
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => { BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
ViewImage(file); ViewImage(file);
@@ -153,25 +155,47 @@ namespace TweetDuck.Core.Handling{
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
}); });
} }
});
break; break;
}
case MenuSaveMedia: case MenuSaveMedia: {
if (LastLink.IsVideo){ bool isVideo = LastLink.IsVideo;
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger); string url = LastLink.GetMediaSource(parameters);
TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault()); string username = LastChirp.Authors.LastOrDefault();
control.InvokeAsyncSafe(() => {
if (isVideo){
TwitterUtils.DownloadVideo(url, username);
analytics.AnalyticsFile.DownloadedVideos.Trigger();
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger); TwitterUtils.DownloadImage(url, username, ImageQuality);
TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality); analytics.AnalyticsFile.DownloadedImages.Trigger();
}
});
break;
} }
break; case MenuSaveTweetImages: {
string[] urls = LastChirp.Images;
string username = LastChirp.Authors.LastOrDefault();
control.InvokeAsyncSafe(() => {
TwitterUtils.DownloadImages(urls, username, ImageQuality);
analytics.AnalyticsFile.DownloadedImages.Trigger();
});
case MenuSaveTweetImages:
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
break; break;
}
case MenuReadApplyROT13:
string selection = parameters.SelectionText;
control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK));
control.InvokeAsyncSafe(analytics.AnalyticsFile.UsedROT13.Trigger);
return true;
case MenuSearchInBrowser: case MenuSearchInBrowser:
string query = parameters.SelectionText; string query = parameters.SelectionText;
@@ -195,15 +219,15 @@ namespace TweetDuck.Core.Handling{
return false; return false;
} }
protected void DeselectAll(IFrame frame){ protected static void DeselectAll(IFrame frame){
ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect");
} }
protected void OpenBrowser(Control control, string url){ protected static void OpenBrowser(Control control, string url){
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
} }
protected void SetClipboardText(Control control, string text){ protected static void SetClipboardText(Control control, string text){
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
} }
@@ -212,8 +236,11 @@ namespace TweetDuck.Core.Handling{
} }
protected static void AddDebugMenuItems(IMenuModel model){ protected static void AddDebugMenuItems(IMenuModel model){
if (BrowserUtils.HasDevTools){
AddSeparator(model);
model.AddItem(MenuOpenDevTools, "Open dev tools"); model.AddItem(MenuOpenDevTools, "Open dev tools");
} }
}
protected static void RemoveSeparatorIfLast(IMenuModel model){ protected static void RemoveSeparatorIfLast(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){ if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614; private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
private const CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615; private const CefMenuCommand MenuWriteApplyROT13 = (CefMenuCommand)26615;
private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616; private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616;
private const string TitleReloadBrowser = "Reload browser"; private const string TitleReloadBrowser = "Reload browser";
@@ -44,7 +44,7 @@ namespace TweetDuck.Core.Handling{
if (isSelecting){ if (isSelecting){
if (isEditing){ if (isEditing){
model.AddSeparator(); model.AddSeparator();
model.AddItem(MenuInputApplyROT13, "Apply ROT13"); model.AddItem(MenuWriteApplyROT13, "Apply ROT13");
} }
model.AddSeparator(); model.AddSeparator();
@@ -84,11 +84,8 @@ namespace TweetDuck.Core.Handling{
globalMenu.AddItem(MenuPlugins, TitlePlugins); globalMenu.AddItem(MenuPlugins, TitlePlugins);
globalMenu.AddItem(MenuAbout, TitleAboutProgram); globalMenu.AddItem(MenuAbout, TitleAboutProgram);
if (HasDevTools){
globalMenu.AddSeparator();
AddDebugMenuItems(globalMenu); AddDebugMenuItems(globalMenu);
} }
}
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
@@ -141,7 +138,7 @@ namespace TweetDuck.Core.Handling{
SetClipboardText(form, LastChirp.QuoteUrl); SetClipboardText(form, LastChirp.QuoteUrl);
return true; return true;
case MenuInputApplyROT13: case MenuWriteApplyROT13:
form.InvokeAsyncSafe(form.ApplyROT13); form.InvokeAsyncSafe(form.ApplyROT13);
return true; return true;

View File

@@ -8,11 +8,7 @@ namespace TweetDuck.Core.Handling{
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear(); model.Clear();
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (HasDevTools){
AddSeparator(model);
AddDebugMenuItems(model); AddDebugMenuItems(model);
} }
} }
} }
}

View File

@@ -47,11 +47,7 @@ namespace TweetDuck.Core.Handling{
} }
} }
if (HasDevTools){
AddSeparator(model);
AddDebugMenuItems(model); AddDebugMenuItems(model);
}
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {

View File

@@ -1,13 +1,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using CefSharp; using CefSharp;
using CefSharp.Enums;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class DragHandlerBrowser : IDragHandler{ sealed class DragHandlerBrowser : IDragHandler{
private readonly RequestHandlerBrowser requestHandler;
public DragHandlerBrowser(RequestHandlerBrowser requestHandler){
this.requestHandler = requestHandler;
}
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){ public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
void TriggerDragStart(string type, string data = null){ void TriggerDragStart(string type, string data = null){
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data); browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
} }
requestHandler.BlockNextUserNavUrl = dragData.LinkUrl; // empty if not a link
if (dragData.IsLink){ if (dragData.IsLink){
TriggerDragStart("link", dragData.LinkUrl); TriggerDragStart("link", dragData.LinkUrl);
} }

View File

@@ -0,0 +1,72 @@
using System;
using System.IO;
using System.Text;
using CefSharp;
namespace TweetDuck.Core.Handling.Filters{
abstract class ResponseFilterBase : IResponseFilter{
private enum State{
Reading, Writing, Done
}
private readonly Encoding encoding;
private byte[] responseData;
private State state;
private int offset;
protected ResponseFilterBase(int totalBytes, Encoding encoding){
this.responseData = new byte[totalBytes];
this.encoding = encoding;
this.state = State.Reading;
}
FilterStatus IResponseFilter.Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten){
int responseLength = responseData.Length;
if (state == State.Reading){
int bytesToRead = Math.Min(responseLength-offset, (int)Math.Min(dataIn.Length, int.MaxValue));
dataIn.Read(responseData, offset, bytesToRead);
offset += bytesToRead;
dataInRead = bytesToRead;
dataOutWritten = 0;
if (offset >= responseLength){
responseData = encoding.GetBytes(ProcessResponse(encoding.GetString(responseData)));
state = State.Writing;
offset = 0;
}
return FilterStatus.NeedMoreData;
}
else if (state == State.Writing){
int bytesToWrite = Math.Min(responseLength-offset, (int)Math.Min(dataOut.Length, int.MaxValue));
if (bytesToWrite > 0){
dataOut.Write(responseData, offset, bytesToWrite);
offset += bytesToWrite;
}
dataOutWritten = bytesToWrite;
dataInRead = 0;
if (offset < responseLength){
return FilterStatus.NeedMoreData;
}
else{
state = State.Done;
return FilterStatus.Done;
}
}
else{
throw new InvalidOperationException("This resource filter cannot be reused.");
}
}
public abstract bool InitFilter();
protected abstract string ProcessResponse(string text);
public abstract void Dispose();
}
}

View File

@@ -0,0 +1,20 @@
using System.Text;
using System.Text.RegularExpressions;
namespace TweetDuck.Core.Handling.Filters{
sealed class ResponseFilterVendor : ResponseFilterBase{
private static readonly Regex RegexRestoreJQuery = new Regex(@"(\w+)\.fn=\1\.prototype", RegexOptions.Compiled);
public ResponseFilterVendor(int totalBytes) : base(totalBytes, Encoding.UTF8){}
public override bool InitFilter(){
return true;
}
protected override string ProcessResponse(string text){
return RegexRestoreJQuery.Replace(text, "window.$$=$1;$&", 1);
}
public override void Dispose(){}
}
}

View File

@@ -7,16 +7,14 @@ using CefSharp;
namespace TweetDuck.Core.Handling.General{ namespace TweetDuck.Core.Handling.General{
sealed class FileDialogHandler : IDialogHandler{ sealed class FileDialogHandler : IDialogHandler{
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){ public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask; if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple){
if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){
string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter)); string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter));
using(OpenFileDialog dialog = new OpenFileDialog{ using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
DereferenceLinks = true, DereferenceLinks = true,
Multiselect = dialogType == CefFileDialogMode.OpenMultiple, Multiselect = mode == CefFileDialogMode.OpenMultiple,
Title = "Open Files", Title = "Open Files",
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*" Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
}){ }){

View File

@@ -2,15 +2,22 @@
using CefSharp; using CefSharp;
using CefSharp.Handler; using CefSharp.Handler;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class RequestHandlerBase : DefaultRequestHandler{ class RequestHandlerBase : DefaultRequestHandler{
private readonly bool autoReload;
public RequestHandlerBase(bool autoReload){
this.autoReload = autoReload;
}
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){ public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl); return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
} }
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (ContextMenuBase.HasDevTools){ if (BrowserUtils.HasDevTools){
NameValueCollection headers = request.Headers; NameValueCollection headers = request.Headers;
headers.Remove("x-devtools-emulate-network-conditions-client-id"); headers.Remove("x-devtools-emulate-network-conditions-client-id");
request.Headers = headers; request.Headers = headers;
@@ -18,5 +25,11 @@ namespace TweetDuck.Core.Handling{
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
} }
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
if (autoReload){
browser.Reload();
}
}
} }
} }

View File

@@ -1,11 +1,21 @@
using CefSharp; // Uncomment to force TweetDeck to load a predefined version of the vendor/bundle scripts
// #define FREEZE_TWEETDECK_SCRIPTS
using System.Collections.Specialized;
using CefSharp;
using TweetDuck.Core.Handling.Filters;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
#if FREEZE_TWEETDECK_SCRIPTS
using System.Collections.Generic;
using System.Text.RegularExpressions;
#endif
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class RequestHandlerBrowser : RequestHandlerBase{ sealed class RequestHandlerBrowser : RequestHandlerBase{
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ public string BlockNextUserNavUrl { get; set; }
browser.Reload();
} public RequestHandlerBrowser() : base(true){}
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){ if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
@@ -16,13 +26,71 @@ namespace TweetDuck.Core.Handling{
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
} }
public override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){
if (userGesture && request.TransitionType == TransitionType.LinkClicked){
bool block = request.Url == BlockNextUserNavUrl;
BlockNextUserNavUrl = string.Empty;
return block;
}
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
}
#if FREEZE_TWEETDECK_SCRIPTS
private static readonly Regex TweetDeckScriptUrl = new Regex(@"/dist/(.*?)\.(.*?)\.js$", RegexOptions.Compiled);
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(2){
{ "vendor", "942c0a20e8" },
{ "bundle", "1bd75b5854" }
};
#endif
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Image && request.Url.Contains("backgrounds/spinner_blue")){ if (request.ResourceType == ResourceType.Image && request.Url.Contains("/backgrounds/spinner_blue")){
request.Url = TwitterUtils.LoadingSpinner.Url; request.Url = TwitterUtils.LoadingSpinner.Url;
return true; return true;
} }
#if FREEZE_TWEETDECK_SCRIPTS
else if (request.ResourceType == ResourceType.Script){
Match match = TweetDeckScriptUrl.Match(request.Url);
if (match.Success && TweetDeckHashes.TryGetValue(match.Groups[1].Value, out string hash)){
if (match.Groups[2].Value == hash){
System.Diagnostics.Debug.WriteLine($"accepting {request.Url}");
}
else{
System.Diagnostics.Debug.WriteLine($"rewriting {request.Url} to {hash}");
request.Url = TweetDeckScriptUrl.Replace(request.Url, "/dist/$1."+hash+".js");
return true;
}
}
}
#endif
return base.OnResourceResponse(browserControl, browser, frame, request, response); return base.OnResourceResponse(browserControl, browser, frame, request, response);
} }
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("/dist/vendor")){
NameValueCollection headers = response.ResponseHeaders;
if (int.TryParse(headers["x-ton-expected-size"], out int totalBytes)){
return new ResponseFilterVendor(totalBytes);
}
#if DEBUG
else{
System.Diagnostics.Debug.WriteLine($"Missing uncompressed size header in {request.Url}");
foreach(string key in headers){
System.Diagnostics.Debug.WriteLine($" {key}: {headers[key]}");
}
System.Diagnostics.Debugger.Break();
}
#endif
}
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
}
} }
} }

View File

@@ -1,4 +1,6 @@
using System.Windows.Forms; using System;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Resources; using TweetDuck.Resources;
@@ -21,10 +23,14 @@ namespace TweetDuck.Core.Notification.Example{
} }
} }
public event EventHandler Ready;
private readonly TweetNotification exampleNotification; private readonly TweetNotification exampleNotification;
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true)?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty; browser.LoadingStateChanged += browser_LoadingStateChanged;
string exampleTweetHTML = ScriptLoader.LoadResourceSilent("pages/example.html")?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty;
#if DEBUG #if DEBUG
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>"); exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
@@ -33,6 +39,13 @@ namespace TweetDuck.Core.Notification.Example{
exampleNotification = TweetNotification.Example(exampleTweetHTML, 176); exampleNotification = TweetNotification.Example(exampleTweetHTML, 176);
} }
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
Ready?.Invoke(this, EventArgs.Empty);
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
public override void HideNotification(){ public override void HideNotification(){
Location = ControlExtensions.InvisibleLocation; Location = ControlExtensions.InvisibleLocation;
} }

View File

@@ -1,6 +1,4 @@
using CefSharp; using CefSharp.WinForms;
using CefSharp.WinForms;
using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
@@ -65,7 +63,7 @@ namespace TweetDuck.Core.Notification{
} }
} }
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation; protected bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
protected virtual bool CanDragWindow => true; protected virtual bool CanDragWindow => true;
public new Point Location{ public new Point Location{
@@ -109,15 +107,13 @@ namespace TweetDuck.Core.Notification{
public string CurrentQuoteUrl => currentNotification?.QuoteUrl; public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId); public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
public bool IsPaused => pauseCounter > 0;
protected bool IsPaused => pauseCounter > 0;
protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position)); protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position));
public bool FreezeTimer { get; set; } public bool FreezeTimer { get; set; }
public bool ContextMenuOpen { get; set; } public bool ContextMenuOpen { get; set; }
public event EventHandler Initialized;
protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){ protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
InitializeComponent(); InitializeComponent();
@@ -128,12 +124,11 @@ namespace TweetDuck.Core.Notification{
MenuHandler = new ContextMenuNotification(this, enableContextMenu), MenuHandler = new ContextMenuNotification(this, enableContextMenu),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBase() RequestHandler = new RequestHandlerBase(false)
}; };
this.browser.Dock = DockStyle.None; this.browser.Dock = DockStyle.None;
this.browser.ClientSize = ClientSize; this.browser.ClientSize = ClientSize;
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
browser.SetupResourceHandler(TweetNotification.AppLogo); browser.SetupResourceHandler(TweetNotification.AppLogo);
@@ -165,12 +160,6 @@ namespace TweetDuck.Core.Notification{
Close(); Close();
} }
private void browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized){
Initialized?.Invoke(this, EventArgs.Empty);
}
}
// notification methods // notification methods
public virtual void HideNotification(){ public virtual void HideNotification(){
@@ -196,7 +185,7 @@ namespace TweetDuck.Core.Notification{
} }
protected virtual string GetTweetHTML(TweetNotification tweet){ protected virtual string GetTweetHTML(TweetNotification tweet){
return tweet.GenerateHtml(IsCursorOverBrowser ? "td-notification td-hover" : "td-notification"); return tweet.GenerateHtml(IsCursorOverBrowser ? "td-notification td-hover" : "td-notification", this);
} }
protected virtual void LoadTweet(TweetNotification tweet){ protected virtual void LoadTweet(TweetNotification tweet){

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
@@ -14,11 +15,6 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{ abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{
private const string NotificationScriptFile = "notification.js";
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
private readonly PluginManager plugins; private readonly PluginManager plugins;
private readonly int timerBarHeight; private readonly int timerBarHeight;
@@ -65,7 +61,7 @@ namespace TweetDuck.Core.Notification{
get{ get{
switch(Program.UserConfig.NotificationSize){ switch(Program.UserConfig.NotificationSize){
default: default:
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel)); return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel));
case TweetNotification.Size.Custom: case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Height; return Program.UserConfig.CustomNotificationSize.Height;
@@ -87,7 +83,7 @@ namespace TweetDuck.Core.Notification{
browser.LoadingStateChanged += Browser_LoadingStateChanged; browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd; browser.FrameLoadEnd += Browser_FrameLoadEnd;
plugins.Register(this, PluginEnvironment.Notification); plugins.Register(this, PluginEnvironment.Notification, this);
mouseHookDelegate = MouseHookProc; mouseHookDelegate = MouseHookProc;
Disposed += (sender, args) => StopMouseHook(true); Disposed += (sender, args) => StopMouseHook(true);
@@ -105,7 +101,7 @@ namespace TweetDuck.Core.Notification{
browser.FrameLoadEnd += (sender, args) => { browser.FrameLoadEnd += (sender, args) => {
IFrame frame = args.Frame; IFrame frame = args.Frame;
if (frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ if (frame.IsMain && browser.Address != "about:blank"){
callback(frame); callback(frame);
} }
}; };
@@ -136,7 +132,14 @@ namespace TweetDuck.Core.Notification{
int eventType = wParam.ToInt32(); int eventType = wParam.ToInt32();
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){ if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
if (Arguments.HasFlag(Arguments.ArgNotificationScrollWA)){
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01);
browser.ExecuteScriptAsync("window.scrollBy", 0, -Math.Round(delta/0.72));
}
else{
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None); browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
}
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
@@ -186,9 +189,9 @@ namespace TweetDuck.Core.Notification{
} }
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ if (e.Frame.IsMain && browser.Address != "about:blank"){
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification)); e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier); ScriptLoader.ExecuteScript(e.Frame, ScriptLoader.LoadResource("notification.js", this), "root:notification");
} }
} }
@@ -255,7 +258,7 @@ namespace TweetDuck.Core.Notification{
string html = base.GetTweetHTML(tweet); string html = base.GetTweetHTML(tweet);
foreach(InjectedHTML injection in plugins.NotificationInjections){ foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.Inject(html); html = injection.InjectInto(html);
} }
return html; return html;

View File

@@ -20,6 +20,8 @@ namespace TweetDuck.Core.Notification.Screenshot{
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){ public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){
this.plugins = pluginManager; this.plugins = pluginManager;
int realWidth = BrowserUtils.Scale(width, DpiScale);
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback)); browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
browser.LoadingStateChanged += (sender, args) => { browser.LoadingStateChanged += (sender, args) => {
@@ -27,7 +29,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
return; return;
} }
string script = ScriptLoader.LoadResource("screenshot.js", true); string script = ScriptLoader.LoadResourceSilent("screenshot.js");
if (script == null){ if (script == null){
this.InvokeAsyncSafe(callback); this.InvokeAsyncSafe(callback);
@@ -35,19 +37,19 @@ namespace TweetDuck.Core.Notification.Screenshot{
} }
using(IFrame frame = args.Browser.MainFrame){ using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()), "screenshot"); ScriptLoader.ExecuteScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
} }
}; };
SetNotificationSize(width, 1024); SetNotificationSize(realWidth, 1024);
LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty)); LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty));
} }
protected override string GetTweetHTML(TweetNotification tweet){ protected override string GetTweetHTML(TweetNotification tweet){
string html = tweet.GenerateHtml("td-screenshot"); string html = tweet.GenerateHtml("td-screenshot", this);
foreach(InjectedHTML injection in plugins.NotificationInjections){ foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.Inject(html); html = injection.InjectInto(html);
} }
return html; return html;
@@ -69,6 +71,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
} }
} }
if (!WindowsUtils.IsAeroEnabled){
MoveToVisibleLocation(); // TODO make this look nicer I guess
}
IntPtr context = NativeMethods.GetDC(this.Handle); IntPtr context = NativeMethods.GetDC(this.Handle);
if (context == IntPtr.Zero){ if (context == IntPtr.Zero){

View File

@@ -27,6 +27,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
#if GEN_SCREENSHOT_FRAMES #if GEN_SCREENSHOT_FRAMES
private readonly Timer debugger; private readonly Timer debugger;
private int frameCounter; private int frameCounter;
public const int WaitFrames = 60;
#else
public const int WaitFrames = 5;
#endif #endif
private FormNotificationScreenshotable screenshot; private FormNotificationScreenshotable screenshot;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Text; using System.Text;
using System.Windows.Forms;
using CefSharp; using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Data; using TweetDuck.Data;
@@ -7,8 +8,7 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class TweetNotification{ sealed class TweetNotification{
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty;
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png")); public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
public static TweetNotification Example(string html, int characters){ public static TweetNotification Example(string html, int characters){
@@ -53,11 +53,11 @@ namespace TweetDuck.Core.Notification{
return 2000+Math.Max(1000, value*characters); return 2000+Math.Max(1000, value*characters);
} }
public string GenerateHtml(string bodyClasses){ public string GenerateHtml(string bodyClasses, Control sync){
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
build.Append("<!DOCTYPE html>"); build.Append("<!DOCTYPE html>");
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout); build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(ScriptLoader.LoadResource("styles/notification.css", sync) ?? string.Empty).Append("</style>");
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){ if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
@@ -70,7 +70,7 @@ namespace TweetDuck.Core.Notification{
build.Append(' ').Append(bodyClasses); build.Append(' ').Append(bodyClasses);
} }
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;height:auto!important;overflow:initial!important;'>"); build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>");
build.Append(html); build.Append(html);
build.Append("</div></body>"); build.Append("</div></body>");
build.Append("</html>"); build.Append("</html>");

View File

@@ -8,7 +8,6 @@ using TweetDuck.Configuration;
using System.Linq; using System.Linq;
using System.Management; using System.Management;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
@@ -22,7 +21,7 @@ namespace TweetDuck.Core.Other.Analytics{
return new AnalyticsReport{ return new AnalyticsReport{
{ "App Version" , Program.VersionTag }, { "App Version" , Program.VersionTag },
{ "App Type" , Program.IsPortable ? "portable" : "installed" }, { "App Type" , Program.IsPortable ? "portable" : "installed" },
{ "App Dev Tools" , Bool(ContextMenuBase.HasDevTools) }, { "App Dev Tools" , Bool(BrowserUtils.HasDevTools) },
0, 0,
{ "System Name" , SystemName }, { "System Name" , SystemName },
{ "System Edition" , SystemEdition }, { "System Edition" , SystemEdition },
@@ -42,7 +41,6 @@ namespace TweetDuck.Core.Other.Analytics{
{ "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) }, { "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) },
0, 0,
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) }, { "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
{ "Switch Account Selectors" , Bool(false) }, // TODO remove in next major update
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) }, { "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) }, { "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) }, { "Best Image Quality" , Bool(UserConfig.BestImageQuality) },

View File

@@ -5,7 +5,7 @@ using System.Windows.Forms;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormAbout : Form{ sealed partial class FormAbout : Form, FormManager.IAppDialog{
private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki"; private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki";
private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues"; private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues";

View File

@@ -12,7 +12,7 @@ using TweetDuck.Data;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormGuide : Form{ sealed partial class FormGuide : Form, FormManager.IAppDialog{
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/"; private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?"; private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
@@ -69,7 +69,7 @@ namespace TweetDuck.Core.Other{
MenuHandler = new ContextMenuGuide(owner), MenuHandler = new ContextMenuGuide(owner),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser() RequestHandler = new RequestHandlerBase(true)
}; };
browser.LoadingStateChanged += browser_LoadingStateChanged; browser.LoadingStateChanged += browser_LoadingStateChanged;

View File

@@ -7,7 +7,7 @@ using TweetDuck.Plugins;
using TweetDuck.Plugins.Controls; using TweetDuck.Plugins.Controls;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormPlugins : Form{ sealed partial class FormPlugins : Form, FormManager.IAppDialog{
private readonly PluginManager pluginManager; private readonly PluginManager pluginManager;
public FormPlugins(){ public FormPlugins(){

View File

@@ -13,7 +13,7 @@ using TweetDuck.Plugins;
using TweetDuck.Updates; using TweetDuck.Updates;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormSettings : Form{ sealed partial class FormSettings : Form, FormManager.IAppDialog{
private readonly FormBrowser browser; private readonly FormBrowser browser;
private readonly PluginManager plugins; private readonly PluginManager plugins;
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other{
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins))); AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification)); AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification));
AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins)); AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS)); AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools));
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]); SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
} }

View File

@@ -63,10 +63,10 @@
this.labelInfo.Location = new System.Drawing.Point(12, 9); this.labelInfo.Location = new System.Drawing.Point(12, 9);
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.labelInfo.Name = "labelInfo"; this.labelInfo.Name = "labelInfo";
this.labelInfo.Size = new System.Drawing.Size(431, 30); this.labelInfo.Size = new System.Drawing.Size(434, 30);
this.labelInfo.TabIndex = 0; this.labelInfo.TabIndex = 0;
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly twice a week.\r" + this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly every 14 days." +
"\nSome numbers in the report were made imprecise on purpose."; "\r\nSome numbers in the report were made imprecise on purpose.";
// //
// DialogSettingsAnalytics // DialogSettingsAnalytics
// //

View File

@@ -27,33 +27,30 @@
this.textBoxBrowserCSS = new System.Windows.Forms.TextBox(); this.textBoxBrowserCSS = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button();
this.btnApply = new System.Windows.Forms.Button(); this.btnApply = new System.Windows.Forms.Button();
this.splitContainer = new System.Windows.Forms.SplitContainer();
this.labelBrowser = new System.Windows.Forms.Label();
this.labelNotification = new System.Windows.Forms.Label();
this.textBoxNotificationCSS = new System.Windows.Forms.TextBox(); this.textBoxNotificationCSS = new System.Windows.Forms.TextBox();
this.labelWarning = new System.Windows.Forms.Label(); this.btnOpenDevTools = new System.Windows.Forms.Button();
this.btnOpenWiki = new System.Windows.Forms.Button();
this.timerTestBrowser = new System.Windows.Forms.Timer(this.components); this.timerTestBrowser = new System.Windows.Forms.Timer(this.components);
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.tabPanel = new System.Windows.Forms.TabControl();
this.splitContainer.Panel1.SuspendLayout(); this.tabPageBrowser = new System.Windows.Forms.TabPage();
this.splitContainer.Panel2.SuspendLayout(); this.tabPageNotification = new System.Windows.Forms.TabPage();
this.splitContainer.SuspendLayout(); this.tabPanel.SuspendLayout();
this.tabPageBrowser.SuspendLayout();
this.tabPageNotification.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// textBoxBrowserCSS // textBoxBrowserCSS
// //
this.textBoxBrowserCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.textBoxBrowserCSS.Dock = System.Windows.Forms.DockStyle.Fill;
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16); this.textBoxBrowserCSS.Location = new System.Drawing.Point(3, 3);
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
this.textBoxBrowserCSS.Multiline = true; this.textBoxBrowserCSS.Multiline = true;
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS"; this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxBrowserCSS.Size = new System.Drawing.Size(378, 251); this.textBoxBrowserCSS.Size = new System.Drawing.Size(426, 332);
this.textBoxBrowserCSS.TabIndex = 1; this.textBoxBrowserCSS.TabIndex = 0;
this.textBoxBrowserCSS.WordWrap = false; this.textBoxBrowserCSS.WordWrap = false;
this.textBoxBrowserCSS.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxCSS_KeyDown);
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp); this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
// //
// btnCancel // btnCancel
@@ -61,7 +58,7 @@
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true; this.btnCancel.AutoSize = true;
this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btnCancel.Location = new System.Drawing.Point(657, 285); this.btnCancel.Location = new System.Drawing.Point(337, 384);
this.btnCancel.Name = "btnCancel"; this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCancel.Size = new System.Drawing.Size(57, 25); this.btnCancel.Size = new System.Drawing.Size(57, 25);
@@ -75,7 +72,7 @@
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnApply.AutoSize = true; this.btnApply.AutoSize = true;
this.btnApply.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnApply.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btnApply.Location = new System.Drawing.Point(720, 285); this.btnApply.Location = new System.Drawing.Point(400, 384);
this.btnApply.Name = "btnApply"; this.btnApply.Name = "btnApply";
this.btnApply.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); this.btnApply.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnApply.Size = new System.Drawing.Size(52, 25); this.btnApply.Size = new System.Drawing.Size(52, 25);
@@ -84,117 +81,94 @@
this.btnApply.UseVisualStyleBackColor = true; this.btnApply.UseVisualStyleBackColor = true;
this.btnApply.Click += new System.EventHandler(this.btnApply_Click); this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
// //
// splitContainer
//
this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.splitContainer.Location = new System.Drawing.Point(12, 12);
this.splitContainer.Name = "splitContainer";
//
// splitContainer.Panel1
//
this.splitContainer.Panel1.Controls.Add(this.labelBrowser);
this.splitContainer.Panel1.Controls.Add(this.textBoxBrowserCSS);
this.splitContainer.Panel1MinSize = 64;
//
// splitContainer.Panel2
//
this.splitContainer.Panel2.Controls.Add(this.labelNotification);
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
this.splitContainer.Panel2MinSize = 64;
this.splitContainer.Size = new System.Drawing.Size(760, 267);
this.splitContainer.SplitterDistance = 378;
this.splitContainer.SplitterWidth = 5;
this.splitContainer.TabIndex = 0;
//
// labelBrowser
//
this.labelBrowser.AutoSize = true;
this.labelBrowser.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.labelBrowser.Location = new System.Drawing.Point(-3, 0);
this.labelBrowser.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelBrowser.Name = "labelBrowser";
this.labelBrowser.Size = new System.Drawing.Size(49, 15);
this.labelBrowser.TabIndex = 0;
this.labelBrowser.Text = "Browser";
//
// labelNotification
//
this.labelNotification.AutoSize = true;
this.labelNotification.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.labelNotification.Location = new System.Drawing.Point(-3, 0);
this.labelNotification.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelNotification.Name = "labelNotification";
this.labelNotification.Size = new System.Drawing.Size(70, 15);
this.labelNotification.TabIndex = 0;
this.labelNotification.Text = "Notification";
//
// textBoxNotificationCSS // textBoxNotificationCSS
// //
this.textBoxNotificationCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.textBoxNotificationCSS.Dock = System.Windows.Forms.DockStyle.Fill;
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16); this.textBoxNotificationCSS.Location = new System.Drawing.Point(3, 3);
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
this.textBoxNotificationCSS.Multiline = true; this.textBoxNotificationCSS.Multiline = true;
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS"; this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxNotificationCSS.Size = new System.Drawing.Size(372, 251); this.textBoxNotificationCSS.Size = new System.Drawing.Size(426, 332);
this.textBoxNotificationCSS.TabIndex = 1; this.textBoxNotificationCSS.TabIndex = 0;
this.textBoxNotificationCSS.WordWrap = false; this.textBoxNotificationCSS.WordWrap = false;
this.textBoxNotificationCSS.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxCSS_KeyDown);
// //
// labelWarning // btnOpenDevTools
// //
this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnOpenDevTools.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelWarning.AutoSize = true; this.btnOpenDevTools.AutoSize = true;
this.labelWarning.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.btnOpenDevTools.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.labelWarning.Location = new System.Drawing.Point(94, 290); this.btnOpenDevTools.Location = new System.Drawing.Point(12, 384);
this.labelWarning.Name = "labelWarning"; this.btnOpenDevTools.Name = "btnOpenDevTools";
this.labelWarning.Size = new System.Drawing.Size(373, 15); this.btnOpenDevTools.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.labelWarning.TabIndex = 3; this.btnOpenDevTools.Size = new System.Drawing.Size(104, 25);
this.labelWarning.Text = "The code is not validated, please make sure there are no syntax errors."; this.btnOpenDevTools.TabIndex = 3;
// this.btnOpenDevTools.Text = "Open Dev Tools";
// btnOpenWiki this.btnOpenDevTools.UseVisualStyleBackColor = true;
// this.btnOpenDevTools.Click += new System.EventHandler(this.btnOpenDevTools_Click);
this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnOpenWiki.AutoSize = true;
this.btnOpenWiki.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btnOpenWiki.Location = new System.Drawing.Point(12, 285);
this.btnOpenWiki.Name = "btnOpenWiki";
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnOpenWiki.Size = new System.Drawing.Size(76, 25);
this.btnOpenWiki.TabIndex = 4;
this.btnOpenWiki.Text = "Open Wiki";
this.btnOpenWiki.UseVisualStyleBackColor = true;
this.btnOpenWiki.Click += new System.EventHandler(this.btnOpenWiki_Click);
// //
// timerTestBrowser // timerTestBrowser
// //
this.timerTestBrowser.Interval = 500; this.timerTestBrowser.Interval = 400;
this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick); this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick);
// //
// tabPanel
//
this.tabPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tabPanel.Controls.Add(this.tabPageBrowser);
this.tabPanel.Controls.Add(this.tabPageNotification);
this.tabPanel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.tabPanel.Location = new System.Drawing.Point(12, 12);
this.tabPanel.Name = "tabPanel";
this.tabPanel.SelectedIndex = 0;
this.tabPanel.Size = new System.Drawing.Size(440, 366);
this.tabPanel.TabIndex = 0;
this.tabPanel.SelectedIndexChanged += new System.EventHandler(this.tabPanel_SelectedIndexChanged);
//
// tabPageBrowser
//
this.tabPageBrowser.Controls.Add(this.textBoxBrowserCSS);
this.tabPageBrowser.Location = new System.Drawing.Point(4, 24);
this.tabPageBrowser.Name = "tabPageBrowser";
this.tabPageBrowser.Padding = new System.Windows.Forms.Padding(3);
this.tabPageBrowser.Size = new System.Drawing.Size(432, 338);
this.tabPageBrowser.TabIndex = 0;
this.tabPageBrowser.Text = "Browser";
this.tabPageBrowser.UseVisualStyleBackColor = true;
//
// tabPageNotification
//
this.tabPageNotification.Controls.Add(this.textBoxNotificationCSS);
this.tabPageNotification.Location = new System.Drawing.Point(4, 24);
this.tabPageNotification.Name = "tabPageNotification";
this.tabPageNotification.Padding = new System.Windows.Forms.Padding(3);
this.tabPageNotification.Size = new System.Drawing.Size(432, 338);
this.tabPageNotification.TabIndex = 1;
this.tabPageNotification.Text = "Notification";
this.tabPageNotification.UseVisualStyleBackColor = true;
//
// DialogSettingsCSS // DialogSettingsCSS
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(784, 322); this.ClientSize = new System.Drawing.Size(464, 421);
this.Controls.Add(this.btnOpenWiki); this.Controls.Add(this.tabPanel);
this.Controls.Add(this.labelWarning); this.Controls.Add(this.btnOpenDevTools);
this.Controls.Add(this.splitContainer);
this.Controls.Add(this.btnApply); this.Controls.Add(this.btnApply);
this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnCancel);
this.MinimumSize = new System.Drawing.Size(620, 160); this.MinimumSize = new System.Drawing.Size(320, 240);
this.Name = "DialogSettingsCSS"; this.Name = "DialogSettingsCSS";
this.ShowIcon = false; this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.splitContainer.Panel1.ResumeLayout(false); this.tabPanel.ResumeLayout(false);
this.splitContainer.Panel1.PerformLayout(); this.tabPageBrowser.ResumeLayout(false);
this.splitContainer.Panel2.ResumeLayout(false); this.tabPageBrowser.PerformLayout();
this.splitContainer.Panel2.PerformLayout(); this.tabPageNotification.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); this.tabPageNotification.PerformLayout();
this.splitContainer.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
@@ -205,12 +179,11 @@
private System.Windows.Forms.TextBox textBoxBrowserCSS; private System.Windows.Forms.TextBox textBoxBrowserCSS;
private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnApply; private System.Windows.Forms.Button btnApply;
private System.Windows.Forms.SplitContainer splitContainer;
private System.Windows.Forms.TextBox textBoxNotificationCSS; private System.Windows.Forms.TextBox textBoxNotificationCSS;
private System.Windows.Forms.Label labelBrowser; private System.Windows.Forms.Button btnOpenDevTools;
private System.Windows.Forms.Label labelNotification;
private System.Windows.Forms.Label labelWarning;
private System.Windows.Forms.Button btnOpenWiki;
private System.Windows.Forms.Timer timerTestBrowser; private System.Windows.Forms.Timer timerTestBrowser;
private System.Windows.Forms.TabControl tabPanel;
private System.Windows.Forms.TabPage tabPageBrowser;
private System.Windows.Forms.TabPage tabPageNotification;
} }
} }

View File

@@ -1,4 +1,6 @@
using System; using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -9,19 +11,108 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public string NotificationCSS => textBoxNotificationCSS.Text; public string NotificationCSS => textBoxNotificationCSS.Text;
private readonly Action<string> reinjectBrowserCSS; private readonly Action<string> reinjectBrowserCSS;
private readonly Action openDevTools;
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){ // TODO high dpi breaks scaling of things inside the panel... public DialogSettingsCSS(Action<string> reinjectBrowserCSS, Action openDevTools){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options - CSS"; Text = Program.BrandName+" Options - CSS";
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
this.openDevTools = openDevTools;
textBoxBrowserCSS.EnableMultilineShortcuts(); textBoxBrowserCSS.EnableMultilineShortcuts();
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? ""; textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
textBoxNotificationCSS.EnableMultilineShortcuts(); textBoxNotificationCSS.EnableMultilineShortcuts();
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? ""; textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
if (!BrowserUtils.HasDevTools){
btnOpenDevTools.Enabled = false;
}
ActiveControl = textBoxBrowserCSS;
textBoxBrowserCSS.Select(textBoxBrowserCSS.TextLength, 0);
}
private void tabPanel_SelectedIndexChanged(object sender, EventArgs e){
TextBox tb = tabPanel.SelectedTab.Controls.OfType<TextBox>().FirstOrDefault();
if (tb != null){
tb.Focus();
tb.Select(tb.TextLength, 0);
}
}
private void textBoxCSS_KeyDown(object sender, KeyEventArgs e){
TextBox tb = (TextBox)sender;
string text = tb.Text;
if (e.KeyCode == Keys.Back && e.Modifiers == Keys.Control){
e.SuppressKeyPress = true;
int deleteTo = tb.SelectionStart;
if (deleteTo > 0){
char initialChar = text[--deleteTo];
bool shouldDeleteAlphanumeric = char.IsLetterOrDigit(initialChar);
while(--deleteTo >= 0){
if ((shouldDeleteAlphanumeric && !char.IsLetterOrDigit(text[deleteTo])) ||
(!shouldDeleteAlphanumeric && text[deleteTo] != initialChar)){
break;
}
}
if (!(deleteTo < text.Length-1 && text[deleteTo] == '\r' && text[deleteTo+1] == '\n')){
++deleteTo;
}
tb.Select(deleteTo, tb.SelectionLength+tb.SelectionStart-deleteTo);
tb.SelectedText = string.Empty;
}
}
else if (e.KeyCode == Keys.Back && e.Modifiers == Keys.None){
int deleteTo = tb.SelectionStart;
if (deleteTo > 1 && text[deleteTo-1] == ' ' && text[deleteTo-2] == ' '){
e.SuppressKeyPress = true;
tb.Select(deleteTo-2, 2);
tb.SelectedText = string.Empty;
}
}
else if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.None && tb.SelectionLength == 0){
int insertAt = tb.SelectionStart, cursorOffset = 0;
string insertText;
if (insertAt == 0){
return;
}
else if (text[insertAt-1] == '{'){
insertText = Environment.NewLine+" ";
int nextBracket = insertAt < text.Length ? text.IndexOfAny(new char[]{ '{', '}' }, insertAt+1) : -1;
if (nextBracket == -1 || text[nextBracket] == '{'){
string insertExtra = Environment.NewLine+"}";
insertText += insertExtra;
cursorOffset -= insertExtra.Length;
}
}
else{
int lineStart = text.LastIndexOf('\n', tb.SelectionStart-1);
Match match = Regex.Match(text.Substring(lineStart == -1 ? 0 : lineStart+1), "^([ \t]+)");
insertText = match.Success ? Environment.NewLine+match.Groups[1].Value : null;
}
if (!string.IsNullOrEmpty(insertText)){
e.SuppressKeyPress = true;
tb.Text = text.Insert(insertAt, insertText);
tb.SelectionStart = insertAt+cursorOffset+insertText.Length;
}
}
} }
private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){ private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){
@@ -34,8 +125,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
timerTestBrowser.Stop(); timerTestBrowser.Stop();
} }
private void btnOpenWiki_Click(object sender, EventArgs e){ private void btnOpenDevTools_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki"); openDevTools();
} }
private void btnApply_Click(object sender, EventArgs e){ private void btnApply_Click(object sender, EventArgs e){

View File

@@ -34,7 +34,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
private ProfileManager.Items _selectedItems = ProfileManager.Items.None; private ProfileManager.Items _selectedItems = ProfileManager.Items.None;
public DialogSettingsManage(PluginManager plugins){ public DialogSettingsManage(PluginManager plugins, bool openImportImmediately = false){
InitializeComponent(); InitializeComponent();
this.plugins = plugins; this.plugins = plugins;
@@ -44,6 +44,11 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
this.checkBoxMap[cbSystemConfig] = ProfileManager.Items.SystemConfig; this.checkBoxMap[cbSystemConfig] = ProfileManager.Items.SystemConfig;
this.checkBoxMap[cbSession] = ProfileManager.Items.Session; this.checkBoxMap[cbSession] = ProfileManager.Items.Session;
this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData; this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData;
if (openImportImmediately){
radioImport.Checked = true;
btnContinue_Click(null, EventArgs.Empty);
}
} }
private void radioDecision_CheckedChanged(object sender, EventArgs e){ private void radioDecision_CheckedChanged(object sender, EventArgs e){

View File

@@ -13,18 +13,20 @@ namespace TweetDuck.Core.Other.Settings{
private static SystemConfig SysConfig => Program.SystemConfig; private static SystemConfig SysConfig => Program.SystemConfig;
private readonly Action<string> reinjectBrowserCSS; private readonly Action<string> reinjectBrowserCSS;
private readonly Action openDevTools;
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){ public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, Action openDevTools){
InitializeComponent(); InitializeComponent();
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
this.openDevTools = openDevTools;
toolTip.SetToolTip(btnOpenAppFolder, "Opens the folder where the app is located."); toolTip.SetToolTip(btnOpenAppFolder, "Opens the folder where the app is located.");
toolTip.SetToolTip(btnOpenDataFolder, "Opens the folder where your profile data is located."); toolTip.SetToolTip(btnOpenDataFolder, "Opens the folder where your profile data is located.");
toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch."); toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments."); toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments.");
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile."); toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience visual glitches, or to save a small amount of RAM.");
toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources."); toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
toolTip.SetToolTip(checkClearCacheAuto, "Automatically clears cache when its size exceeds the set threshold. Note that cache can only be cleared when closing TweetDuck."); toolTip.SetToolTip(checkClearCacheAuto, "Automatically clears cache when its size exceeds the set threshold. Note that cache can only be cleared when closing TweetDuck.");
@@ -32,13 +34,7 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework."); toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows."); toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows.");
if (SystemConfig.IsHardwareAccelerationSupported){
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration; checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
}
else{
checkHardwareAcceleration.Enabled = false;
checkHardwareAcceleration.Checked = false;
}
checkClearCacheAuto.Checked = SysConfig.ClearCacheAutomatically; checkClearCacheAuto.Checked = SysConfig.ClearCacheAutomatically;
numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked; numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked;
@@ -109,7 +105,7 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void btnEditCSS_Click(object sender, EventArgs e){ private void btnEditCSS_Click(object sender, EventArgs e){
DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS); DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS, openDevTools);
form.VisibleChanged += (sender2, args2) => { form.VisibleChanged += (sender2, args2) => {
form.MoveToCenter(ParentForm); form.MoveToCenter(ParentForm);

View File

@@ -15,7 +15,7 @@ namespace TweetDuck.Core.Other.Settings{
this.notification = notification; this.notification = notification;
this.notification.Initialized += (sender, args) => { this.notification.Ready += (sender, args) => {
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
this.notification.ShowExampleNotification(true); this.notification.ShowExampleNotification(true);
this.notification.Move += notification_Move; this.notification.Move += notification_Move;

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration;
using Res = TweetDuck.Properties.Resources;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class TrayIcon : Component{ sealed partial class TrayIcon : Component{
@@ -8,6 +10,8 @@ namespace TweetDuck.Core.Other{
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
} }
private static UserConfig Config => Program.UserConfig;
public event EventHandler ClickRestore; public event EventHandler ClickRestore;
public event EventHandler ClickClose; public event EventHandler ClickClose;
@@ -17,12 +21,9 @@ namespace TweetDuck.Core.Other{
} }
set{ set{
if (value){
notifyIcon.Icon = Properties.Resources.icon_tray;
}
notifyIcon.Visible = value; notifyIcon.Visible = value;
hasNotifications = false; hasNotifications = false;
UpdateIcon();
} }
} }
@@ -32,9 +33,9 @@ namespace TweetDuck.Core.Other{
} }
set{ set{
if (hasNotifications != value && Visible){ if (hasNotifications != value){
notifyIcon.Icon = value ? Properties.Resources.icon_tray_new : Properties.Resources.icon_tray;
hasNotifications = value; hasNotifications = value;
UpdateIcon();
} }
} }
} }
@@ -53,14 +54,26 @@ namespace TweetDuck.Core.Other{
this.notifyIcon.ContextMenu = contextMenu; this.notifyIcon.ContextMenu = contextMenu;
this.notifyIcon.Text = Program.BrandName; this.notifyIcon.Text = Program.BrandName;
Config.MuteToggled += Config_MuteToggled;
} }
public TrayIcon(IContainer container) : this(){ public TrayIcon(IContainer container) : this(){
container.Add(this); container.Add(this);
} }
private void UpdateIcon(){
if (Visible){
notifyIcon.Icon = hasNotifications ? Res.icon_tray_new : Config.MuteNotifications ? Res.icon_tray_muted : Res.icon_tray;
}
}
// event handlers // event handlers
private void Config_MuteToggled(object sender, EventArgs e){
UpdateIcon();
}
private void trayIcon_MouseClick(object sender, MouseEventArgs e){ private void trayIcon_MouseClick(object sender, MouseEventArgs e){
if (e.Button == MouseButtons.Left){ if (e.Button == MouseButtons.Left){
menuItemRestore_Click(sender, e); menuItemRestore_Click(sender, e);
@@ -68,7 +81,7 @@ namespace TweetDuck.Core.Other{
} }
private void contextMenu_Popup(object sender, EventArgs e){ private void contextMenu_Popup(object sender, EventArgs e){
contextMenu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications; contextMenu.MenuItems[1].Checked = Config.MuteNotifications;
} }
private void menuItemRestore_Click(object sender, EventArgs e){ private void menuItemRestore_Click(object sender, EventArgs e){
@@ -76,8 +89,8 @@ namespace TweetDuck.Core.Other{
} }
private void menuItemMuteNotifications_Click(object sender, EventArgs e){ private void menuItemMuteNotifications_Click(object sender, EventArgs e){
Program.UserConfig.MuteNotifications = !contextMenu.MenuItems[1].Checked; Config.MuteNotifications = !contextMenu.MenuItems[1].Checked;
Program.UserConfig.Save(); Config.Save();
} }
private void menuItemClose_Click(object sender, EventArgs e){ private void menuItemClose_Click(object sender, EventArgs e){

View File

@@ -4,10 +4,12 @@ using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Management;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other.Interfaces; using TweetDuck.Core.Other.Interfaces;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -39,14 +41,16 @@ namespace TweetDuck.Core{
private string prevSoundNotificationPath = null; private string prevSoundNotificationPath = null;
public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){ public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){ this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
DialogHandler = new FileDialogHandler(), DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(), DragHandler = new DragHandlerBrowser(requestHandler),
MenuHandler = new ContextMenuBrowser(owner), MenuHandler = new ContextMenuBrowser(owner),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(owner), KeyboardHandler = new KeyboardHandlerBrowser(owner),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser() RequestHandler = requestHandler
}; };
this.browser.LoadingStateChanged += browser_LoadingStateChanged; this.browser.LoadingStateChanged += browser_LoadingStateChanged;
@@ -154,6 +158,10 @@ namespace TweetDuck.Core{
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){
ScriptLoader.ExecuteScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
}
if (Program.UserConfig.FirstRun){ if (Program.UserConfig.FirstRun){
ScriptLoader.ExecuteFile(frame, "introduction.js", browser); ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
} }
@@ -166,7 +174,7 @@ namespace TweetDuck.Core{
} }
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){ if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true); string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html");
if (errorPage != null){ if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error"); browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
@@ -215,7 +223,7 @@ namespace TweetDuck.Core{
} }
public void InjectBrowserCSS(){ public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", false, browser)?.TrimEnd() ?? string.Empty); browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", browser)?.TrimEnd() ?? string.Empty);
} }
public void ReinjectCustomCSS(string css){ public void ReinjectCustomCSS(string css){
@@ -253,5 +261,9 @@ namespace TweetDuck.Core{
public void ShowUpdateNotification(string versionTag, string releaseNotes){ public void ShowUpdateNotification(string versionTag, string releaseNotes){
browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes))); browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes)));
} }
public void OpenDevTools(){
browser.ShowDevTools();
}
} }
} }

View File

@@ -14,6 +14,8 @@ namespace TweetDuck.Core.Utils{
public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion; public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion;
public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36"; public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36";
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
public static void SetupCefArgs(IDictionary<string, string> args){ public static void SetupCefArgs(IDictionary<string, string> args){
if (!Program.SystemConfig.HardwareAcceleration){ if (!Program.SystemConfig.HardwareAcceleration){
args["disable-gpu"] = "1"; args["disable-gpu"] = "1";

View File

@@ -71,6 +71,9 @@ namespace TweetDuck.Core.Utils{
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop); private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(out bool enabled);
[DllImport("user32.dll")] [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow); public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

View File

@@ -19,11 +19,23 @@ namespace TweetDuck.Core.Utils{
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper(); return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
} }
public static string ConvertRot13(string str){
return Regex.Replace(str, @"[a-zA-Z]", match => {
int code = match.Value[0];
int start = code <= 90 ? 65 : 97;
return ((char)(start+(code-start+13)%26)).ToString();
});
}
public static int CountOccurrences(string source, string substring){ public static int CountOccurrences(string source, string substring){
int count = 0, index = 0; if (substring.Length == 0){
throw new ArgumentOutOfRangeException(nameof(substring), "Searched substring must not be empty.");
}
int count = 0, index = 0, length = substring.Length;
while((index = source.IndexOf(substring, index)) != -1){ while((index = source.IndexOf(substring, index)) != -1){
index += substring.Length; index += length;
++count; ++count;
} }

View File

@@ -15,10 +15,12 @@ namespace TweetDuck.Core.Utils{
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false); private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false); private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
private static readonly bool IsWindows8OrNewer;
private static bool HasMicrosoftBeenBroughtTo2008Yet;
public static int CurrentProcessID { get; } public static int CurrentProcessID { get; }
public static bool ShouldAvoidToolWindow { get; } public static bool ShouldAvoidToolWindow { get; }
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
private static bool HasMicrosoftBeenBroughtTo2008Yet;
static WindowsUtils(){ static WindowsUtils(){
using(Process me = Process.GetCurrentProcess()){ using(Process me = Process.GetCurrentProcess()){
@@ -26,7 +28,9 @@ namespace TweetDuck.Core.Utils{
} }
Version ver = Environment.OSVersion.Version; Version ver = Environment.OSVersion.Version;
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10 IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10
ShouldAvoidToolWindow = IsWindows8OrNewer;
} }
public static void EnsureTLS12(){ public static void EnsureTLS12(){

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Data{
this.html = html; this.html = html;
} }
public string Inject(string targetHTML){ public string InjectInto(string targetHTML){
int index = targetHTML.IndexOf(search, StringComparison.Ordinal); int index = targetHTML.IndexOf(search, StringComparison.Ordinal);
if (index == -1){ if (index == -1){

View File

@@ -24,15 +24,6 @@ namespace TweetDuck.Plugins.Enums{
return environment == PluginEnvironment.Browser; return environment == PluginEnvironment.Browser;
} }
public static string GetScriptIdentifier(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.None: return "root:plugins";
case PluginEnvironment.Browser: return "root:plugins.browser";
case PluginEnvironment.Notification: return "root:plugins.notification";
default: return null;
}
}
public static string GetPluginScriptFile(this PluginEnvironment environment){ public static string GetPluginScriptFile(this PluginEnvironment environment){
switch(environment){ switch(environment){
case PluginEnvironment.Browser: return "browser.js"; case PluginEnvironment.Browser: return "browser.js";

View File

@@ -84,14 +84,20 @@ namespace TweetDuck.Plugins{
try{ try{
string folderPathName = new DirectoryInfo(rootFolder).FullName; string folderPathName = new DirectoryInfo(rootFolder).FullName;
DirectoryInfo currentInfo = new DirectoryInfo(fullPath); DirectoryInfo currentInfo = new DirectoryInfo(fullPath); // initially points to the file, which is convenient for the Attributes check below
DirectoryInfo parentInfo = currentInfo.Parent;
while(currentInfo.Parent != null){ while(parentInfo != null){
if (currentInfo.Parent.FullName == folderPathName){ if (currentInfo.Exists && currentInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)){
return string.Empty; // no reason why a plugin should have files/folders with symlinks, junctions, or any other crap
}
if (parentInfo.FullName == folderPathName){
return fullPath; return fullPath;
} }
currentInfo = currentInfo.Parent; currentInfo = parentInfo;
parentInfo = currentInfo.Parent;
} }
} }
catch{ catch{

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms;
using TweetDuck.Core.Other.Interfaces; using TweetDuck.Core.Other.Interfaces;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
@@ -12,15 +13,7 @@ using TweetDuck.Resources;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
sealed class PluginManager{ sealed class PluginManager{
private static IReadOnlyDictionary<PluginEnvironment, string> LoadSetupScripts(){ private static readonly IReadOnlyDictionary<PluginEnvironment, string> PluginSetupScriptNames = PluginEnvironmentExtensions.Map(null, "plugins.browser.js", "plugins.notification.js");
return PluginEnvironmentExtensions.Map(
ScriptLoader.LoadResource("plugins.js"),
ScriptLoader.LoadResource("plugins.browser.js"),
ScriptLoader.LoadResource("plugins.notification.js")
);
}
private static readonly IReadOnlyDictionary<PluginEnvironment, string> PluginSetupScripts = LoadSetupScripts();
public string PathOfficialPlugins => Path.Combine(rootPath, "official"); public string PathOfficialPlugins => Path.Combine(rootPath, "official");
public string PathCustomPlugins => Path.Combine(rootPath, "user"); public string PathCustomPlugins => Path.Combine(rootPath, "user");
@@ -54,8 +47,8 @@ namespace TweetDuck.Plugins{
Config.PluginChangedState += Config_PluginChangedState; Config.PluginChangedState += Config_PluginChangedState;
} }
public void Register(ITweetDeckBrowser browser, PluginEnvironment environment, bool asMainBrowser = false){ public void Register(ITweetDeckBrowser browser, PluginEnvironment environment, Control sync, bool asMainBrowser = false){
browser.OnFrameLoaded(frame => ExecutePlugins(frame, environment)); browser.OnFrameLoaded(frame => ExecutePlugins(frame, environment, sync));
browser.RegisterBridge("$TDP", bridge); browser.RegisterBridge("$TDP", bridge);
if (asMainBrowser){ if (asMainBrowser){
@@ -152,14 +145,11 @@ namespace TweetDuck.Plugins{
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors)); Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
} }
private void ExecutePlugins(IFrame frame, PluginEnvironment environment){ private void ExecutePlugins(IFrame frame, PluginEnvironment environment, Control sync){
if (!HasAnyPlugin(environment)){ if (!HasAnyPlugin(environment) || !ScriptLoader.ExecuteFile(frame, PluginSetupScriptNames[environment], sync)){
return; return;
} }
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
bool includeDisabled = environment.IncludesDisabledPlugins(); bool includeDisabled = environment.IncludesDisabledPlugins();
if (includeDisabled){ if (includeDisabled){

View File

@@ -19,7 +19,7 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.13.6"; public const string VersionTag = "1.15";
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable")); public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
@@ -157,7 +157,7 @@ namespace TweetDuck{
ExitCleanup(); ExitCleanup();
// ProgramPath has a trailing backslash // ProgramPath has a trailing backslash
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : ""); string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath); bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){ if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Resources; using System.Resources;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -42,5 +42,6 @@ using TweetDuck;
[assembly: CLSCompliant(true)] [assembly: CLSCompliant(true)]
#if DEBUG #if DEBUG
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TweetTest.System")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TweetTest.Unit")]
#endif #endif

View File

@@ -80,6 +80,16 @@ namespace TweetDuck.Properties {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon icon_muted {
get {
object obj = ResourceManager.GetObject("icon_muted", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary> /// </summary>
@@ -90,6 +100,16 @@ namespace TweetDuck.Properties {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon icon_tray_muted {
get {
object obj = ResourceManager.GetObject("icon_tray_muted", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary> /// </summary>

View File

@@ -119,18 +119,24 @@
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="avatar" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="avatar" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\avatar.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\Resources\Images\avatar.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\Images\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="icon_muted" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\icon-muted.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="icon_tray" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="icon_tray" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon-tray.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\Images\icon-tray.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="icon_tray_muted" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\icon-tray-muted.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\Images\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\Resources\Images\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
</root> </root>

View File

@@ -9,53 +9,48 @@
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space): The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
* **.NET desktop development** * **.NET desktop development**
* .NET Framework 4 4.6 development tools * .NET Framework 4 4.6 development tools
* F# desktop language support
* **Desktop development with C++** * **Desktop development with C++**
* VC++ 2017 v141 toolset * VC++ 2017 latest v141 tools
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running this command in the **Package Manager Console**: After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
``` ```
PM> Install-Package CefSharp.WinForms -Version 64.0.0-CI2508 -Source https://www.myget.org/F/cefsharp/api/v3/index.json PM> Install-Package CefSharp.WinForms -Version 67.0.0-CI2662 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
``` ```
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources. Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
### Debug ### Debug
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution. The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure your **Configuration** is set to `Active (Debug)` (or just `Debug`), and insert this into the **Command line arguments** field: While debugging, opening the main menu and clicking **Reload browser** automatically rebuilds all resources in the `Resources/Scripts` and `Resources/Plugins` folders. This allows editing HTML/CSS/JS files and applying the changes without restarting the program, but it will cause a short delay between browser reloads.
```
-datafolder TweetDuckDebug
```
### Build ### Release
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section. Open **Batch Build**, tick all `Release` configurations with `x86` platform, and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation. After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck. The `Release` configuration omits debug symbols and other unnecessary files. You can modify this behavior by opening `TweetDuck.csproj`, and editing the `<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">` section.
If you decide to publicly release a custom version, please make it clear that it is not an official release of TweetDuck. There are many references to the official website and this repository, especially in the update system, so search for `chylex.com` and `github.com` in all files and replace them appropriately.
### Troubleshooting ### Troubleshooting
There are a few quirks in the build process that may catch you off guard: #### Error: The command (...) exited with code 1
- This indicates a failed post-build event, open the **Output** tab for logs
- **Plugin files are not updated automatically** - Determine if there was an IO error from the `rmdir` commands, or whether the error was in the **PostBuild.ps1** script (`Encountered an error while running PostBuild.ps1 on line <xyz>`)
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
- **Error: The command (...) exited with code 1**
- If the post-build event fails, open the **Output** tab and look for the cause
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
- Some files are checked for invalid characters: - Some files are checked for invalid characters:
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor - `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
### Installers ### Installers
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin). TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place. Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio! Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that this will only package the files, you still need to run the [release build](#release) in Visual Studio!
After the window closes, three installers will be generated inside the `bld/Output` folder: After the window closes, three installers will be generated inside the `bld/Output` folder:
* **TweetDuck.exe** * **TweetDuck.exe**
@@ -67,8 +62,10 @@ After the window closes, three installers will be generated inside the `bld/Outp
* This is a portable installer that does not need administrator privileges * This is a portable installer that does not need administrator privileges
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode * It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
Note: There is a small chance you will see a resource error when running `RUN BUILD.bat`. If that happens, close the console window (which will terminate all Inno Setup processes and leave corrupted installer files in the output folder), and run it again. #### Notes
### Code Notes > When opening **Batch Build**, you will also see `x64` and `AnyCPU` configurations. These are visible due to what I consider a Visual Studio bug, and will not work without significant changes to the project. Manually running the `Resources/PostCefUpdate.ps1` PowerShell script modifies the downloaded CefSharp packages, and removes the invalid configurations.
There are many references to the official TweetDuck website and this repository in the code and installers, so if you plan to release your own version, make sure to search for `tweetduck.chylex.com` and `github.com` in the whole repository and replace them appropriately. > There is a small chance running `RUN BUILD.bat` immediately shows a resource error. If that happens, close the console window (which terminates all Inno Setup processes and leaves corrupted installers in the output folder), and run it again.
> Running `RUN BUILD.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`.

View File

@@ -47,7 +47,8 @@ namespace TweetDuck{
public void HandleException(string caption, string message, bool canIgnore, Exception e){ public void HandleException(string caption, string message, bool canIgnore, Exception e){
bool loggedSuccessfully = Log(e.ToString()); bool loggedSuccessfully = Log(e.ToString());
FormMessage form = new FormMessage(caption, message+"\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error); string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message;
FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
Button btnExit = form.AddButton(FormMessage.Exit); Button btnExit = form.AddButton(FormMessage.Exit);
Button btnIgnore = form.AddButton(FormMessage.Ignore, DialogResult.Ignore, ControlType.Cancel); Button btnIgnore = form.AddButton(FormMessage.Ignore, DialogResult.Ignore, ControlType.Cancel);
@@ -95,5 +96,15 @@ namespace TweetDuck{
Environment.FailFast(message, new Exception(message)); Environment.FailFast(message, new Exception(message));
} }
} }
public sealed class ExpandedLogException : Exception{
private readonly string details;
public ExpandedLogException(Exception source, string details) : base(source.Message, source){
this.details = details;
}
public override string ToString() => base.ToString()+"\r\n"+details;
}
} }
} }

View File

@@ -0,0 +1,20 @@
{
"folders": [
{
"name": "Installers",
"path": "..\\bld"
},
{
"name": "Resources",
"path": "."
}
],
"settings": {
"editor.tabSize": 2,
"javascript.validate.enable": false,
"files.autoGuessEncoding": false,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.encoding": "utf8",
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
Resources/Images/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -2,6 +2,7 @@ enabled(){
this.isDebugging = false; this.isDebugging = false;
this.onKeyDown = (e) => { this.onKeyDown = (e) => {
// ========================== // ==========================
// F4 key - toggle debug mode // F4 key - toggle debug mode
// ========================== // ==========================
@@ -11,8 +12,6 @@ enabled(){
$(".nav-user-info").first().css("background-color", this.isDebugging ? "#5A6B75" : "#292F33"); $(".nav-user-info").first().css("background-color", this.isDebugging ? "#5A6B75" : "#292F33");
} }
// Debug mode handling
else if (this.isDebugging){ else if (this.isDebugging){
e.preventDefault(); e.preventDefault();
@@ -22,13 +21,14 @@ enabled(){
// =================================== // ===================================
if (e.keyCode === 78 || e.keyCode === 83){ if (e.keyCode === 78 || e.keyCode === 83){
var col = TD.controller.columnManager.getAllOrdered()[0]; let col = TD.controller.columnManager.getAllOrdered()[0];
let model = col.model;
var prevPopup = col.model.getHasNotification(); let prevPopup = model.getHasNotification();
var prevSound = col.model.getHasSound(); let prevSound = model.getHasSound();
col.model.setHasNotification(e.keyCode === 78); model.setHasNotification(e.keyCode === 78);
col.model.setHasSound(e.keyCode === 83); model.setHasSound(e.keyCode === 83);
$.publish("/notifications/new", [{ $.publish("/notifications/new", [{
column: col, column: col,
@@ -38,8 +38,8 @@ enabled(){
}]); }]);
setTimeout(function(){ setTimeout(function(){
col.model.setHasNotification(prevPopup); model.setHasNotification(prevPopup);
col.model.setHasSound(prevSound); model.setHasSound(prevSound);
}, 1); }, 1);
} }

View File

@@ -1,33 +1,22 @@
constructor(){
super({
requiresPageReload: true
});
}
enabled(){ enabled(){
// prepare variables and functions const clearColumn = (columnName) => {
var clearColumn = (columnName) => {
TD.controller.columnManager.get(columnName).clear(); TD.controller.columnManager.get(columnName).clear();
TD.controller.stats.columnActionClick("clear"); TD.controller.stats.columnActionClick("clear");
}; };
var resetColumn = (columnName) => { const resetColumn = (columnName) => {
let col = TD.controller.columnManager.get(columnName); let col = TD.controller.columnManager.get(columnName);
col.model.setClearedTimestamp(0); col.model.setClearedTimestamp(0);
col.reloadTweets(); col.reloadTweets();
}; };
var forEachColumn = (func) => { const forEachColumn = (func) => {
Object.keys(TD.controller.columnManager.getAll()).forEach(func); Object.keys(TD.controller.columnManager.getAll()).forEach(func);
}; };
var replaceMustache = (key, search, replace) => { let wasShiftPressed = false;
TD.mustaches[key] = TD.mustaches[key].replace(search, replace);
};
var wasShiftPressed = false; const updateShiftState = (pressed) => {
var updateShiftState = (pressed) => {
if (pressed != wasShiftPressed){ if (pressed != wasShiftPressed){
wasShiftPressed = pressed; wasShiftPressed = pressed;
@@ -42,10 +31,17 @@ enabled(){
} }
}; };
// prepare event handlers // event handlers
this.eventClickSingle = function(e){
var name = $(this).closest(".js-column").attr("data-column"); this.eventClickOneCapture = function(e){
if (e.target.getAttribute("data-action") === "td-clearcolumns-dosingle"){
let name = $(e.target).closest(".js-column").attr("data-column");
e.shiftKey ? resetColumn(name) : clearColumn(name); e.shiftKey ? resetColumn(name) : clearColumn(name);
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}; };
this.eventClickAll = function(e){ this.eventClickAll = function(e){
@@ -62,10 +58,10 @@ enabled(){
forEachColumn(e.shiftKey ? resetColumn : clearColumn); forEachColumn(e.shiftKey ? resetColumn : clearColumn);
} }
else{ else{
var focusedColumn = $(".js-column.is-focused"); let focusedColumn = $(".js-column.is-focused");
if (focusedColumn.length){ if (focusedColumn.length){
var name = focusedColumn.attr("data-column"); let name = focusedColumn.attr("data-column");
e.shiftKey ? resetColumn(name) : clearColumn(name); e.shiftKey ? resetColumn(name) : clearColumn(name);
} }
} }
@@ -78,63 +74,86 @@ enabled(){
} }
}; };
// setup clear all button this.eventKeyboardShortcuts = function(e){
$(".keyboard-shortcut-list").first().append(`
<dd class="keyboard-shortcut-definition" style="white-space:nowrap">
<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19
</dd>
<dd class="keyboard-shortcut-definition">
<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns
</dd>`);
};
// update UI
this.btnClearAllHTML = ` this.btnClearAllHTML = `
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall"> <a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div> <div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div> <div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div>
</a>`; </a>`;
// add column buttons and keyboard shortcut info to UI this.btnClearOneHTML = `
replaceMustache("column/column_header.mustache", "</header>", ` <a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle">
{{^isTemporary}} <i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)" data-action="td-clearcolumns-dosingle"></i>
<a class="column-header-link td-clear-column-shortcut" href="#" data-action="td-clearcolumns-dosingle" style="right:34px"> </a>`;
<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i>
</a>
{{/isTemporary}}
</header>`);
replaceMustache("keyboard_shortcut_list.mustache", "</dl> <dl", ` this.prevNavMenuMustache = TD.mustaches["menus/column_nav_menu.mustache"];
<dd class="keyboard-shortcut-definition" style="white-space:nowrap"> window.TDPF_injectMustache("menus/column_nav_menu.mustache", "replace", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`);
<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19
</dd><dd class="keyboard-shortcut-definition">
<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns
</dd></dl><dl`);
replaceMustache("menus/column_nav_menu.mustache", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`) this.prevColumnHeaderMustache = TD.mustaches["column/column_header.mustache"];
window.TDPF_injectMustache("column/column_header.mustache", "prepend", "<a data-testid=\"optionsToggle\"", this.btnClearOneHTML);
// load custom style if (TD.ready){
var css = window.TDPF_createCustomStyle(this); $(".js-header-add-column").after(this.btnClearAllHTML);
css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }"); $("a[data-testid='optionsToggle']", ".js-column-header").before(this.btnClearOneHTML);
css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }"); }
css.insert(".column-navigator-overflow { bottom: 224px !important; }");
css.insert(".column-title { margin-right: 60px !important; }"); // styles
css.insert(".mark-all-read-link { right: 59px !important; }");
css.insert(".open-compose-dm-link { right: 90px !important; }"); this.css = window.TDPF_createCustomStyle(this);
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }"); this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
css.insert("[data-td-icon='icon-message'] .column-title { margin-right: 115px !important; }"); this.css.insert(".column-header-links { min-width: 51px !important; }");
css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }"); this.css.insert("[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }"); this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
this.css.insert("[data-action='td-clearcolumns-dosingle'] { padding: 3px 0 !important; }");
this.css.insert("[data-action='clear'].btn-options-tray { display: none !important; }");
this.css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
this.css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
} }
ready(){ ready(){
// setup events document.addEventListener("click", this.eventClickOneCapture, true);
$(document).on("click", "[data-action='td-clearcolumns-dosingle']", this.eventClickSingle);
$(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll); $(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll);
$(document).on("keydown", this.eventKeyDown); $(document).on("keydown", this.eventKeyDown);
$(document).on("keyup", this.eventKeyUp); $(document).on("keyup", this.eventKeyUp);
$(document).on("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts);
// setup clear all button for nav overflow
$(".js-app-add-column").first().after(this.btnClearAllHTML); $(".js-app-add-column").first().after(this.btnClearAllHTML);
// setup tooltip handling // fix tooltip
var tooltipEvents = $._data($(".js-header-action")[0]).events;
let tooltipEvents = $._data($(".js-header-action")[0], "events");
if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){ if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){
$(".clear-columns-btn-all-parent").on("mouseover", tooltipEvents.mouseover[0].handler).on("mouseout", tooltipEvents.mouseout[0].handler); $(".clear-columns-btn-all-parent").on({
mouseover: tooltipEvents.mouseover[0].handler,
mouseout: tooltipEvents.mouseout[0].handler
});
} }
} }
disabled(){ disabled(){
// not needed, plugin reloads the page when enabled or disabled this.css.remove();
document.removeEventListener("click", this.eventClickOneCapture);
$(document).off("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll);
$(document).off("keydown", this.eventKeyDown);
$(document).off("keyup", this.eventKeyUp);
$(document).off("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts);
TD.mustaches["menus/column_nav_menu.mustache"] = this.prevNavMenuMustache;
TD.mustaches["column/column_header.mustache"] = this.prevColumnHeaderMustache;
$("[data-action^='td-clearcolumns-']").remove();
} }

View File

@@ -21,7 +21,7 @@ enabled(){
avatarRadius: 2 avatarRadius: 2
}; };
var prepareDefaultConfig = () => { const prepareDefaultConfig = () => {
this.defaultConfig._theme = TD.settings.getTheme(); this.defaultConfig._theme = TD.settings.getTheme();
switch(TD.settings.getColumnWidth()){ switch(TD.settings.getColumnWidth()){
@@ -39,7 +39,7 @@ enabled(){
this.firstTimeLoad = null; this.firstTimeLoad = null;
var me = this; const me = this;
// modal dialog loading // modal dialog loading
$TDP.readFileRoot(this.$token, "modal.html").then(contents => { $TDP.readFileRoot(this.$token, "modal.html").then(contents => {
@@ -72,7 +72,7 @@ enabled(){
} }
}; };
var loadConfigObject = obj => { const loadConfigObject = obj => {
this.tmpConfig = obj || {}; this.tmpConfig = obj || {};
this.firstTimeLoad = obj === null; this.firstTimeLoad = obj === null;
@@ -139,7 +139,7 @@ enabled(){
}; };
// modal dialog setup // modal dialog setup
var updateKey = function(key, value){ const updateKey = function(key, value){
me.config[key] = value; me.config[key] = value;
setTimeout(function(){ setTimeout(function(){
@@ -396,8 +396,10 @@ enabled(){
switch(currentTheme){ switch(currentTheme){
case "black": case "black":
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }"); this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
this.css.insert(".column-header-temp { background-color: transparent !important }");
this.css.insert(".column-drag-handle { opacity: 0.5 !important }"); this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }"); this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
this.css.insert(".column-message.is-actionable span:hover > .icon-small-valigned { filter: saturate(20) }");
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }"); this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
notificationScrollbarColor = "666"; notificationScrollbarColor = "666";
break; break;
@@ -539,8 +541,8 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important } #tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important } #tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
.column-header .column-type-icon { bottom: 26px !important } .js-column-header .column-type-icon { margin-top: -1px !important }
.is-options-open .column-type-icon { bottom: 25px !important } .inline-reply .pull-left .Button--link { margin-top: 3px !important }
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; } .tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; } .tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
@@ -561,7 +563,7 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
if (this.config.columnWidth[0] === '/'){ if (this.config.columnWidth[0] === '/'){
let cols = this.config.columnWidth.slice(1); let cols = this.config.columnWidth.slice(1);
this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important }"); this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important; min-width: 160px }");
this.css.insert(".is-condensed .column { width: calc((100vw - 55px) / "+cols+" - 6px) !important }"); this.css.insert(".is-condensed .column { width: calc((100vw - 55px) / "+cols+" - 6px) !important }");
} }
else{ else{
@@ -641,7 +643,7 @@ ready(){
$(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>'); $(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>');
// global settings override // global settings override
var me = this; const me = this;
this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo; this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo;
this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab; this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab;

View File

@@ -44,6 +44,8 @@
</optgroup> </optgroup>
<option disabled></option> <option disabled></option>
<optgroup label="Dynamic width"> <optgroup label="Dynamic width">
<option value="/1">1 column on screen</option>
<option value="/2">2 columns on screen</option>
<option value="/3">3 columns on screen</option> <option value="/3">3 columns on screen</option>
<option value="/4">4 columns on screen</option> <option value="/4">4 columns on screen</option>
<option value="/5">5 columns on screen</option> <option value="/5">5 columns on screen</option>
@@ -151,8 +153,7 @@
<!-- END --> <!-- END -->
</div> </div>
<footer class="padding-vxl txt-center"> <footer class="padding-vxl txt-center">
<button class="js-dismiss btn btn-positive pull-right"> <button class="js-dismiss Button--primary pull-right">
<i class="icon icon-check icon-small padding-rs"></i>
<span class="label">Done</span> <span class="label">Done</span>
</button> </button>
</footer> </footer>

View File

@@ -13,7 +13,6 @@ html.dark .color-twitter-blue{color:#1DA1F2}
html.dark .color-twitter-red{color:#E0245E} html.dark .color-twitter-red{color:#E0245E}
html.dark .color-twitter-deep-red{color:#A01744} html.dark .color-twitter-deep-red{color:#A01744}
html.dark .color-twitter-green{color:#17BF63} html.dark .color-twitter-green{color:#17BF63}
html.dark .color-twitter-deep-green{color:#008951}
html.dark .color-twitter-deep-black{color:#14171A} html.dark .color-twitter-deep-black{color:#14171A}
html.dark .color-twitter-dark-black{color:#292F33} html.dark .color-twitter-dark-black{color:#292F33}
html.dark .color-twitter-dark-gray{color:#8899A6} html.dark .color-twitter-dark-gray{color:#8899A6}
@@ -22,7 +21,6 @@ html.dark .color-twitter-yellow{color:#FFAD1F}
html.dark .bg-color-twitter-white{background-color:#fff} html.dark .bg-color-twitter-white{background-color:#fff}
html.dark .bg-color-twitter-blue{background-color:#1DA1F2} html.dark .bg-color-twitter-blue{background-color:#1DA1F2}
html.dark .bg-color-twitter-medium-blue{background-color:#1DA1F2} html.dark .bg-color-twitter-medium-blue{background-color:#1DA1F2}
html.dark .bg-color-twitter-faded-yellow{background-color:#FFE8B6}
html.dark .bg-color-twitter-deep-black{background-color:#292F33} html.dark .bg-color-twitter-deep-black{background-color:#292F33}
html.dark .bg-color-twitter-lightest-gray{background-color:#F5F8FA} html.dark .bg-color-twitter-lightest-gray{background-color:#F5F8FA}
html.dark .link-hover-override:hover .link-hover-target{color:#8bd} html.dark .link-hover-override:hover .link-hover-target{color:#8bd}
@@ -113,9 +111,6 @@ html.dark .btn-on-blue[disabled],html.dark .btn-on-blue[disabled]:hover,html.dar
html.dark .btn-options-tray{color:#e1e8ed} html.dark .btn-options-tray{color:#e1e8ed}
html.dark .btn-options-tray:hover,html.dark .btn-options-tray:focus{color:#8bd} html.dark .btn-options-tray:hover,html.dark .btn-options-tray:focus{color:#8bd}
html.dark .btn-options-tray[disabled],html.dark .btn-options-tray[disabled]:hover,html.dark .btn-options-tray[disabled]:active,html.dark .btn-options-tray.is-disabled,html.dark .btn-options-tray.is-disabled:hover,html.dark .btn-options-tray.is-disabled:focus,html.dark .btn-options-tray.is-disabled:active{color:#8bd} html.dark .btn-options-tray[disabled],html.dark .btn-options-tray[disabled]:hover,html.dark .btn-options-tray[disabled]:active,html.dark .btn-options-tray.is-disabled,html.dark .btn-options-tray.is-disabled:hover,html.dark .btn-options-tray.is-disabled:focus,html.dark .btn-options-tray.is-disabled:active{color:#8bd}
html.dark .btn-bg-positive{background-color:rgba(102,117,127,0.5)}
html.dark .btn-bg-positive:hover,html.dark .btn-bg-positive:focus{background-color:rgba(102,117,127,0.5)}
html.dark .btn-bg-positive[disabled],html.dark .btn-bg-positive[disabled]:hover,html.dark .btn-bg-positive[disabled]:active,html.dark .btn-bg-positive.is-disabled,html.dark .btn-bg-positive.is-disabled:hover,html.dark .btn-bg-positive.is-disabled:focus,html.dark .btn-bg-positive.is-disabled:active{background-color:rgba(102,117,127,0.5)}
html.dark .follow-btn .icon,html.dark .follow-btn .Icon{color:#1DA1F2} html.dark .follow-btn .icon,html.dark .follow-btn .Icon{color:#1DA1F2}
html.dark .account-profile-header{background-color:#1DA1F2} html.dark .account-profile-header{background-color:#1DA1F2}
html.dark .account-settings-bt{border-top:1px solid #e1e8ed} html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
@@ -190,8 +185,6 @@ html.dark .is-touch-search .list-item:hover,html.dark .is-touch-search .list-ite
html.dark .is-touch-search .list-item:hover .list-icon,html.dark .is-touch-search .list-item:active .list-icon,html.dark .is-touch-search .list-item.is-selected .list-icon{color:#fff} html.dark .is-touch-search .list-item:hover .list-icon,html.dark .is-touch-search .list-item:active .list-icon,html.dark .is-touch-search .list-item.is-selected .list-icon{color:#fff}
html.dark .avatar-border--2{border:2px solid #fff;background-color:#fff} html.dark .avatar-border--2{border:2px solid #fff;background-color:#fff}
html.dark .account-link{color:#e1e8ed} html.dark .account-link{color:#e1e8ed}
.on-blue html.dark .account-link{color:#fff}
.compose .quoted-tweet html.dark .account-link{color:#66757f}
html.dark .media-badge{border:1px solid #292F33;color:#999} html.dark .media-badge{border:1px solid #292F33;color:#999}
html.dark .media-badge:hover{background-color:#14171A} html.dark .media-badge:hover{background-color:#14171A}
html.dark .media-size-large-height::after,html.dark .media-item.media-size-large::after{background-image:linear-gradient(rgba(17,17,17,0.25),rgba(17,17,17,0))} html.dark .media-size-large-height::after,html.dark .media-item.media-size-large::after{background-image:linear-gradient(rgba(17,17,17,0.25),rgba(17,17,17,0))}
@@ -219,7 +212,7 @@ html.dark .is-minimalist .tweet-img{background:#292F33}
html.dark .is-selected-tweet{background:#292F33} html.dark .is-selected-tweet{background:#292F33}
html.dark .in-tweet-divider:before{background:#292F33} html.dark .in-tweet-divider:before{background:#292F33}
html.dark .tweet-translation-original-text{color:#8899a6} html.dark .tweet-translation-original-text{color:#8899a6}
html.dark .quoted-tweet{border:1px solid #E1E8ED;color:#66757f;border-color:#292F33;color:#999} html.dark .quoted-tweet{border-color:#292F33;color:#999}
html.dark .scheduled-tweet{border:1px solid #292F33;color:#8899A6} html.dark .scheduled-tweet{border:1px solid #292F33;color:#8899A6}
html.dark .stream-item .icon-edit,html.dark .stream-item .icon-trash{color:#657786} html.dark .stream-item .icon-edit,html.dark .stream-item .icon-trash{color:#657786}
html.dark .stream-item .icon-edit:hover,html.dark .stream-item .icon-trash:hover{color:#8899A6} html.dark .stream-item .icon-edit:hover,html.dark .stream-item .icon-trash:hover{color:#8899A6}
@@ -435,7 +428,7 @@ html.dark .list-account .username{color:#8899a6}
html.dark .list-listmember .username{color:#8899a6} html.dark .list-listmember .username{color:#8899a6}
html.dark .list-listmember .bio{color:#657786} html.dark .list-listmember .bio{color:#657786}
html.dark .divider-bar{background-color:#ddd} html.dark .divider-bar{background-color:#ddd}
html.dark select{background-image:url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='18' height='8' viewBox='0 0 18 8'><path fill='#aaa' d='M9.82875,0.840168025 C9.59875,0.608328018 9.22625,0.608328018 8.99625,0.840168025 L5.00125,4.86964815 L1.00375,0.840168025 C0.77375,0.608328018 0.40125,0.608328018 0.17125,0.840168025 C-0.05875,1.07200803 -0.05625,1.44748804 0.17125,1.67932805 L4.58375,6.12712819 C4.69875,6.24304819 4.84875,6.30100819 5.00125,6.30100819 C5.15125,6.30100819 5.30125,6.24304819 5.41625,6.12712819 L9.82875,1.67932805 C10.05875,1.44748804 10.05875,1.07200803 9.82875,0.840168025'></path></svg>");background-color:#fff} html.dark select{background-color:#fff}
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed} html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed}
html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed} html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed}
html.dark select:disabled{background-color:#f5f8fa} html.dark select:disabled{background-color:#f5f8fa}
@@ -613,46 +606,45 @@ html.dark .Dropdown-divider{background-color:#ccd6dd}
html.dark .Dropdown-menuItem .Dropdown-menuItemContent,html.dark .Dropdown-menuGroupLabel{color:#14171a} html.dark .Dropdown-menuItem .Dropdown-menuItemContent,html.dark .Dropdown-menuGroupLabel{color:#14171a}
html.dark .Dropdown-menuItem .Dropdown-menuItemContent .Icon--check{color:#1da1f2} html.dark .Dropdown-menuItem .Dropdown-menuItemContent .Icon--check{color:#1da1f2}
html.dark .Dropdown-menuItem.is-focus{background-color:#1da1f2} html.dark .Dropdown-menuItem.is-focus{background-color:#1da1f2}
html.dark .Dropdown-menuItem.is-focus .User .Icon--verified::before{color:#1da1f2}
html.dark .Dropdown-menuGroupLabel{color:#657786} html.dark .Dropdown-menuGroupLabel{color:#657786}
html.dark .ButtonGroup>.Button.is-selected,html.dark .ButtonGroup>.Button.is-selected:visited{background-color:#1da1f2;border:1px solid #1da1f2} html.dark .ButtonGroup>.Button.is-selected,html.dark .ButtonGroup>.Button.is-selected:visited{background-color:#1da1f2;border:1px solid #1da1f2}
html.dark .ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2} html.dark .ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup>.Button.is-selected.is-hover{background-color:#1da1f2;border-color:#1da1f2} html.dark .ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup>.Button.is-selected.is-hover{background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background-color:#1da1f2;border-color:#1da1f2} html.dark .ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #1da1f2;background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup>.Button.is-selected{background-color:#1da1f2;border-color:#1da1f2} html.dark .ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup>.Button.is-selected{background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup--tertiary>.Button.is-selected,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected,html.dark .ButtonGroup--tertiary>.Button.is-selected:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:visited{background-color:#657786;border:1px solid #657786} html.dark .ButtonGroup--tertiary>.Button.is-selected,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected,html.dark .ButtonGroup--tertiary>.Button.is-selected:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:visited{background-color:#657786;border:1px solid #657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ccd6dd;background:#657786;border-color:#657786} html.dark .ButtonGroup--tertiary>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #ccd6dd;background:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-hover{background-color:#657786;border-color:#657786} html.dark .ButtonGroup--tertiary>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-hover{background-color:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #aab8c2;background-color:#657786;border-color:#657786} html.dark .ButtonGroup--tertiary>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #aab8c2;background-color:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.Button.is-selected.is-disabled,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button.is-selected,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected{background-color:#657786;border-color:#657786} html.dark .ButtonGroup--tertiary>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.Button.is-selected.is-disabled,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button.is-selected,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected{background-color:#657786;border-color:#657786}
html.dark .Button,html.dark .Button:visited,html.dark .Button.is-visited{border:1px solid #1da1f2;color:#1da1f2} html.dark .Button,html.dark .Button:visited,html.dark .Button.is-visited{border:1px solid #1da1f2;color:#1da1f2}
html.dark .Button:focus,html.dark .Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;border-color:#1da1f2;color:#1da1f2} html.dark .Button:focus,html.dark .Button.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #71c9f8;border-color:#1da1f2;color:#1da1f2}
html.dark .Button:hover,html.dark .Button.is-hover{background-color:#eaf5fd;color:#1da1f2} html.dark .Button:hover,html.dark .Button.is-hover{background-color:#eaf5fd;color:#1da1f2}
html.dark .Button:active,html.dark .Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background:#eaf5fd;border-color:#1da1f2;color:#1da1f2} html.dark .Button:active,html.dark .Button.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #1da1f2;background:#eaf5fd;border-color:#1da1f2;color:#1da1f2}
html.dark .Button.Button--primary,html.dark .Button.Button--primary:visited,html.dark .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.Button:visited,html.dark .ButtonGroup--primary>.ButtonGroup>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:visited{background-color:#1da1f2;border:1px solid #1da1f2} html.dark .Button.Button--primary,html.dark .Button.Button--primary:visited,html.dark .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.Button:visited,html.dark .ButtonGroup--primary>.ButtonGroup>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:visited{background-color:#1da1f2;border:1px solid #1da1f2}
html.dark .Button.Button--primary:focus,html.dark .Button.Button--primary.is-focus,html.dark .ButtonGroup--primary>.Button:focus,html.dark .ButtonGroup--primary>.Button.is-focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2} html.dark .Button.Button--primary:focus,html.dark .Button.Button--primary.is-focus,html.dark .ButtonGroup--primary>.Button:focus,html.dark .ButtonGroup--primary>.Button.is-focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2}
html.dark .Button.Button--primary:hover,html.dark .Button.Button--primary.is-hover,html.dark .ButtonGroup--primary>.Button:hover,html.dark .ButtonGroup--primary>.Button.is-hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-hover{background-color:#005fd1;border-color:#005fd1} html.dark .Button.Button--primary:hover,html.dark .Button.Button--primary.is-hover,html.dark .ButtonGroup--primary>.Button:hover,html.dark .ButtonGroup--primary>.Button.is-hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-hover{background-color:#005fd1;border-color:#005fd1}
html.dark .Button.Button--primary:active,html.dark .Button.Button--primary.is-active,html.dark .ButtonGroup--primary>.Button:active,html.dark .ButtonGroup--primary>.Button.is-active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background-color:#005fd1;border-color:#005fd1} html.dark .Button.Button--primary:active,html.dark .Button.Button--primary.is-active,html.dark .ButtonGroup--primary>.Button:active,html.dark .ButtonGroup--primary>.Button.is-active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #1da1f2;background-color:#005fd1;border-color:#005fd1}
html.dark .Button.Button--primary[disabled],html.dark .Button.Button--primary.is-disabled,html.dark fieldset[disabled] .Button.Button--primary,html.dark .ButtonGroup--primary>.Button[disabled],html.dark .ButtonGroup--primary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.ButtonGroup>.Button{background-color:#1da1f2;border-color:#1da1f2} html.dark .Button.Button--primary[disabled],html.dark .Button.Button--primary.is-disabled,html.dark fieldset[disabled] .Button.Button--primary,html.dark .ButtonGroup--primary>.Button[disabled],html.dark .ButtonGroup--primary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.ButtonGroup>.Button{background-color:#1da1f2;border-color:#1da1f2}
html.dark .Button.Button--tertiary,html.dark .Button.Button--tertiary:visited,html.dark .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.Button:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:visited{border:1px solid #657786;color:#657786} html.dark .Button.Button--tertiary,html.dark .Button.Button--tertiary:visited,html.dark .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.Button:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:visited{border:1px solid #657786;color:#657786}
html.dark .Button.Button--tertiary:focus,html.dark .Button.Button--tertiary.is-focus,html.dark .ButtonGroup--tertiary>.Button:focus,html.dark .ButtonGroup--tertiary>.Button.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ccd6dd;border-color:#657786;color:#657786} html.dark .Button.Button--tertiary:focus,html.dark .Button.Button--tertiary.is-focus,html.dark .ButtonGroup--tertiary>.Button:focus,html.dark .ButtonGroup--tertiary>.Button.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #ccd6dd;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary:hover,html.dark .Button.Button--tertiary.is-hover,html.dark .ButtonGroup--tertiary>.Button:hover,html.dark .ButtonGroup--tertiary>.Button.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-hover{background-color:#f5f8fa;border-color:#657786;color:#657786} html.dark .Button.Button--tertiary:hover,html.dark .Button.Button--tertiary.is-hover,html.dark .ButtonGroup--tertiary>.Button:hover,html.dark .ButtonGroup--tertiary>.Button.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-hover{background-color:#f5f8fa;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary:active,html.dark .Button.Button--tertiary.is-active,html.dark .ButtonGroup--tertiary>.Button:active,html.dark .ButtonGroup--tertiary>.Button.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #657786;background-color:#f5f8fa;border-color:#657786;color:#657786} html.dark .Button.Button--tertiary:active,html.dark .Button.Button--tertiary.is-active,html.dark .ButtonGroup--tertiary>.Button:active,html.dark .ButtonGroup--tertiary>.Button.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #657786;background-color:#f5f8fa;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary[disabled],html.dark .Button.Button--tertiary.is-disabled,html.dark fieldset[disabled] .Button.Button--tertiary,html.dark .ButtonGroup--tertiary>.Button[disabled],html.dark .ButtonGroup--tertiary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button{border-color:#657786} html.dark .Button.Button--tertiary[disabled],html.dark .Button.Button--tertiary.is-disabled,html.dark fieldset[disabled] .Button.Button--tertiary,html.dark .ButtonGroup--tertiary>.Button[disabled],html.dark .ButtonGroup--tertiary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button{border-color:#657786}
html.dark .Button.Button--success,html.dark .Button.Button--success:visited{background-color:#17bf63;border:1px solid #17bf63} html.dark .Button.Button--success,html.dark .Button.Button--success:visited{background-color:#17bf63;border:1px solid #17bf63}
html.dark .Button.Button--success:focus,html.dark .Button.Button--success.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #68e090;background:#17bf63;border-color:#17bf63} html.dark .Button.Button--success:focus,html.dark .Button.Button--success.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #68e090;background:#17bf63;border-color:#17bf63}
html.dark .Button.Button--success:hover,html.dark .Button.Button--success.is-hover{background-color:#008951;border-color:#008951} html.dark .Button.Button--success:hover,html.dark .Button.Button--success.is-hover{background-color:#008951;border-color:#008951}
html.dark .Button.Button--success:active,html.dark .Button.Button--success.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #17bf63;background-color:#008951;border-color:#008951} html.dark .Button.Button--success:active,html.dark .Button.Button--success.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #17bf63;background-color:#008951;border-color:#008951}
html.dark .Button.Button--success[disabled],html.dark .Button.Button--success.is-disabled,html.dark fieldset[disabled] .Button.Button--success{background-color:#17bf63;border-color:#17bf63} html.dark .Button.Button--success[disabled],html.dark .Button.Button--success.is-disabled,html.dark fieldset[disabled] .Button.Button--success{background-color:#17bf63;border-color:#17bf63}
html.dark .Button.Button--warning,html.dark .Button.Button--warning:visited{background-color:#ffad1f;border:1px solid #ffad1f} html.dark .Button.Button--warning,html.dark .Button.Button--warning:visited{background-color:#ffad1f;border:1px solid #ffad1f}
html.dark .Button.Button--warning:focus,html.dark .Button.Button--warning.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ffd03f;background:#ffad1f;border-color:#ffad1f} html.dark .Button.Button--warning:focus,html.dark .Button.Button--warning.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #ffd03f;background:#ffad1f;border-color:#ffad1f}
html.dark .Button.Button--warning:hover,html.dark .Button.Button--warning.is-hover{background-color:#f98e00;border-color:#f98e00} html.dark .Button.Button--warning:hover,html.dark .Button.Button--warning.is-hover{background-color:#f98e00;border-color:#f98e00}
html.dark .Button.Button--warning:active,html.dark .Button.Button--warning.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #ffad1f;background-color:#f98e00;border-color:#f98e00} html.dark .Button.Button--warning:active,html.dark .Button.Button--warning.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #ffad1f;background-color:#f98e00;border-color:#f98e00}
html.dark .Button.Button--warning[disabled],html.dark .Button.Button--warning.is-disabled,html.dark fieldset[disabled] .Button.Button--warning{background-color:#ffad1f;border-color:#ffad1f} html.dark .Button.Button--warning[disabled],html.dark .Button.Button--warning.is-disabled,html.dark fieldset[disabled] .Button.Button--warning{background-color:#ffad1f;border-color:#ffad1f}
html.dark .Button.Button--danger,html.dark .Button.Button--danger:visited{background-color:#e0245e;border:1px solid #e0245e} html.dark .Button.Button--danger,html.dark .Button.Button--danger:visited{background-color:#e0245e;border:1px solid #e0245e}
html.dark .Button.Button--danger:focus,html.dark .Button.Button--danger.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #f6809a;background:#e0245e;border-color:#e0245e} html.dark .Button.Button--danger:focus,html.dark .Button.Button--danger.is-focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #f6809a;background:#e0245e;border-color:#e0245e}
html.dark .Button.Button--danger:hover,html.dark .Button.Button--danger.is-hover{background-color:#a01744;border-color:#a01744} html.dark .Button.Button--danger:hover,html.dark .Button.Button--danger.is-hover{background-color:#a01744;border-color:#a01744}
html.dark .Button.Button--danger:active,html.dark .Button.Button--danger.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #e0245e;background-color:#a01744;border-color:#a01744} html.dark .Button.Button--danger:active,html.dark .Button.Button--danger.is-active{box-shadow:0 0 0 2px #fff,0 0 0 4px #e0245e;background-color:#a01744;border-color:#a01744}
html.dark .Button.Button--danger[disabled],html.dark .Button.Button--danger.is-disabled,html.dark fieldset[disabled] .Button.Button--danger{background-color:#e0245e;border-color:#e0245e} html.dark .Button.Button--danger[disabled],html.dark .Button.Button--danger.is-disabled,html.dark fieldset[disabled] .Button.Button--danger{background-color:#e0245e;border-color:#e0245e}
html.dark .Button.Button--link{color:#1b95e0} html.dark .Button.Button--link{color:#1b95e0}
html.dark .Button.Button--dangerLink{color:#e0245e} html.dark .Button.Button--dangerLink{color:#e0245e}
@@ -730,8 +722,8 @@ html.dark .DateRange[dir='rtl'] .DateRange-pickersRow:first-child .DateRange-pic
html.dark .PillGroup .Pill.is-selected{background:#005fd1} html.dark .PillGroup .Pill.is-selected{background:#005fd1}
html.dark .PillGroup .Pill>a,html.dark .PillGroup .Pill>button{color:#1b95e0} html.dark .PillGroup .Pill>a,html.dark .PillGroup .Pill>button{color:#1b95e0}
html.dark .PillGroup .Pill>a:hover,html.dark .PillGroup .Pill>button:hover{background:#eaf5fd} html.dark .PillGroup .Pill>a:hover,html.dark .PillGroup .Pill>button:hover{background:#eaf5fd}
html.dark .PillGroup .Pill>a:focus,html.dark .PillGroup .Pill>button:focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8} html.dark .PillGroup .Pill>a:focus,html.dark .PillGroup .Pill>button:focus{box-shadow:0 0 0 2px #fff,0 0 0 4px #71c9f8}
html.dark .PillGroup .Pill>a:active,html.dark .PillGroup .Pill>button:active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2} html.dark .PillGroup .Pill>a:active,html.dark .PillGroup .Pill>button:active{box-shadow:0 0 0 2px #fff,0 0 0 4px #1da1f2}
html.dark .PillGroup .Pill.is-selected>a,html.dark .PillGroup .Pill.is-selected>button{color:#FFF} html.dark .PillGroup .Pill.is-selected>a,html.dark .PillGroup .Pill.is-selected>button{color:#FFF}
html.dark .FormInput,html.dark .FormTextarea{border:1px solid #ccd6dd;color:#14171a} html.dark .FormInput,html.dark .FormTextarea{border:1px solid #ccd6dd;color:#14171a}
html.dark .FormInput-characterCount{color:#ccd6dd} html.dark .FormInput-characterCount{color:#ccd6dd}
@@ -751,51 +743,51 @@ html.dark .FormInputWrapper-startAdornment,html.dark .FormInputWrapper-endAdornm
html.dark .FormField.is-invalid .FormField-validationMessage{color:#e0245e} html.dark .FormField.is-invalid .FormField-validationMessage{color:#e0245e}
html.dark .FormField.is-valid .FormField-validationMessage{color:#008951} html.dark .FormField.is-valid .FormField-validationMessage{color:#008951}
html.dark .FormField-description{color:#657786} html.dark .FormField-description{color:#657786}
html.dark .Token-checkbox input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 5px white,0 0 0 7px #71c9f8} html.dark .Token-checkbox input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 5px #fff,0 0 0 7px #71c9f8}
html.dark .Token--small .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 3px white,0 0 0 5px #71c9f8} html.dark .Token--small .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 3px #fff,0 0 0 5px #71c9f8}
html.dark .Token--xsmall .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 0 white,0 0 0 2px #71c9f8} html.dark .Token--xsmall .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 0 #fff,0 0 0 2px #71c9f8}
html.dark .Token,html.dark .Token--blue{border-color:#1da1f2;color:#1da1f2} html.dark .Token,html.dark .Token--blue{border-color:#1da1f2;color:#1da1f2}
html.dark .Token .Token-adornment,html.dark .Token--blue .Token-adornment{background-color:#1da1f2} html.dark .Token .Token-adornment,html.dark .Token--blue .Token-adornment{background-color:#1da1f2}
html.dark .Token:hover,html.dark .Token--blue:hover{background-color:#97e3ff;color:#005fd1} html.dark .Token:hover,html.dark .Token--blue:hover{background-color:#97e3ff;color:#005fd1}
html.dark .Token.is-selected,html.dark .Token--blue.is-selected{background-color:#1da1f2} html.dark .Token.is-selected,html.dark .Token--blue.is-selected{background-color:#1da1f2}
html.dark .Token.is-selected .Token-adornment,html.dark .Token--blue.is-selected .Token-adornment{color:#1da1f2} html.dark .Token.is-selected .Token-adornment,html.dark .Token--blue.is-selected .Token-adornment{color:#1da1f2}
html.dark .Token.is-selected:hover,html.dark .Token--blue.is-selected:hover{background-color:#005fd1;border-color:#005fd1} html.dark .Token.is-selected:hover,html.dark .Token--blue.is-selected:hover{background-color:#005fd1;border-color:#005fd1}
html.dark .Token:focus,html.dark .Token--blue:focus,html.dark .Token.is-focused,html.dark .Token--blue.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #71c9f8} html.dark .Token:focus,html.dark .Token--blue:focus,html.dark .Token.is-focused,html.dark .Token--blue.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #71c9f8}
html.dark .Token--green{border-color:#17bf63;color:#17bf63} html.dark .Token--green{border-color:#17bf63;color:#17bf63}
html.dark .Token--green .Token-adornment{background-color:#17bf63} html.dark .Token--green .Token-adornment{background-color:#17bf63}
html.dark .Token--green:hover{background-color:#a5f2aa;color:#008951} html.dark .Token--green:hover{background-color:#a5f2aa;color:#008951}
html.dark .Token--green.is-selected{background-color:#17bf63} html.dark .Token--green.is-selected{background-color:#17bf63}
html.dark .Token--green.is-selected .Token-adornment{color:#17bf63} html.dark .Token--green.is-selected .Token-adornment{color:#17bf63}
html.dark .Token--green.is-selected:hover{background-color:#008951;border-color:#008951} html.dark .Token--green.is-selected:hover{background-color:#008951;border-color:#008951}
html.dark .Token--green:focus,html.dark .Token--green.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #68e090} html.dark .Token--green:focus,html.dark .Token--green.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #68e090}
html.dark .Token--red{border-color:#e0245e;color:#e0245e} html.dark .Token--red{border-color:#e0245e;color:#e0245e}
html.dark .Token--red .Token-adornment{background-color:#e0245e} html.dark .Token--red .Token-adornment{background-color:#e0245e}
html.dark .Token--red:hover{background-color:#ffb8c2;color:#a01744} html.dark .Token--red:hover{background-color:#ffb8c2;color:#a01744}
html.dark .Token--red.is-selected{background-color:#e0245e} html.dark .Token--red.is-selected{background-color:#e0245e}
html.dark .Token--red.is-selected .Token-adornment{color:#e0245e} html.dark .Token--red.is-selected .Token-adornment{color:#e0245e}
html.dark .Token--red.is-selected:hover{background-color:#a01744;border-color:#a01744} html.dark .Token--red.is-selected:hover{background-color:#a01744;border-color:#a01744}
html.dark .Token--red:focus,html.dark .Token--red.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #f6809a} html.dark .Token--red:focus,html.dark .Token--red.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #f6809a}
html.dark .Token--purple{border-color:#794bc4;color:#794bc4} html.dark .Token--purple{border-color:#794bc4;color:#794bc4}
html.dark .Token--purple .Token-adornment{background-color:#794bc4} html.dark .Token--purple .Token-adornment{background-color:#794bc4}
html.dark .Token--purple:hover{background-color:#c7b4fa;color:#4f0299} html.dark .Token--purple:hover{background-color:#c7b4fa;color:#4f0299}
html.dark .Token--purple.is-selected{background-color:#794bc4} html.dark .Token--purple.is-selected{background-color:#794bc4}
html.dark .Token--purple.is-selected .Token-adornment{color:#794bc4} html.dark .Token--purple.is-selected .Token-adornment{color:#794bc4}
html.dark .Token--purple.is-selected:hover{background-color:#4f0299;border-color:#4f0299} html.dark .Token--purple.is-selected:hover{background-color:#4f0299;border-color:#4f0299}
html.dark .Token--purple:focus,html.dark .Token--purple.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #a37ced} html.dark .Token--purple:focus,html.dark .Token--purple.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #a37ced}
html.dark .Token--yellow{border-color:#ffad1f;color:#ffad1f} html.dark .Token--yellow{border-color:#ffad1f;color:#ffad1f}
html.dark .Token--yellow .Token-adornment{background-color:#ffad1f} html.dark .Token--yellow .Token-adornment{background-color:#ffad1f}
html.dark .Token--yellow:hover{background-color:#ffe76e;color:#f98e00} html.dark .Token--yellow:hover{background-color:#ffe76e;color:#f98e00}
html.dark .Token--yellow.is-selected{background-color:#ffad1f} html.dark .Token--yellow.is-selected{background-color:#ffad1f}
html.dark .Token--yellow.is-selected .Token-adornment{color:#ffad1f} html.dark .Token--yellow.is-selected .Token-adornment{color:#ffad1f}
html.dark .Token--yellow.is-selected:hover{background-color:#f98e00;border-color:#f98e00} html.dark .Token--yellow.is-selected:hover{background-color:#f98e00;border-color:#f98e00}
html.dark .Token--yellow:focus,html.dark .Token--yellow.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #ffd03f} html.dark .Token--yellow:focus,html.dark .Token--yellow.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #ffd03f}
html.dark .Token--gray{border-color:#657786;color:#657786} html.dark .Token--gray{border-color:#657786;color:#657786}
html.dark .Token--gray .Token-adornment{background-color:#657786} html.dark .Token--gray .Token-adornment{background-color:#657786}
html.dark .Token--gray:hover{background-color:#e6ecf0;color:#657786} html.dark .Token--gray:hover{background-color:#e6ecf0;color:#657786}
html.dark .Token--gray.is-selected{background-color:#657786} html.dark .Token--gray.is-selected{background-color:#657786}
html.dark .Token--gray.is-selected .Token-adornment{color:#657786} html.dark .Token--gray.is-selected .Token-adornment{color:#657786}
html.dark .Token--gray.is-selected:hover{background-color:#aab8c2;border-color:#aab8c2} html.dark .Token--gray.is-selected:hover{background-color:#aab8c2;border-color:#aab8c2}
html.dark .Token--gray:focus,html.dark .Token--gray.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #aab8c2} html.dark .Token--gray:focus,html.dark .Token--gray.is-focused{box-shadow:0 0 0 1px #fff,0 0 0 3px #aab8c2}
html.dark .FormTokenInput-input::-webkit-input-placeholder{color:#aab8c2} html.dark .FormTokenInput-input::-webkit-input-placeholder{color:#aab8c2}
html.dark .DataPoint .DataPoint-label{color:#657786} html.dark .DataPoint .DataPoint-label{color:#657786}
html.dark .DataPoint .DataPoint-info{color:#14171a} html.dark .DataPoint .DataPoint-info{color:#14171a}
@@ -805,7 +797,7 @@ html.dark .DataPoint--withBottomBorder{border-bottom:1px solid #ccd6dd}
html.dark .FormTokenInput.FormTextarea::-webkit-input-placeholder{color:#8899A6} html.dark .FormTokenInput.FormTextarea::-webkit-input-placeholder{color:#8899A6}
html.dark .FormTokenInput.FormTextarea::placeholder{color:#8899A6} html.dark .FormTokenInput.FormTextarea::placeholder{color:#8899A6}
html.dark .DatePicker.date-unselected .is-rangeStart,html.dark .DatePicker.date-unselected .is-rangeEnd{color:#14171a} html.dark .DatePicker.date-unselected .is-rangeStart,html.dark .DatePicker.date-unselected .is-rangeEnd{color:#14171a}
html.dark .DatePicker.date-unselected .is-rangeStart:hover,html.dark .DatePicker.date-unselected .is-rangeEnd:hover{background-color:#005091;color:#ffffff} html.dark .DatePicker.date-unselected .is-rangeStart:hover,html.dark .DatePicker.date-unselected .is-rangeEnd:hover{background-color:#005091;color:#fff}
html.dark .NotificationList .Notification-body{color:#14171A} html.dark .NotificationList .Notification-body{color:#14171A}
html.dark .DrawerModal{color:#14171A} html.dark .DrawerModal{color:#14171A}
/* fixes */ /* fixes */

View File

@@ -26,7 +26,7 @@ enabled(){
this.emojiData3 = []; // no skin tones, appended this.emojiData3 = []; // no skin tones, appended
this.emojiNames = []; this.emojiNames = [];
var me = this; const me = this;
// styles // styles
@@ -51,12 +51,12 @@ enabled(){
// layout // layout
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--6 padding-h--8 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>'; let buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--6 padding-h--8 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML); window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
var maybeDockedComposePanel = $(".js-docked-compose"); let maybeDockedComposePanel = $(".js-docked-compose");
if (maybeDockedComposePanel.length){ if (maybeDockedComposePanel.length){
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML); maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
@@ -67,10 +67,10 @@ enabled(){
this.currentKeyboard = null; this.currentKeyboard = null;
this.currentSpanner = null; this.currentSpanner = null;
var wasSearchFocused = false; let wasSearchFocused = false;
var lastEmojiKeyword, lastEmojiPosition, lastEmojiLength; let lastEmojiKeyword, lastEmojiPosition, lastEmojiLength;
var hideKeyboard = (refocus) => { const hideKeyboard = (refocus) => {
$(this.currentKeyboard).remove(); $(this.currentKeyboard).remove();
this.currentKeyboard = null; this.currentKeyboard = null;
@@ -94,7 +94,7 @@ enabled(){
lastEmojiKeyword = null; lastEmojiKeyword = null;
}; };
var generateEmojiHTML = skinTone => { const generateEmojiHTML = skinTone => {
let index = 0; let index = 0;
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ]; let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ];
@@ -113,7 +113,7 @@ enabled(){
return html.join(""); return html.join("");
}; };
var updateFilters = () => { const updateFilters = () => {
let keywords = this.currentKeywords; let keywords = this.currentKeywords;
let container = $(this.currentKeyboard.children[1]); let container = $(this.currentKeyboard.children[1]);
@@ -135,7 +135,7 @@ enabled(){
} }
}; };
var selectSkinTone = skinTone => { const selectSkinTone = skinTone => {
let selectedEle = this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']"); let selectedEle = this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']");
selectedEle && selectedEle.classList.remove("sel"); selectedEle && selectedEle.classList.remove("sel");
@@ -146,12 +146,12 @@ enabled(){
}; };
this.generateKeyboard = (left, top) => { this.generateKeyboard = (left, top) => {
var outer = document.createElement("div"); let outer = document.createElement("div");
outer.classList.add("emoji-keyboard"); outer.classList.add("emoji-keyboard");
outer.style.left = left+"px"; outer.style.left = left+"px";
outer.style.top = top+"px"; outer.style.top = top+"px";
var keyboard = document.createElement("div"); let keyboard = document.createElement("div");
keyboard.classList.add("emoji-keyboard-list"); keyboard.classList.add("emoji-keyboard-list");
keyboard.addEventListener("click", function(e){ keyboard.addEventListener("click", function(e){
@@ -164,11 +164,11 @@ enabled(){
e.stopPropagation(); e.stopPropagation();
}); });
var search = document.createElement("div"); let search = document.createElement("div");
search.innerHTML = "<input type='text' placeholder='Search...'>"; search.innerHTML = "<input type='text' placeholder='Search...'>";
search.classList.add("emoji-keyboard-search"); search.classList.add("emoji-keyboard-search");
var skintones = document.createElement("div"); let skintones = document.createElement("div");
skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join(""); skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join("");
skintones.classList.add("emoji-keyboard-skintones"); skintones.classList.add("emoji-keyboard-skintones");
@@ -189,7 +189,7 @@ enabled(){
e.stopPropagation(); e.stopPropagation();
}); });
var searchInput = search.children[0]; let searchInput = search.children[0];
searchInput.focus(); searchInput.focus();
wasSearchFocused = false; wasSearchFocused = false;
@@ -232,12 +232,12 @@ enabled(){
this.composePanelScroller.trigger("scroll"); this.composePanelScroller.trigger("scroll");
}; };
var getKeyboardTop = () => { const getKeyboardTop = () => {
let button = $(".emoji-keyboard-popup-btn"); let button = $(".emoji-keyboard-popup-btn");
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8; return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
}; };
var insertEmoji = (src, alt) => { const insertEmoji = (src, alt) => {
let input = this.composeInput; let input = this.composeInput;
let val = input.val(); let val = input.val();
@@ -418,7 +418,7 @@ ready(){
// HTML generation // HTML generation
var convUnicode = function(codePt){ const convUnicode = function(codePt){
if (codePt > 0xFFFF){ if (codePt > 0xFFFF){
codePt -= 0x10000; codePt -= 0x10000;
return String.fromCharCode(0xD800+(codePt>>10), 0xDC00+(codePt&0x3FF)); return String.fromCharCode(0xD800+(codePt>>10), 0xDC00+(codePt&0x3FF));

View File

@@ -1,5 +1,5 @@
enabled(){ enabled(){
var configuration = { defaultAccount: "#preferred" }; let configuration = { defaultAccount: "#preferred" };
window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj); window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj);
@@ -8,7 +8,7 @@ enabled(){
this.uiComposeTweetEvent = (e, data) => { this.uiComposeTweetEvent = (e, data) => {
return if !(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data); return if !(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data);
var query; let query;
if (configuration.useAdvancedSelector){ if (configuration.useAdvancedSelector){
if (configuration.customSelector){ if (configuration.customSelector){
@@ -23,25 +23,34 @@ enabled(){
return; return;
} }
var section = data.element.closest("section.js-column"); let section = data.element.closest("section.js-column");
let column = TD.controller.columnManager.get(section.attr("data-column"));
var column = TD.controller.columnManager.get(section.attr("data-column")); let feeds = column.getFeeds();
var header = $(".column-title", section); let accountText = "";
var title = header.children(".column-head-title");
var columnTitle, columnAccount; if (feeds.length === 1){
let metadata = feeds[0].getMetadata();
let id = metadata.ownerId || metadata.id;
if (title.length){ if (id){
columnTitle = title.text(); accountText = TD.cache.names.getScreenName(id);
columnAccount = header.children(".attribution").text();
} }
else{ else{
columnTitle = header.children(".column-title-edit-box").val(); let account = TD.storage.accountController.get(feeds[0].getAccountKey());
columnAccount = "";
if (account){
accountText = "@"+account.getUsername();
}
}
} }
let header = $(".column-header-title", section);
let title = header.children(".column-heading");
let titleText = title.length ? title.text() : header.children(".column-title-edit-box").val();
try{ try{
query = configuration.customSelector(columnTitle, columnAccount, column, section.hasClass("column-temp")); query = configuration.customSelector(titleText, accountText, column, section.hasClass("column-temp"));
}catch(e){ }catch(e){
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message); $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
return; return;
@@ -79,7 +88,7 @@ enabled(){
return; return;
} }
var identifier = null; let identifier = null;
switch(query){ switch(query){
case "#preferred": case "#preferred":
@@ -97,7 +106,7 @@ enabled(){
default: default:
if (query[0] === '@'){ if (query[0] === '@'){
var obj = TD.storage.accountController.getAccountFromUsername(query.substring(1)); let obj = TD.storage.accountController.getAccountFromUsername(query.substring(1));
if (obj.length === 0){ if (obj.length === 0){
$TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: "+query); $TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: "+query);
@@ -117,7 +126,7 @@ enabled(){
}; };
this.onSelectedAccountChanged = () => { this.onSelectedAccountChanged = () => {
var selected = $(".js-account-item.is-selected", ".js-account-list"); let selected = $(".js-account-item.is-selected", ".js-account-list");
this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null; this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null;
}; };
} }

View File

@@ -32,62 +32,32 @@ enabled(){
}); });
}; };
// setup
this.htmlModal = null;
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
this.htmlModal = contents;
}).catch(err => {
$TD.alert("error", "Problem loading data for the template plugin: "+err.message);
});
// button // button
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>'; let buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="js-tweet-type-button">', buttonHTML+'<div class="js-tweet-type-button">'); window.TDPF_injectMustache("compose/docked_compose.mustache", "prepend", '<div class="js-tweet-type-button">', buttonHTML);
var dockedComposePanel = $(".js-docked-compose"); let dockedComposePanel = $(".js-docked-compose");
if (dockedComposePanel.length){ if (dockedComposePanel.length){
dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML); dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML);
} }
// css
this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
this.css.insert("#templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
this.css.insert("#templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
this.css.insert("#templates-modal > div { display: flex; flex-direction: column; }");
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
this.css.insert("#template-list .templates-modal-bottom { display: flex; justify-content: space-between; }");
this.css.insert("#template-editor .templates-modal-bottom { text-align: right; }");
this.css.insert("#template-list { height: 100%; flex: 1 1 auto; }");
this.css.insert("#template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
this.css.insert("#template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
this.css.insert("#template-list li[data-template] { cursor: pointer; }");
this.css.insert("#template-list li[data-template]:hover { background-color: #d8d8d8; }");
this.css.insert("#template-list li span { white-space: nowrap; }");
this.css.insert("#template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
this.css.insert("#template-list li .icon:hover { opacity: 1; }");
this.css.insert("#template-list li .template-actions { float: right; }");
this.css.insert("#template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }");
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
this.css.insert(".template-editor-form input, .template-editor-form textarea { color: #111; background-color: #fff; border: none; border-radius: 0; }");
this.css.insert(".template-editor-form input:focus, .template-editor-form textarea:focus { box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6); }");
this.css.insert(".template-editor-form textarea { height: 146px; font-size: 14px; padding: 10px; resize: none; }");
this.css.insert(".template-editor-form .template-editor-tips-button { cursor: pointer; }");
this.css.insert(".template-editor-form .template-editor-tips-button .icon { font-size: 12px; vertical-align: -5%; margin-left: 4px; }");
this.css.insert(".template-editor-form .template-editor-tips { display: none; }");
this.css.insert(".template-editor-form .template-editor-tips p { margin: 10px 0; }");
this.css.insert(".template-editor-form .template-editor-tips p:first-child { margin-top: 0; }");
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n+1) { margin-top: 5px; padding-left: 6px; font-family: monospace; }");
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n) { margin-top: 1px; padding-left: 14px; opacity: 0.66; }");
this.css.insert(".invisible { display: none !important; }");
// template implementation // template implementation
var readTemplateTokens = (contents, tokenData) => { const readTemplateTokens = (contents, tokenData) => {
let startIndex = -1; let startIndex = -1;
let endIndex = -1; let endIndex = -1;
@@ -155,7 +125,7 @@ enabled(){
return [ contents, data ]; return [ contents, data ];
}; };
var doAjaxRequest = (index, url, evaluator) => { const doAjaxRequest = (index, url, evaluator) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!url){ if (!url){
resolve([ index, "{ajax}" ]); resolve([ index, "{ajax}" ]);
@@ -175,7 +145,7 @@ enabled(){
}); });
}; };
var useTemplate = (contents, append) => { const useTemplate = (contents, append) => {
let ele = $(".js-compose-text"); let ele = $(".js-compose-text");
return if ele.length === 0; return if ele.length === 0;
@@ -257,55 +227,8 @@ enabled(){
this.editingTemplate = null; this.editingTemplate = null;
var showTemplateModal = () => { const showTemplateModal = () => {
$(".manage-templates-btn").addClass("active"); $(".js-app-content").prepend(this.htmlModal);
let html = `
<div id="templates-modal-wrap">
<div id="templates-modal">
<div id="template-list">
<ul></ul>
<div class="templates-modal-bottom">
<button data-action="close" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
</div>
</div>
<div id="template-editor" class="invisible">
<div class="template-editor-form">
<div class="compose-text-title">Template Name</div>
<input name="template-name" type="text">
<div class="compose-text-title">Contents</div>
<textarea name="template-contents" class="compose-text scroll-v scroll-styled-v scroll-styled-h scroll-alt"></textarea>
<div class="compose-text-title template-editor-tips-button">Advanced <i class="icon icon-arrow-d"></i></div>
<div class="template-editor-tips">
<p>You can use the following tokens. All tokens except for <span style="font-family: monospace">{ajax}</span> can only be used once.</p>
<ul>
<li>{cursor}</li>
<li>Location where the cursor is placed</li>
<li>{cursor#&lt;selectionlength&gt;}</li>
<li>Places cursor and selects a set amount of characters</li>
<li>{ajax#&lt;url&gt;}</li>
<li>Replaced with the result of a cross-origin ajax request</li>
<li>{ajax#&lt;eval&gt;#&lt;url&gt;}</li>
<li>Allows parsing the ajax request using <span style="font-family: monospace">$</span> as the placeholder for the result<br>Example: <span style="font-family: monospace">$.substring(0,5)</span></li>
</ul>
<p>To use special characters in the tweet text, escape them with a backslash:
<br><span style="font-family: monospace">&nbsp; \\{&nbsp; \\}&nbsp; \\#&nbsp; \\\\</span>
</p>
</div>
</div>
<div class="templates-modal-bottom">
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
<button data-action="editor-confirm" class="btn btn-positive" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
</div>
</div>
</div>
</div>`;
/* TODO possibly implement this later /* TODO possibly implement this later
@@ -318,8 +241,6 @@ enabled(){
*/ */
$(".js-app-content").prepend(html);
let ele = $("#templates-modal-wrap").first(); let ele = $("#templates-modal-wrap").first();
ele.on("click", "li[data-template]", function(e){ ele.on("click", "li[data-template]", function(e){
@@ -407,12 +328,11 @@ enabled(){
onTemplatesUpdated(false); onTemplatesUpdated(false);
}; };
var hideTemplateModal = function(){ const hideTemplateModal = () => {
$("#templates-modal-wrap").remove(); $("#templates-modal-wrap").remove();
$(".manage-templates-btn").removeClass("active");
}; };
var toggleEditor = function(){ const toggleEditor = () => {
let editor = $("#template-editor"); let editor = $("#template-editor");
$("[name]", editor).val(""); $("[name]", editor).val("");
@@ -421,7 +341,7 @@ enabled(){
} }
}; };
var onTemplatesUpdated = (save) => { const onTemplatesUpdated = (save) => {
let eles = []; let eles = [];
for(let identifier of Object.keys(this.config.templates)){ for(let identifier of Object.keys(this.config.templates)){
@@ -468,8 +388,6 @@ ready(){
} }
disabled(){ disabled(){
this.css.remove();
$(".manage-templates-btn").remove(); $(".manage-templates-btn").remove();
$("#templates-modal-wrap").remove(); $("#templates-modal-wrap").remove();

View File

@@ -0,0 +1,227 @@
<div id="templates-modal-wrap" class="scroll-v scroll-styled-v">
<div id="templates-modal">
<div id="template-list">
<ul></ul>
<div class="templates-modal-bottom">
<button data-action="close" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
<button data-action="new-template" class="Button--primary"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
</div>
</div>
<div id="template-editor" class="invisible">
<div class="template-editor-form">
<div class="compose-text-title">Template Name</div>
<input name="template-name" type="text">
<div class="compose-text-title">Contents</div>
<textarea name="template-contents" class="compose-text scroll-v scroll-styled-v scroll-styled-h scroll-alt"></textarea>
<div class="compose-text-title template-editor-tips-button">Advanced <i class="icon icon-arrow-d"></i></div>
<div class="template-editor-tips">
<p>You can use the following tokens. All tokens except for <span style="font-family: monospace">{ajax}</span> can only be used once.</p>
<ul>
<li>{cursor}</li>
<li>Location where the cursor is placed</li>
<li>{cursor#&lt;selectionlength&gt;}</li>
<li>Places cursor and selects a set amount of characters</li>
<li>{ajax#&lt;url&gt;}</li>
<li>Replaced with the result of a cross-origin ajax request</li>
<li>{ajax#&lt;eval&gt;#&lt;url&gt;}</li>
<li>Allows parsing the ajax request using <span style="font-family: monospace">$</span> as the placeholder for the result<br>Example: <span style="font-family: monospace">$.substring(0,5)</span></li>
</ul>
<p>To use special characters in the tweet text, escape them with a backslash:
<br><span style="font-family: monospace">&nbsp; \{&nbsp; \}&nbsp; \#&nbsp; \\</span>
</p>
</div>
</div>
<div class="templates-modal-bottom">
<button data-action="editor-cancel" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
<button data-action="editor-confirm" class="Button--primary" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
</div>
</div>
</div>
<style type="text/css">
/* General */
#templates-modal-wrap {
width: 100%;
height: 100%;
padding: 49px;
position: absolute;
z-index: 999;
box-sizing: border-box;
background-color: rgba(0, 0, 0, 0.5);
}
#templates-modal {
width: 100%;
height: 100%;
min-width: 500px;
background-color: #fff;
display: flex;
}
#templates-modal > div {
display: flex;
flex-direction: column;
}
.templates-modal-bottom {
flex: 0 0 auto;
padding: 16px;
}
#template-list .templates-modal-bottom {
display: flex;
justify-content: space-between;
}
#template-editor .templates-modal-bottom {
text-align: right;
}
.manage-templates-btn {
/* modifies the Manage Templates button while open */
color: #fff;
box-shadow: 0 0 2px 3px #50a5e6;
outline: 0;
}
/* Template list */
#template-list {
height: 100%;
flex: 1 1 auto;
}
#template-list ul {
list-style-type: none;
font-size: 24px;
color: #222;
flex: 1 1 auto;
padding: 12px;
overflow-y: auto;
}
#template-list li {
display: block;
width: 100%;
padding: 4px 8px;
box-sizing: border-box;
}
#template-list li[data-template] {
cursor: pointer;
}
#template-list li[data-template]:hover {
background-color: #d8d8d8;
}
#template-list li span {
white-space: nowrap;
}
#template-list li .icon {
opacity: 0.6;
margin-left: 4px;
padding: 3px;
}
#template-list li .icon:hover {
opacity: 1;
}
#template-list li .template-actions {
float: right;
}
/* Template editor */
#template-editor {
height: 100%;
flex: 0 0 auto;
width: 25vw;
min-width: 225px;
max-width: 400px;
background-color: #485865;
}
#template-editor.invisible {
display: none;
}
.template-editor-form {
flex: 1 1 auto;
padding: 12px 16px;
font-size: 14px;
overflow-y: auto;
}
.template-editor-form .compose-text-title {
margin: 24px 0 9px;
}
.template-editor-form .compose-text-title:first-child {
margin-top: 0;
}
.template-editor-form input, .template-editor-form textarea {
color: #111;
background-color: #fff;
border: none;
border-radius: 0;
}
.template-editor-form input:focus, .template-editor-form textarea:focus {
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6);
}
.template-editor-form textarea {
height: 146px;
font-size: 14px;
padding: 10px;
resize: none;
}
/* Template tips */
.template-editor-form .template-editor-tips-button {
cursor: pointer;
}
.template-editor-form .template-editor-tips-button .icon {
font-size: 12px;
vertical-align: -5%;
margin-left: 4px;
}
.template-editor-form .template-editor-tips {
display: none;
}
.template-editor-form .template-editor-tips p {
margin: 10px 0;
}
.template-editor-form .template-editor-tips p:first-child {
margin-top: 0;
}
.template-editor-form .template-editor-tips li:nth-child(2n+1) {
margin-top: 5px;
padding-left: 6px;
font-family: monospace;
}
.template-editor-form .template-editor-tips li:nth-child(2n) {
margin-top: 1px;
padding-left: 14px;
opacity: 0.66;
}
</style>
</div>

View File

@@ -38,7 +38,7 @@ enabled(){
// hooks // hooks
var funcs = { const funcs = {
TwitterStatus: TD.services.TwitterStatus.prototype.render, TwitterStatus: TD.services.TwitterStatus.prototype.render,
TwitterActionOnTweet: TD.services.TwitterActionOnTweet.prototype.render, TwitterActionOnTweet: TD.services.TwitterActionOnTweet.prototype.render,
TweetDetailView: TD.components.TweetDetailView.prototype._renderChirp TweetDetailView: TD.components.TweetDetailView.prototype._renderChirp

View File

@@ -9,14 +9,13 @@ $ErrorActionPreference = "Stop"
try{ try{
$sw = [Diagnostics.Stopwatch]::StartNew() $sw = [Diagnostics.Stopwatch]::StartNew()
Write-Host "--------------------------"
if ($version.Equals("")){ if ($version.Equals("")){
$version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion $version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion
} }
Write-Host "--------------------------"
Write-Host "TweetDuck version" $version Write-Host "TweetDuck version" $version
Write-Host "--------------------------" Write-Host "--------------------------"
# Cleanup # Cleanup
@@ -27,8 +26,7 @@ try{
# Copy resources # Copy resources
Copy-Item (Join-Path $projectDir "bld\Resources\CEFSHARP-LICENSE.txt") -Destination $targetDir -Force Copy-Item (Join-Path $projectDir "bld\Resources\LICENSES.txt") -Destination $targetDir -Force
Copy-Item (Join-Path $projectDir "LICENSE.md") -Destination (Join-Path $targetDir "LICENSE.txt") -Force
New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null
New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null
@@ -45,76 +43,122 @@ try{
# Helper functions # Helper functions
function Check-Carriage-Return{ function Remove-Empty-Lines{
Param( Param([Parameter(Mandatory = $True, Position = 1)] $lines)
[Parameter(Mandatory = $True, Position = 1)] $fname
)
$file = @(Get-ChildItem -Path $targetDir -Include $fname -Recurse)[0] foreach($line in $lines){
if ($line -ne ''){
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){ $line
Throw "$fname cannot contain carriage return" }
}
} }
Write-Host "Verified" $file.FullName.Substring($targetDir.Length) function Check-Carriage-Return{
Param([Parameter(Mandatory = $True, Position = 1)] $file)
if (!(Test-Path $file)){
Throw "$file does not exist"
}
if ((Get-Content -Path $file -Raw).Contains("`r")){
Throw "$file must not have any carriage return characters"
}
Write-Host "Verified" $file.Substring($targetDir.Length)
} }
function Rewrite-File{ function Rewrite-File{
[CmdletBinding()] Param([Parameter(Mandatory = $True, Position = 1)] $file,
Param( [Parameter(Mandatory = $True, Position = 2)] $lines,
[Parameter(Mandatory = $True, Position = 1)] $file, [Parameter(Mandatory = $True, Position = 3)] $imports)
[Parameter(Mandatory = $True, Position = 2)] $lines
)
$lines = Remove-Empty-Lines($lines)
$relativePath = $file.FullName.Substring($targetDir.Length) $relativePath = $file.FullName.Substring($targetDir.Length)
foreach($line in $lines){
if ($line.Contains('#import ')){
$imports.Add($file.FullName)
break
}
}
if ($relativePath.StartsWith("scripts\")){ if ($relativePath.StartsWith("scripts\")){
$lines = (,("#" + $version) + $lines) $lines = (,("#" + $version) + $lines)
} }
$lines = $lines | Where { $_ -ne '' }
[IO.File]::WriteAllLines($file.FullName, $lines) [IO.File]::WriteAllLines($file.FullName, $lines)
Write-Host "Processed" $relativePath Write-Host "Processed" $relativePath
} }
# Post processing # Validation
Check-Carriage-Return("emoji-ordering.txt") Check-Carriage-Return(Join-Path $targetDir "plugins\official\emoji-keyboard\emoji-ordering.txt")
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){ # Processing
$imports = New-Object "System.Collections.Generic.List[string]"
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
$lines = [IO.File]::ReadLines($file.FullName) $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines | % { $_.TrimStart() } $lines = $lines | ForEach-Object { $_.TrimStart() }
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' $lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;' $lines = $lines -Replace '(?<!\w)(return|throw)(\s.*?)? if (.*?);', 'if ($3)$1$2;'
Rewrite-File $file $lines Rewrite-File $file $lines $imports
} }
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){ foreach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
$lines = [IO.File]::ReadLines($file.FullName) $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines -Replace '\s*/\*.*?\*/', '' $lines = $lines -Replace '\s*/\*.*?\*/', ''
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;' $lines = $lines -Replace '^(\S.*) {$', '$1{'
$lines = $lines -Replace '^(\S.*?) {$', '$1{' $lines = $lines -Replace '^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$', '$1:$2$3;'
$lines = @(($lines | Where { $_ -ne '' }) -Join ' ') $lines = @((Remove-Empty-Lines($lines)) -Join ' ')
Rewrite-File $file $lines $lines = $lines -Replace '([{};])\s', '$1'
$lines = $lines -Replace ';}', '}'
Rewrite-File $file $lines $imports
} }
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){ foreach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){
$lines = [IO.File]::ReadLines($file.FullName) $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines | % { $_.TrimStart() } $lines = $lines | ForEach-Object { $_.TrimStart() }
Rewrite-File $file $lines Rewrite-File $file $lines $imports
} }
ForEach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){ foreach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){
$lines = [IO.File]::ReadLines($file.FullName) $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines -Replace '\{version\}', $version $lines = $lines -Replace '\{version\}', $version
Rewrite-File $file $lines Rewrite-File $file $lines $imports
} }
Write-Host "------------------" # Imports
$importFolder = Join-Path $targetDir "scripts\imports"
foreach($path in $imports){
$text = [IO.File]::ReadAllText($path)
$text = [Regex]::Replace($text, '#import "(.*?)"', {
$importPath = Join-Path $importFolder ($args[0].Groups[1].Value.Trim())
$importStr = [IO.File]::ReadAllText($importPath).TrimEnd()
if ($importStr[0] -eq '#'){
$importStr = $importStr.Substring($importStr.IndexOf("`n") + 1)
}
return $importStr
}, [System.Text.RegularExpressions.RegexOptions]::MultiLine)
[IO.File]::WriteAllText($path, $text)
Write-Host "Resolved" $path.Substring($targetDir.Length)
}
[IO.Directory]::Delete($importFolder, $True)
# Finished
$sw.Stop() $sw.Stop()
Write-Host "------------------"
Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms" Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms"
Write-Host ------------------ Write-Host "------------------"
}catch{ }catch{
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
Write-Host $_ Write-Host $_

View File

@@ -0,0 +1,60 @@
$ErrorActionPreference = "Stop"
try{
$mainProj = "..\TweetDuck.csproj"
$browserProj = "..\subprocess\TweetDuck.Browser.csproj"
$cefMatch = Select-String -Path $mainProj '<Import Project="packages\\cef\.redist\.x86\.(.*?)\\'
$cefVersion = $cefMatch.Matches[0].Groups[1].Value
$sharpMatch = Select-String -Path $mainProj '<Import Project="packages\\CefSharp\.Common\.(.*?)\\'
$sharpVersion = $sharpMatch.Matches[0].Groups[1].Value
$propsFiles = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.props",
"..\packages\CefSharp.WinForms.${sharpVersion}\build\CefSharp.WinForms.props"
# Greetings
$title = "CEF ${cefVersion}, CefSharp ${sharpVersion}"
Write-Host ("-" * $title.Length)
Write-Host $title
Write-Host ("-" * $title.Length)
# Perform update
Write-Host "Copying dev tools to repository..."
Copy-Item "..\packages\cef.redist.x86.${cefVersion}\CEF\devtools_resources.pak" -Destination "..\bld\Resources\" -Force
Write-Host "Updating browser subprocess reference..."
$contents = [IO.File]::ReadAllText($browserProj)
$contents = $contents -Replace '(?<=<HintPath>\.\.\\packages\\CefSharp\.Common\.)(.*?)(?=\\)', $sharpVersion
$contents = $contents -Replace '(?<=<Reference Include="CefSharp\.BrowserSubprocess\.Core, Version=)(\d+)', $sharpVersion.Split(".")[0]
[IO.File]::WriteAllText($browserProj, $contents)
Write-Host "Removing x64 and AnyCPU from CefSharp props..."
foreach($file in $propsFiles){
$contents = [IO.File]::ReadAllText($file)
$contents = $contents -Replace '(?<=<When Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
[IO.File]::WriteAllText($file, $contents)
}
# Finished
Write-Host ""
Write-Host "Finished. Exiting in 6 seconds..."
Start-Sleep -Seconds 6
}catch{
Write-Host ""
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
Write-Host $_
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Exit 1
}

View File

@@ -1,10 +1,12 @@
using CefSharp; using CefSharp;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
#if DEBUG #if DEBUG
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
@@ -14,8 +16,17 @@ using TweetDuck.Plugins;
namespace TweetDuck.Resources{ namespace TweetDuck.Resources{
static class ScriptLoader{ static class ScriptLoader{
public static string LoadResource(string name, bool silent = false, Control sync = null){ private static readonly Dictionary<string, string> CachedData = new Dictionary<string, string>(16);
try{
public static string LoadResourceSilent(string name){
return LoadResource(name, null);
}
public static string LoadResource(string name, Control sync){
if (CachedData.TryGetValue(name, out string resourceData)){
return resourceData;
}
string path = Program.ScriptPath; string path = Program.ScriptPath;
#if DEBUG #if DEBUG
@@ -25,6 +36,9 @@ namespace TweetDuck.Resources{
} }
#endif #endif
string resource;
try{
string contents = File.ReadAllText(Path.Combine(path, name), Encoding.UTF8); string contents = File.ReadAllText(Path.Combine(path, name), Encoding.UTF8);
int separator; int separator;
@@ -33,7 +47,7 @@ namespace TweetDuck.Resources{
// #<version>\n // #<version>\n
if (contents[0] != '#'){ if (contents[0] != '#'){
ShowLoadError(silent, sync, $"File {name} appears to be corrupted, please try reinstalling the app."); ShowLoadError(sync, $"File {name} appears to be corrupted, please try reinstalling the app.");
separator = 0; separator = 0;
} }
else{ else{
@@ -41,44 +55,33 @@ namespace TweetDuck.Resources{
string fileVersion = contents.Substring(1, separator-1).TrimEnd(); string fileVersion = contents.Substring(1, separator-1).TrimEnd();
if (fileVersion != Program.VersionTag){ if (fileVersion != Program.VersionTag){
ShowLoadError(silent, sync, $"File {name} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app."); ShowLoadError(sync, $"File {name} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app.");
} }
} }
return contents.Substring(separator).TrimStart(); resource = contents.Substring(separator).TrimStart();
}catch(Exception ex){ }catch(Exception ex){
ShowLoadError(silent, sync, $"Could not load {name}. The program will continue running with limited functionality.\n\n{ex.Message}"); ShowLoadError(sync, $"Could not load {name}. The program will continue running with limited functionality.\n\n{ex.Message}");
return null; resource = null;
}
} }
public static bool ExecuteFile(IFrame frame, string file, Control sync = null){ return CachedData[name] = resource;
string script = LoadResource(file, sync == null, sync); }
ExecuteScript(frame, script, GetRootIdentifier(file));
public static bool ExecuteFile(IFrame frame, string file, Control sync){
string script = LoadResource(file, sync);
ExecuteScript(frame, script, "root:"+Path.GetFileNameWithoutExtension(file));
return script != null; return script != null;
} }
public static void ExecuteScript(IFrame frame, string script, string identifier){ public static void ExecuteScript(IFrame frame, string script, string identifier){
if (script != null){ if (script != null){
frame.ExecuteJavaScriptAsync(script, "td:"+identifier, 1); frame.ExecuteJavaScriptAsync(script, identifier, 1);
} }
} }
public static string GetRootIdentifier(string file){ private static void ShowLoadError(Control sync, string message){
return "root:"+Path.GetFileNameWithoutExtension(file); sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
}
private static void ShowLoadError(bool silent, Control sync, string message){
if (silent){
return;
}
if (sync == null){
FormMessage.Error("Resource Error", message, FormMessage.OK);
}
else{
sync.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
}
} }
#if DEBUG #if DEBUG
@@ -88,7 +91,7 @@ namespace TweetDuck.Resources{
static ScriptLoader(){ static ScriptLoader(){
if (File.Exists(HotSwapRebuildScript)){ if (File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Activating resource hot swap"); Debug.WriteLine("Activating resource hot swap...");
ResetHotSwap(); ResetHotSwap();
Application.ApplicationExit += (sender, args) => ResetHotSwap(); Application.ApplicationExit += (sender, args) => ResetHotSwap();
@@ -104,6 +107,8 @@ namespace TweetDuck.Resources{
ResetHotSwap(); ResetHotSwap();
Directory.CreateDirectory(HotSwapTargetDir); Directory.CreateDirectory(HotSwapTargetDir);
Stopwatch sw = Stopwatch.StartNew();
using(Process process = Process.Start(new ProcessStartInfo{ using(Process process = Process.Start(new ProcessStartInfo{
FileName = "powershell", FileName = "powershell",
Arguments = $"-ExecutionPolicy Unrestricted -File \"{HotSwapRebuildScript}\" \"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"", Arguments = $"-ExecutionPolicy Unrestricted -File \"{HotSwapRebuildScript}\" \"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
@@ -120,20 +125,21 @@ namespace TweetDuck.Resources{
} }
} }
sw.Stop();
Debug.WriteLine("Finished rebuild script in "+sw.ElapsedMilliseconds+" ms");
CachedData.Clear();
// Force update plugin manager setup scripts // Force update plugin manager setup scripts
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins"); string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic; const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
const BindingFlags flagsStatic = BindingFlags.Static | BindingFlags.NonPublic;
Type typePluginManager = typeof(PluginManager); Type typePluginManager = typeof(PluginManager);
Type typeFormBrowser = typeof(FormBrowser); Type typeFormBrowser = typeof(FormBrowser);
// ReSharper disable PossibleNullReferenceException // ReSharper disable PossibleNullReferenceException
object pluginSetupScripts = typePluginManager.GetMethod("LoadSetupScripts", flagsStatic).Invoke(null, new object[0]);
typePluginManager.GetField("PluginSetupScripts", flagsStatic).SetValue(null, pluginSetupScripts);
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>()); object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
typePluginManager.GetField("rootPath", flagsInstance).SetValue(instPluginManager, newPluginRoot); typePluginManager.GetField("rootPath", flagsInstance).SetValue(instPluginManager, newPluginRoot);

View File

@@ -1,18 +1,18 @@
(function($, $TD, $TDX, TD){ (function($TD, $TDX, $, TD){
// //
// Variable: Current highlighted column jQuery & data objects. // Variable: Current highlighted column jQuery & data objects.
// //
var highlightedColumnEle, highlightedColumnObj; let highlightedColumnEle, highlightedColumnObj;
// //
// Variable: Currently highlighted tweet jQuery & data objects. // Variable: Currently highlighted tweet jQuery & data objects.
// //
var highlightedTweetEle, highlightedTweetObj; let highlightedTweetEle, highlightedTweetObj;
// //
// Variable: Array of functions called after the website app is loaded. // Variable: Array of functions called after the website app is loaded.
// //
var onAppReady = []; let onAppReady = [];
// //
// Variable: DOM HTML element. // Variable: DOM HTML element.
@@ -22,7 +22,7 @@
// //
// Variable: DOM object containing the main app element. // Variable: DOM object containing the main app element.
// //
const app = $(document.body).children(".js-app"); const app = typeof $ === "function" && $(document.body).children(".js-app");
// //
// Constant: Column icon classes mapped to their titles. // Constant: Column icon classes mapped to their titles.
@@ -65,11 +65,12 @@
}; };
// //
// Function: Returns true if an object has a specified property, otherwise returns false without throwing an error. // Function: Returns true if an object has a specified property, otherwise returns false with a debug-only error message.
// //
const ensurePropertyExists = function(obj, ...chain){ const ensurePropertyExists = function(obj, ...chain){
for(let index = 0; index < chain.length; index++){ for(let index = 0; index < chain.length; index++){
if (!obj.hasOwnProperty(chain[index])){ if (!obj.hasOwnProperty(chain[index])){
debugger;
$TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join(".")); $TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join("."));
return false; return false;
} }
@@ -80,6 +81,36 @@
return true; return true;
}; };
//
// Function: Returns a jQuery object but also shows a debug-only error message if no elements are found.
//
const $$ = function(selector, context){
let result = $(selector, context);
if (!result.length){
debugger;
$TD.crashDebug("No elements were found for selector "+selector);
}
return result;
};
//
// Function: Executes a function inside a try-catch to stop it from crashing everything.
//
const execSafe = function(func, fail){
try{
func();
}catch(err){
console.error(err);
debugger;
$TD.crashDebug("Caught error in function "+func.name)
fail && fail();
}
};
// //
// Function: Retrieves a property of an element with a specified class. // Function: Retrieves a property of an element with a specified class.
// //
@@ -98,6 +129,7 @@
// //
// Block: Fix columns missing any identifiable attributes to allow individual styles. // Block: Fix columns missing any identifiable attributes to allow individual styles.
// //
execSafe(function setupColumnAttrIdentifiers(){
$(document).on("uiColumnRendered", function(e, data){ $(document).on("uiColumnRendered", function(e, data){
let icon = data.$column.find(".column-type-icon").first(); let icon = data.$column.find(".column-type-icon").first();
return if icon.length !== 1; return if icon.length !== 1;
@@ -108,6 +140,7 @@
data.$column.attr("data-td-icon", name); data.$column.attr("data-td-icon", name);
data.column._tduck_icon = name; data.column._tduck_icon = name;
}); });
});
// //
// Block: Setup global function to retrieve the column name. // Block: Setup global function to retrieve the column name.
@@ -124,17 +157,17 @@
let recentTweets = new Set(); let recentTweets = new Set();
let recentTweetTimer = null; let recentTweetTimer = null;
let resetRecentTweets = () => { const resetRecentTweets = () => {
recentTweetTimer = null; recentTweetTimer = null;
recentTweets.clear(); recentTweets.clear();
}; };
let startRecentTweetTimer = () => { const startRecentTweetTimer = () => {
recentTweetTimer && window.clearTimeout(recentTweetTimer); recentTweetTimer && window.clearTimeout(recentTweetTimer);
recentTweetTimer = window.setTimeout(resetRecentTweets, 20000); recentTweetTimer = window.setTimeout(resetRecentTweets, 20000);
}; };
let checkTweetCache = (set, id) => { const checkTweetCache = (set, id) => {
return true if set.has(id); return true if set.has(id);
if (set.size > 50){ if (set.size > 50){
@@ -145,8 +178,21 @@
return false; return false;
}; };
let fixMedia = (html, media) => { const isSensitive = (tweet) => {
return html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.small()+'")').removeClass("is-zoomable"); let main = tweet.getMainTweet && tweet.getMainTweet();
return true if main && main.possiblySensitive; // TODO these don't show media badges when hiding sensitive media
let related = tweet.getRelatedTweet && tweet.getRelatedTweet();
return true if related && related.possiblySensitive;
let quoted = tweet.quotedTweet;
return true if quoted && quoted.possiblySensitive;
return false;
};
const fixMedia = (html, media) => {
return html.find("a[data-media-entity-id='"+media.mediaId+"'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("'+media.small()+'")');
}; };
return function(column, tweet){ return function(column, tweet){
@@ -160,7 +206,7 @@
startRecentTweetTimer(); startRecentTweetTimer();
if (column.model.getHasNotification()){ if (column.model.getHasNotification()){
let sensitive = (tweet.getRelatedTweet() && tweet.getRelatedTweet().possiblySensitive || (tweet.quotedTweet && tweet.quotedTweet.possiblySensitive)); let sensitive = isSensitive(tweet);
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia()); let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
let html = $(tweet.render({ let html = $(tweet.render({
@@ -172,7 +218,8 @@
isMediaPreviewLarge: false, isMediaPreviewLarge: false,
isMediaPreviewCompact: false, isMediaPreviewCompact: false,
isMediaPreviewInQuoted: previews, isMediaPreviewInQuoted: previews,
thumbSizeClass: "media-size-medium" thumbSizeClass: "media-size-medium",
mediaPreviewSize: "medium"
})); }));
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
@@ -181,13 +228,15 @@
if (previews){ if (previews){
html.find(".reverse-image-search").remove(); html.find(".reverse-image-search").remove();
let container = html.find(".js-media");
for(let media of tweet.getMedia()){ for(let media of tweet.getMedia()){
fixMedia(html, media); fixMedia(container, media);
} }
if (tweet.quotedTweet){ if (tweet.quotedTweet){
for(let media of tweet.quotedTweet.getMedia()){ for(let media of tweet.quotedTweet.getMedia()){
fixMedia(html, media).addClass("media-size-medium"); fixMedia(container, media).addClass("media-size-medium");
} }
} }
} }
@@ -195,6 +244,10 @@
html.find(".js-media").remove(); html.find(".js-media").remove();
} }
html.find("a[data-full-url]").each(function(){ // bypass t.co on all links
this.href = this.getAttribute("data-full-url");
});
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to) html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
this.outerHTML = this.innerHTML; this.outerHTML = this.innerHTML;
}); });
@@ -211,6 +264,12 @@
} }
}); });
if (tweet.quotedTweet){
html.find("p.txt-mute").filter(function(){
return $(this).text() === "Show this thread";
}).first().remove();
}
let type = tweet.getChirpType(); let type = tweet.getChirpType();
if (type === "follow"){ if (type === "follow"){
@@ -250,7 +309,12 @@
// //
// Function: Shows tweet detail, used in notification context menu. // Function: Shows tweet detail, used in notification context menu.
// //
(function(){ execSafe(function setupShowTweetDetail(){
throw 1 if !ensurePropertyExists(TD, "ui", "updates", "showDetailView");
throw 2 if !ensurePropertyExists(TD, "controller", "columnManager", "showColumn");
throw 3 if !ensurePropertyExists(TD, "controller", "columnManager", "getByApiid");
throw 4 if !ensurePropertyExists(TD, "controller", "clients", "getPreferredClient");
const showTweetDetailInternal = function(column, chirp){ const showTweetDetailInternal = function(column, chirp){
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp); TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
TD.controller.columnManager.showColumn(column.model.privateState.key); TD.controller.columnManager.showColumn(column.model.privateState.key);
@@ -292,12 +356,21 @@
}); });
} }
}; };
})(); }, function(){
window.TDGF_showTweetDetail = function(){
alert("error|This feature is not available due to an internal error.");
};
});
// //
// Block: Hook into settings object to detect when the settings change, and update html attributes and notification layout. // Block: Hook into settings object to detect when the settings change, and update html attributes and notification layout.
// //
(function(){ execSafe(function hookTweetDeckSettings(){
throw 1 if !ensurePropertyExists(TD, "settings", "getFontSize");
throw 2 if !ensurePropertyExists(TD, "settings", "setFontSize");
throw 3 if !ensurePropertyExists(TD, "settings", "getTheme");
throw 4 if !ensurePropertyExists(TD, "settings", "setTheme");
const refreshSettings = function(){ const refreshSettings = function(){
let fontSizeName = TD.settings.getFontSize(); let fontSizeName = TD.settings.getFontSize();
let themeName = TD.settings.getTheme(); let themeName = TD.settings.getTheme();
@@ -328,7 +401,7 @@
}); });
onAppReady.push(refreshSettings); onAppReady.push(refreshSettings);
})(); });
// //
// Block: Fix OS name and add ID to the document for priority CSS selectors. // Block: Fix OS name and add ID to the document for priority CSS selectors.
@@ -347,7 +420,9 @@
// //
// Block: Enable popup notifications. // Block: Enable popup notifications.
// //
if (ensurePropertyExists(TD, "controller", "notifications")){ execSafe(function hookDesktopNotifications(){
throw 1 if !ensurePropertyExists(TD, "controller", "notifications");
TD.controller.notifications.hasNotifications = function(){ TD.controller.notifications.hasNotifications = function(){
return true; return true;
}; };
@@ -355,19 +430,19 @@
TD.controller.notifications.isPermissionGranted = function(){ TD.controller.notifications.isPermissionGranted = function(){
return true; return true;
}; };
}
$.subscribe("/notifications/new", function(obj){ $.subscribe("/notifications/new", function(obj){
for(let index = obj.items.length-1; index >= 0; index--){ for(let index = obj.items.length-1; index >= 0; index--){
onNewTweet(obj.column, obj.items[index]); onNewTweet(obj.column, obj.items[index]);
} }
}); });
});
// //
// Block: Add TweetDuck buttons to the settings menu. // Block: Add TweetDuck buttons to the settings menu.
// //
onAppReady.push(function(){ onAppReady.push(function setupSettingsDropdown(){
$("[data-action='settings-menu']").click(function(){ $$("[data-action='settings-menu']").click(function(){
setTimeout(function(){ setTimeout(function(){
let menu = $(".js-dropdown-content").children("ul").first(); let menu = $(".js-dropdown-content").children("ul").first();
return if menu.length === 0; return if menu.length === 0;
@@ -391,9 +466,9 @@
// //
// Block: Expand shortened links on hover or display tooltip. // Block: Expand shortened links on hover or display tooltip.
// //
(function(){ execSafe(function setupLinkExpansionOrTooltip(){
var prevMouseX = -1, prevMouseY = -1; let prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed; let tooltipTimer, tooltipDisplayed;
$(document.body).delegate("a[data-full-url]", { $(document.body).delegate("a[data-full-url]", {
mouseenter: function(){ mouseenter: function(){
@@ -438,11 +513,12 @@
} }
} }
}); });
})(); });
// //
// Block: Bypass t.co when clicking/dragging links and media. // Block: Bypass t.co when clicking/dragging links and media.
// //
execSafe(function setupShortenerBypass(){
$(document.body).delegate("a[data-full-url]", "click auxclick", function(e){ $(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick
$TD.openBrowser($(this).attr("data-full-url")); $TD.openBrowser($(this).attr("data-full-url"));
@@ -478,11 +554,12 @@
return obj; return obj;
}; };
} }
});
// //
// Block: Bypass t.co in user profiles and setup a top tier account bamboozle scheme. // Block: Bypass t.co in user profiles and setup a top tier account bamboozle scheme.
// //
(function(){ execSafe(function setupAccountLoadHook(){
const realDisplayName = "TweetDuck"; const realDisplayName = "TweetDuck";
const realAvatar = "https://ton.twimg.com/tduck/avatar"; const realAvatar = "https://ton.twimg.com/tduck/avatar";
const accountId = "957608948189880320"; const accountId = "957608948189880320";
@@ -500,6 +577,7 @@
obj.profileImageURL = realAvatar; obj.profileImageURL = realAvatar;
obj.url = "https://tweetduck.chylex.com"; obj.url = "https://tweetduck.chylex.com";
if (obj.entities && obj.entities.url){
obj.entities.url.urls = [{ obj.entities.url.urls = [{
url: obj.url, url: obj.url,
expanded_url: obj.url, expanded_url: obj.url,
@@ -507,6 +585,7 @@
indices: [ 0, 23 ] indices: [ 0, 23 ]
}]; }];
} }
}
else if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){ else if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
obj.url = e.url.urls[0].expanded_url; obj.url = e.url.urls[0].expanded_url;
} }
@@ -537,11 +616,12 @@
}, onError); }, onError);
}; };
} }
})(); });
// //
// Block: Include additional information in context menus. // Block: Include additional information in context menus.
// //
execSafe(function setupContextMenuInfo(){
$(document.body).delegate("a", "contextmenu", function(){ $(document.body).delegate("a", "contextmenu", function(){
let me = $(this)[0]; let me = $(this)[0];
@@ -550,19 +630,20 @@
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id")); let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){ if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
$TD.setLastRightClickInfo("video", media.chooseVideoVariant().url); $TD.setRightClickedLink("video", media.chooseVideoVariant().url);
} }
else{ else{
$TD.setLastRightClickInfo("image", media.large()); $TD.setRightClickedLink("image", media.large());
} }
} }
else if (me.classList.contains("js-gif-play")){ else if (me.classList.contains("js-gif-play")){
$TD.setLastRightClickInfo("video", $(this).closest(".js-media-gif-container").find("video").attr("src")); $TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
} }
else{ else{
$TD.setLastRightClickInfo("link", me.getAttribute("data-full-url")); $TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
} }
}); });
});
// //
// Block: Hook into the notification sound effect. // Block: Hook into the notification sound effect.
@@ -598,7 +679,10 @@
// //
// Block: Update highlighted column and tweet for context menu and other functionality. // Block: Update highlighted column and tweet for context menu and other functionality.
// //
(function(){ execSafe(function setupHighlightedColumn(){
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "get");
throw 2 if !ensurePropertyExists(TD, "services", "ChirpBase", "TWEET");
const updateHighlightedColumn = function(ele){ const updateHighlightedColumn = function(ele){
highlightedColumnEle = ele; highlightedColumnEle = ele;
highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null; highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
@@ -654,12 +738,12 @@
updateHighlightedTweet(null, null); updateHighlightedTweet(null, null);
} }
}); });
})(); });
// //
// Block: Screenshot tweet to clipboard. // Block: Screenshot tweet to clipboard.
// //
(function(){ execSafe(function setupTweetScreenshot(){
window.TDGF_triggerScreenshot = function(){ window.TDGF_triggerScreenshot = function(){
return if !highlightedTweetObj || !highlightedColumnObj; return if !highlightedTweetObj || !highlightedColumnObj;
@@ -680,9 +764,9 @@
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
html.find(".td-screenshot-remove").remove(); html.find(".td-screenshot-remove").remove();
html.find("p.link-complex-target").filter(function(){ html.find("p.link-complex-target,p.txt-mute").filter(function(){
return $(this).text() === "Show this thread"; return $(this).text() === "Show this thread";
}).first().remove(); }).remove();
html.addClass($(document.documentElement).attr("class")); html.addClass($(document.documentElement).attr("class"));
html.addClass($(document.body).attr("class")); html.addClass($(document.body).attr("class"));
@@ -699,6 +783,12 @@
} }
} }
let gif = html.find(".js-media-gif-container");
if (gif.length){
gif.css("background-image", 'url("'+chirp.getMedia()[0].small()+'")');
}
let type = chirp.getChirpType(); let type = chirp.getChirpType();
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){ if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
@@ -707,12 +797,16 @@
$TD.screenshotTweet(html[0].outerHTML, columnWidth); $TD.screenshotTweet(html[0].outerHTML, columnWidth);
}; };
})(); }, function(){
window.TDGF_triggerScreenshot = function(){
alert("error|This feature is not available due to an internal error.");
};
});
// //
// Block: Paste images when tweeting. // Block: Paste images when tweeting.
// //
onAppReady.push(function(){ onAppReady.push(function supportImagePaste(){
const uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context; const uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){ app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
@@ -750,7 +844,7 @@
// //
// Block: Support for extra mouse buttons. // Block: Support for extra mouse buttons.
// //
(function(){ execSafe(function supportExtraMouseButtons(){
const tryClickSelector = function(selector, parent){ const tryClickSelector = function(selector, parent){
return $(selector, parent).click().length; return $(selector, parent).click().length;
}; };
@@ -790,12 +884,12 @@
} }
} }
}; };
})(); });
// //
// Block: Allow drag & drop behavior for dropping links on columns to open their detail view. // Block: Allow drag & drop behavior for dropping links on columns to open their detail view.
// //
(function(){ execSafe(function supportDragDropOverColumns(){
const tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/; const tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/;
const selector = "section.js-column"; const selector = "section.js-column";
@@ -838,11 +932,14 @@
app.undelegate(selector, events); app.undelegate(selector, events);
} }
}; };
})(); });
// //
// Block: Fix scheduled tweets not showing up sometimes. // Block: Fix scheduled tweets not showing up sometimes.
// //
execSafe(function fixScheduledTweets(){
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "getAll");
$(document).on("dataTweetSent", function(e, data){ $(document).on("dataTweetSent", function(e, data){
if (data.response.state && data.response.state === "scheduled"){ if (data.response.state && data.response.state === "scheduled"){
let column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled"); let column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");
@@ -854,12 +951,15 @@
} }
} }
}); });
});
// //
// Block: Hold Shift to restore cleared column. // Block: Hold Shift to restore cleared column.
// //
(function(){ execSafe(function supportShiftToClearColumn(){
var holdingShift = false; throw 1 if !ensurePropertyExists(TD, "vo", "Column", "prototype", "clear");
let holdingShift = false;
const updateShiftState = (pressed) => { const updateShiftState = (pressed) => {
if (pressed != holdingShift){ if (pressed != holdingShift){
@@ -882,8 +982,6 @@
} }
}); });
return if !ensurePropertyExists(TD, "vo", "Column", "prototype", "clear");
TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){ TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input
@@ -893,17 +991,19 @@
return true; return true;
} }
}); });
})(); });
// //
// Block: Refocus the textbox after switching accounts. // Block: Refocus the textbox after switching accounts.
// //
onAppReady.push(function(){ onAppReady.push(function setupAccountSwitchRefocus(){
const composeInput = $$(".js-compose-text", ".js-docked-compose");
const refocusInput = function(){ const refocusInput = function(){
$(".js-compose-text", ".js-docked-compose").focus(); composeInput.focus();
}; };
$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){ $$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){
setTimeout(refocusInput, 0); setTimeout(refocusInput, 0);
}); });
}); });
@@ -911,6 +1011,7 @@
// //
// Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal. // Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
// //
execSafe(function supportMiddleClickTweetActions(){
app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){ app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){
return if e.which !== 2; return if e.which !== 2;
@@ -967,18 +1068,19 @@
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
}); });
});
// //
// Block: Work around clipboard HTML formatting. // Block: Work around clipboard HTML formatting.
// //
$(document).on("copy", function(e){ document.addEventListener("copy", function(){
window.setTimeout($TD.fixClipboard, 0); window.setTimeout($TD.fixClipboard, 0);
}); });
// //
// Block: Inject custom CSS and layout into the page. // Block: Inject custom CSS and layout into the page.
// //
(function(){ execSafe(function setupStyleInjection(){
const createStyle = function(id, styles){ const createStyle = function(id, styles){
let ele = document.createElement("style"); let ele = document.createElement("style");
ele.id = id; ele.id = id;
@@ -999,12 +1101,42 @@
createStyle("tweetduck-custom-css", styles); createStyle("tweetduck-custom-css", styles);
} }
}; };
})(); });
//
// Block: Setup global function to inject custom HTML into mustache templates.
//
window.TDGF_injectMustache = function(name, operation, search, custom){
let replacement;
switch(operation){
case "replace": replacement = custom; break;
case "append": replacement = search+custom; break;
case "prepend": replacement = custom+search; break;
default: throw "Invalid mustache injection operation. Only 'replace', 'append', 'prepend' are supported.";
}
let prev = TD.mustaches && TD.mustaches[name];
if (!prev){
$TD.crashDebug("Mustache injection is referencing an invalid mustache: "+name);
return false;
}
TD.mustaches[name] = prev.replace(search, replacement);
if (prev === TD.mustaches[name]){
$TD.crashDebug("Mustache injection had no effect: "+name);
return false;
}
return true;
};
// //
// Block: Let's make retweets lowercase again. // Block: Let's make retweets lowercase again.
// //
TD.mustaches["status/tweet_single.mustache"] = TD.mustaches["status/tweet_single.mustache"].replace("{{_i}} Retweeted{{/i}}", "{{_i}} retweeted{{/i}}"); window.TDGF_injectMustache("status/tweet_single.mustache", "replace", "{{_i}} Retweeted{{/i}}", "{{_i}} retweeted{{/i}}");
if (ensurePropertyExists(TD, "services", "TwitterActionRetweet", "prototype", "generateText")){ if (ensurePropertyExists(TD, "services", "TwitterActionRetweet", "prototype", "generateText")){
TD.services.TwitterActionRetweet.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweet.prototype.generateText, function(){ TD.services.TwitterActionRetweet.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweet.prototype.generateText, function(){
@@ -1022,7 +1154,7 @@
// //
// Block: Setup video player hooks. // Block: Setup video player hooks.
// //
(function(){ execSafe(function setupVideoPlayer(){
window.TDGF_playVideo = function(url, username){ window.TDGF_playVideo = function(url, username){
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){ $('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
$TD.playVideo(null, null); $TD.playVideo(null, null);
@@ -1039,11 +1171,11 @@
let parent = obj.closest(".js-tweet").first(); let parent = obj.closest(".js-tweet").first();
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first(); let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
return link.attr("href"); return link.attr("href");
} };
const getUsername = function(tweet){ const getUsername = function(tweet){
return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName; return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName;
} };
app.delegate(".js-gif-play", { app.delegate(".js-gif-play", {
click: function(e){ click: function(e){
@@ -1073,14 +1205,15 @@
} }
}); });
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused"); window.TDGF_injectMustache("status/media_thumb.mustache", "append", "is-gif", " is-paused");
TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>'; TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>';
if (!ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet") || throw 1 if !ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet");
!ensurePropertyExists(TD, "components", "BaseModal", "prototype", "setAndShowContainer") || throw 2 if !ensurePropertyExists(TD, "components", "BaseModal", "prototype", "setAndShowContainer");
!ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused"))return; throw 3 if !ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused");
var cancelModal = false; let cancelModal = false;
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){ TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId); let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
@@ -1099,7 +1232,11 @@
}); });
TD.ui.Column.prototype.playGifIfNotManuallyPaused = function(){}; TD.ui.Column.prototype.playGifIfNotManuallyPaused = function(){};
})(); }, function(){
window.TDGF_playVideo = function(){
alert("error|This feature is not available due to an internal error.");
};
});
// //
// Block: Fix youtu.be previews not showing up for https links. // Block: Fix youtu.be previews not showing up for https links.
@@ -1107,10 +1244,10 @@
if (ensurePropertyExists(TD, "services", "TwitterMedia")){ if (ensurePropertyExists(TD, "services", "TwitterMedia")){
let media = TD.services.TwitterMedia; let media = TD.services.TwitterMedia;
if (!ensurePropertyExists(media, "YOUTUBE_TINY_RE") || return if !ensurePropertyExists(media, "YOUTUBE_TINY_RE");
!ensurePropertyExists(media, "YOUTUBE_LONG_RE") || return if !ensurePropertyExists(media, "YOUTUBE_LONG_RE");
!ensurePropertyExists(media, "YOUTUBE_RE") || return if !ensurePropertyExists(media, "YOUTUBE_RE");
!ensurePropertyExists(media, "SERVICES", "youtube"))return; return if !ensurePropertyExists(media, "SERVICES", "youtube");
media.YOUTUBE_TINY_RE = new RegExp(media.YOUTUBE_TINY_RE.source.replace("http:", "https?:")); media.YOUTUBE_TINY_RE = new RegExp(media.YOUTUBE_TINY_RE.source.replace("http:", "https?:"));
media.YOUTUBE_RE = new RegExp(media.YOUTUBE_LONG_RE.source+"|"+media.YOUTUBE_TINY_RE.source); media.YOUTUBE_RE = new RegExp(media.YOUTUBE_LONG_RE.source+"|"+media.YOUTUBE_TINY_RE.source);
@@ -1120,14 +1257,14 @@
// //
// Block: Add a pin icon to make tweet compose drawer stay open. // Block: Add a pin icon to make tweet compose drawer stay open.
// //
onAppReady.push(function(){ onAppReady.push(function setupStayOpenPin(){
let ele = $(`<svg id="td-compose-drawer-pin" viewBox="0 0 24 24" class="icon js-show-tip" data-original-title="Stay open" data-tooltip-position="left"> let ele = $(`
<svg id="td-compose-drawer-pin" viewBox="0 0 24 24" class="icon js-show-tip" data-original-title="Stay open" data-tooltip-position="left">
<path d="M9.884,16.959l3.272,0.001l-0.82,4.568l-1.635,0l-0.817,-4.569Z"/> <path d="M9.884,16.959l3.272,0.001l-0.82,4.568l-1.635,0l-0.817,-4.569Z"/>
<rect x="8.694" y="7.208" width="5.652" height="7.445"/> <rect x="8.694" y="7.208" width="5.652" height="7.445"/>
<path d="M16.877,17.448c0,-1.908 -1.549,-3.456 -3.456,-3.456l-3.802,0c-1.907,0 -3.456,1.548 -3.456,3.456l10.714,0Z"/> <path d="M16.877,17.448c0,-1.908 -1.549,-3.456 -3.456,-3.456l-3.802,0c-1.907,0 -3.456,1.548 -3.456,3.456l10.714,0Z"/>
<path d="M6.572,5.676l2.182,2.183l5.532,0l2.182,-2.183l0,-1.455l-9.896,0l0,1.455Z"/> <path d="M6.572,5.676l2.182,2.183l5.532,0l2.182,-2.183l0,-1.455l-9.896,0l0,1.455Z"/>
</svg>` </svg>`).appendTo(".js-docked-compose .js-compose-header");
).appendTo(".js-docked-compose .js-compose-header");
ele.click(function(){ ele.click(function(){
if (TD.settings.getComposeStayOpen()){ if (TD.settings.getComposeStayOpen()){
@@ -1148,6 +1285,10 @@
// //
// Block: Make temporary search column appear as the first one and clear the input box. // Block: Make temporary search column appear as the first one and clear the input box.
// //
execSafe(function setupSearchColumnHook(){
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "_columnOrder");
throw 2 if !ensurePropertyExists(TD, "controller", "columnManager", "move");
$(document).on("uiSearchNoTemporaryColumn", function(e, data){ $(document).on("uiSearchNoTemporaryColumn", function(e, data){
if (data.query && data.searchScope !== "users" && !data.columnKey){ if (data.query && data.searchScope !== "users" && !data.columnKey){
if ($TDX.openSearchInFirstColumn){ if ($TDX.openSearchInFirstColumn){
@@ -1168,33 +1309,38 @@
} }
} }
}); });
});
// //
// Block: Setup global function to add a search column with the specified query. // Block: Setup global function to add a search column with the specified query.
// //
onAppReady.push(function(){ onAppReady.push(() => execSafe(function setupSearchFunction(){
let context = $._data(document, "events")["uiSearchInputSubmit"][0].handler.context; let context = $._data(document, "events")["uiSearchInputSubmit"][0].handler.context;
window.TDGF_performSearch = function(query){ window.TDGF_performSearch = function(query){
context.performSearch({ query, tweetduck: true }); context.performSearch({ query, tweetduck: true });
}; };
}); }, function(){
window.TDGF_performSearch = function(){
alert("error|This feature is not available due to an internal error.");
};
}));
// //
// Block: Reorder search results to move accounts above hashtags. // Block: Reorder search results to move accounts above hashtags.
// //
onAppReady.push(function(){ onAppReady.push(function reorderSearchResults(){
let container = $(".js-search-in-popover"); let container = $(".js-search-in-popover");
let hashtags = $(".js-typeahead-topic-list", container); let hashtags = $$(".js-typeahead-topic-list", container);
$(".js-typeahead-user-list", container).insertBefore(hashtags); $$(".js-typeahead-user-list", container).insertBefore(hashtags);
hashtags.addClass("list-divider"); hashtags.addClass("list-divider");
}); });
// //
// Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally. // Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally.
// //
onAppReady.push(function(){ onAppReady.push(function setupSearchTriggerHook(){
const openSearchExternally = function(event, input){ const openSearchExternally = function(event, input){
$TD.openBrowser("https://twitter.com/search/?q="+encodeURIComponent(input.val() || "")); $TD.openBrowser("https://twitter.com/search/?q="+encodeURIComponent(input.val() || ""));
event.preventDefault(); event.preventDefault();
@@ -1204,17 +1350,17 @@
app.click(); // unfocus everything app.click(); // unfocus everything
}; };
$(".js-app-search-input").keydown(function(e){ $$(".js-app-search-input").keydown(function(e){
(e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter (e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter
}); });
$(".js-perform-search").on("click auxclick", function(e){ $$(".js-perform-search").on("click auxclick", function(e){
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible")); (e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible"));
}).each(function(){ }).each(function(){
window.TDGF_prioritizeNewestEvent($(this)[0], "click"); window.TDGF_prioritizeNewestEvent($(this)[0], "click");
}); });
$("[data-action='show-search']").on("click auxclick", function(e){ $$("[data-action='show-search']").on("click auxclick", function(e){
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $()); (e.ctrlKey || e.button === 1) && openSearchExternally(e, $());
}); });
}); });
@@ -1233,9 +1379,14 @@
// //
// Block: Setup global function to refresh all columns. // Block: Setup global function to refresh all columns.
// //
if (ensurePropertyExists(TD, "controller", "columnManager", "getAll")){
window.TDGF_reloadColumns = function(){ window.TDGF_reloadColumns = function(){
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets()); Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
}; };
}
else{
window.TDGF_reloadColumns = function(){};
}
// //
// Block: Allow applying ROT13 to input selection. // Block: Allow applying ROT13 to input selection.
@@ -1257,7 +1408,7 @@
// //
// Block: Revert Like/Follow dialogs being closed after clicking an action. // Block: Revert Like/Follow dialogs being closed after clicking an action.
// //
(function(){ execSafe(function setupLikeFollowDialogRevert(){
const prevSetTimeout = window.setTimeout; const prevSetTimeout = window.setTimeout;
const overrideState = function(){ const overrideState = function(){
@@ -1302,7 +1453,7 @@
}; };
}); });
}); });
})(); });
// //
// Block: Fix DM reply input box not getting focused after opening a conversation. // Block: Fix DM reply input box not getting focused after opening a conversation.
@@ -1329,11 +1480,37 @@
} }
// //
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift. // Block: Limit amount of loaded DMs to avoid massive lag from re-opening them several times.
//
if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "renderThread")){
const prevFunc = TD.services.TwitterConversation.prototype.renderThread;
TD.services.TwitterConversation.prototype.renderThread = function(){
let prevMessages = this.messages;
this.messages = prevMessages.slice(0, 100);
let result = prevFunc.apply(this, arguments);
this.messages = prevMessages;
return result;
};
}
//
// Block: Fix broken horizontal scrolling of column container when holding Shift. TODO Fix broken smooth scrolling.
// //
if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){ if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){
TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(e){ TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(column){
$(".js-column[data-column='"+e.model.getKey()+"']").off("mousewheel onmousewheel"); let ele = $(".js-column[data-column='"+column.model.getKey()+"']");
return if !ele.length;
ele.off("onmousewheel").on("mousewheel", ".scroll-v", function(e){
if (e.shiftKey){
e.stopImmediatePropagation();
}
});
window.TDGF_prioritizeNewestEvent(ele[0], "mousewheel");
}); });
} }
@@ -1341,7 +1518,7 @@
// Block: Detect and notify about connection issues. // Block: Detect and notify about connection issues.
// //
(function(){ (function(){
let onConnectionError = function(){ const onConnectionError = function(){
return if $("#tweetduck-conn-issues").length; return if $("#tweetduck-conn-issues").length;
let ele = $(` let ele = $(`
@@ -1355,15 +1532,14 @@
</div> </div>
</li> </li>
</ul> </ul>
</div> </div>`).appendTo(document.body);
`).appendTo(document.body);
ele.find("button").click(function(){ ele.find("button").click(function(){
ele.fadeOut(200); ele.fadeOut(200);
}); });
}; };
let onConnectionFine = function(){ const onConnectionFine = function(){
let ele = $("#tweetduck-conn-issues"); let ele = $("#tweetduck-conn-issues");
ele.fadeOut(200, function(){ ele.fadeOut(200, function(){
@@ -1379,6 +1555,7 @@
// Block: Custom reload function with memory cleanup. // Block: Custom reload function with memory cleanup.
// //
window.TDGF_reload = function(){ window.TDGF_reload = function(){
try{
let session = TD.storage.feedController.getAll() let session = TD.storage.feedController.getAll()
.filter(feed => !!feed.getTopSortIndex()) .filter(feed => !!feed.getTopSortIndex())
.reduce((obj, feed) => (obj[feed.privateState.key] = feed.getTopSortIndex(), obj), {}); .reduce((obj, feed) => (obj[feed.privateState.key] = feed.getTopSortIndex(), obj), {});
@@ -1387,12 +1564,16 @@
window.gc && window.gc(); window.gc && window.gc();
window.location.reload(); window.location.reload();
}); });
}catch(err){
$TD.crashDebug("Error saving session during a reload");
window.location.reload();
}
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
}; };
if (window.TD_SESSION && window.TD_SESSION.gc){ if (window.TD_SESSION && window.TD_SESSION.gc){
var state; let state;
try{ try{
state = JSON.parse(window.TD_SESSION.gc); state = JSON.parse(window.TD_SESSION.gc);
@@ -1401,7 +1582,7 @@
state = {}; state = {};
} }
var showMissedNotifications = function(){ const showMissedNotifications = function(){
let tweets = []; let tweets = [];
let columns = {}; let columns = {};
@@ -1429,6 +1610,9 @@
} }
}; };
execSafe(function showMissedNotifications(){
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "getAll");
$(document).one("dataColumnsLoaded", function(){ $(document).one("dataColumnsLoaded", function(){
let columns = Object.values(TD.controller.columnManager.getAll()); let columns = Object.values(TD.controller.columnManager.getAll());
let remaining = columns.length; let remaining = columns.length;
@@ -1441,14 +1625,16 @@
}); });
} }
}); });
});
} }
// //
// Block: Disable default TweetDeck update notification. // Block: Disable default TweetDeck update notification.
// //
onAppReady.push(function(){ execSafe(function disableTweetDeckUpdates(){
let events = $._data(document, "events"); $(document).on("uiSuggestRefreshToggle", function(e){
delete events["uiSuggestRefreshToggle"]; e.stopImmediatePropagation();
});
}); });
// //
@@ -1463,7 +1649,7 @@
TD.metrics.send = noop; TD.metrics.send = noop;
} }
onAppReady.push(function(){ onAppReady.push(function disableMetrics(){
let data = $._data(window); let data = $._data(window);
delete data.events["metric"]; delete data.events["metric"];
delete data.events["metricsFlush"]; delete data.events["metricsFlush"];
@@ -1473,7 +1659,7 @@
// Block: Register the TD.ready event, finish initialization, and load plugins. // Block: Register the TD.ready event, finish initialization, and load plugins.
// //
$(document).one("TD.ready", function(){ $(document).one("TD.ready", function(){
onAppReady.forEach(func => func()); onAppReady.forEach(func => execSafe(func));
onAppReady = null; onAppReady = null;
delete window.TD_SESSION; delete window.TD_SESSION;
@@ -1491,4 +1677,4 @@
location.href = "https://twitter.com/login?hide_message=true&redirect_after_login=https%3A%2F%2Ftweetdeck.twitter.com%2F%3Fvia_twitter_login%3Dtrue"; location.href = "https://twitter.com/login?hide_message=true&redirect_after_login=https%3A%2F%2Ftweetdeck.twitter.com%2F%3Fvia_twitter_login%3Dtrue";
}; };
} }
})($, $TD, $TDX, TD); })($TD, $TDX, window.$, window.TD || {});

View File

@@ -0,0 +1,26 @@
<div id="td-introduction-modal" class="ovl scroll-v scroll-styled-v">
<div class="mdl is-inverted-dark">
<header class="mdl-header">
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
<a href="#" class="mdl-dismiss link-normal-dark"><i class="icon icon-close"></i></a>
</header>
<div class="mdl-inner">
<div class="mdl-content">
<p>Thank you for downloading TweetDuck!</p>
<p><a id="td-introduction-follow" href="#">Follow @TryMyAwesomeApp</a> for latest news and updates about the app.</p>
<div class="main-menu"></div>
<p><strong>Right-click anywhere</strong> or click <strong>Settings&nbsp;&nbsp;TweetDuck</strong> in the left panel to open the main menu. You can also right-click links, tweets, images and videos, and desktop notifications to access their respective context menus.</p>
<p>Click <strong>Show Guide</strong> to see awesome features TweetDuck offers, or view the guide later by going to <strong>About TweetDuck</strong> and clicking the help button on top.</p>
</div>
<footer class="txt-right">
<div class="anondata">
<input id="td-anonymous-data" type="checkbox" checked>
<label for="td-anonymous-data">Send anonymous usage data</label>
<label>&nbsp;(<a href="https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data" rel="nofollow">learn more</a>)</label>
</div>
<button class="Button--primary" data-guide><span class="label">Show Guide</span></button>
<button class="Button--secondary"><span class="label">Close</span></button>
</footer>
</div>
</div>
</div>

View File

@@ -3,14 +3,14 @@
// Block: Setup a simple JavaScript object configuration loader. // Block: Setup a simple JavaScript object configuration loader.
// //
window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){ window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){
var identifier = pluginObject.$id; let identifier = pluginObject.$id;
var token = pluginObject.$token; let token = pluginObject.$token;
$TDP.checkFileExists(token, fileNameUser).then(exists => { $TDP.checkFileExists(token, fileNameUser).then(exists => {
var fileName = exists ? fileNameUser : fileNameDefault; let fileName = exists ? fileNameUser : fileNameDefault;
(exists ? $TDP.readFile(token, fileName, true) : $TDP.readFileRoot(token, fileName)).then(contents => { (exists ? $TDP.readFile(token, fileName, true) : $TDP.readFileRoot(token, fileName)).then(contents => {
var obj; let obj;
try{ try{
obj = eval("("+contents+")"); obj = eval("("+contents+")");
@@ -39,16 +39,14 @@
// Block: Setup a function to add/remove custom CSS. // Block: Setup a function to add/remove custom CSS.
// //
window.TDPF_createCustomStyle = function(pluginObject){ window.TDPF_createCustomStyle = function(pluginObject){
var element = document.createElement("style"); let element = document.createElement("style");
element.id = "plugin-"+pluginObject.$id+"-"+Math.random().toString(36).substring(2, 7); element.id = "plugin-"+pluginObject.$id+"-"+Math.random().toString(36).substring(2, 7);
document.head.appendChild(element); document.head.appendChild(element);
var obj = { return {
insert: (rule) => element.sheet.insertRule(rule, 0), insert: (rule) => element.sheet.insertRule(rule, 0),
remove: () => $(element).remove() remove: () => element.remove(),
element: element
}; };
obj.element = element;
return obj;
}; };
})($TDP); })($TDP);

View File

@@ -0,0 +1,69 @@
#td-introduction-modal {
display: flex;
}
#td-introduction-modal .mdl {
width: 90%;
min-width: 515px;
max-width: 835px;
height: 328px;
}
#td-introduction-modal .mdl-inner {
padding-top: 0;
}
#td-introduction-modal .mdl-header-title {
cursor: default;
}
#td-introduction-modal .mdl-content {
padding: 4px 16px 0;
overflow-y: auto;
}
#td-introduction-modal p {
margin: 12px 0;
font-size: 1.4rem;
}
#td-introduction-modal p strong {
font-weight: normal;
text-shadow: 0 0 #000;
}
#td-introduction-modal .main-menu {
float: left;
width: 187px;
height: 124px;
margin-right: 12px;
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.33);
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALsAAAB8CAMAAAAGozFUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACZUExURQAAAAAANgAAYQA2YQA2iABhYQBhrDYAADYANjYAYTY2ADY2NjY2YTY2iDZhrDaIiDaIz2EAAGEANmEAYWE2NmE2iGFhNmFhYWGs8og2AIg2Nog2YYiIYYisiIjPrIjPz4jP8qxhAKxhNqzPiKzyrKzy8szMzM+INs/PiM/yrM/yz8/y8tfX1/Dw8PKsYfLPiPLyrPLyz/Ly8nG9GIQAAARDSURBVHja7ZyPd9IwEMdDxbI5cemcWJjbWrWrDi2Q//+PM7m7pOGHfcORlD6v7w1ok2wfwqXkvrs78TTcQzypoR6DZ//dcTA7szM7s8dgX2dCiJxGrG+Kvd+C1w619M+uoZqkGiy7AxvivI+XajURSWVO9AtjQ6UQqTIn18R+PxFSP82SCnuUOYxdfdjMzSmNn9mPMJa9p/QWpH5cZ5rjgqbanNQjOMnGS315nWl+7NGkqrmSqpb6hRuvm+PO++qygtkWKZ2YOW2EGBWr6dK3mTKHmcYeuu3r/fR5UawmUrXjY9tMLRFItez6abPYYd/MffbN4uHT89033UPVInfje1ir+Glbm7msGjKR1mbg/UFv7KFqYzGfwUhq6cbHZle1Jp3QZ65fjMwcize3hbEcu1ZnZkVCb+wBq8L86D4JGl1sm+E9AbMzO7MzO7MzO7Mze586QQqdcav7ggO3juMfF8Whlsj+6hXuzx17x0aWmswO/nBLbPaZ8RqatzdHsO/36In94WOltAOnXSZwoR7A+Qa3H5q/CEH+aA5+OfaQ6ASCYGAuZq3nAi4KjML2oH6T9qxX05+W3T5pt5+WQ5NUpA1s99jMpZt05zpiPxzVpOF1gukSFACPDN3+1p0l/3qbHf1rVBQO9gMFISz7ZnE/Xe6wX3oqXzc7KgoH+4GCEFoXA8VL20CuGprCTG6xkzZwyGZIUTjUDxSEwOybu4regzC3mxLXqm8zVhsoYUXai2aF5qQolL7KQB1QQeDvVWZndmZndmZn9nNn/3WuB9sMszM7szM7sw+WXXvEQgYTRYOyNxA9IEMJiyHZjSijWi1pUOxIDcqWiao6uSgalN0E70BYT7YncJ1CFI0074Uvg55KFI1i715U1SlF0bD3maRSGH4nfbn0VKJo+Pt77qKqTi2KRvlePZObI7PzXozZmZ3ZmZ3Zmf1/YjdO3qjY3xH0rBW8PN9jvDyv3cyR+R7DZvdCNq7pQk8iwfE243l7NUTR9CYSHLNWk8qPRsKcJu9CdJHgmHlXqos9vkhwPLsNuWptpi+R4Hh2G3KF+Vgte3yR4FXfq620Orw9QTleDpLd3M6Tvqed95HMzuzMzuzMzuw9sJep6v7XQSnEXsyBd0AZmN3WVzrAL2Rfvb8t/vK3OgukKM/ddelpkdlraVyLV7LvbZujsLuyG1CkxcURtGlalsQVbaHELfP43YYfeP9WRmGBfPWw7Bpc47dFWnaTsBy7K9ri6r3Q4w47BrSsbx7neeh5NwZTp7ZIy34ik2P3iraYxK22aguyb+c46RfvZGibgZsElfzRc9fB7oq2uJysHXvfZs+u0tDspl4RhqBgOAHGEXhpWu3Ks0VbqF6Lq9piC7vQIGszRSkDs+MfqFMbTkBJWF6almO3RVsocQsfS0yihOxXGgQpW2D4/+728p6A2Zmd2Zmd2c+SfaCHZh/u8QdspaeBZ15I7gAAAABJRU5ErkJggg==);
}
#td-introduction-modal .main-menu + p {
margin-top: 15px;
}
#td-introduction-modal footer {
padding: 10px 0;
}
#td-introduction-modal button {
margin-left: 8px;
}
#td-introduction-modal .anondata {
float: left;
margin: 5px 7px;
}
#td-introduction-modal .anondata input {
vertical-align: -10%;
}
#td-introduction-modal .anondata label {
cursor: pointer;
display: inline-block;
font-size: 14px;
}

View File

@@ -0,0 +1,67 @@
/*********************/
/* Center everything */
/*********************/
#doc {
width: 100%;
height: 100%;
margin: 0;
position: absolute;
display: table;
}
#page-outer {
display: table-cell;
vertical-align: middle;
}
#page-container {
padding: 0 20px !important;
width: 100% !important;
box-sizing: border-box !important;
}
.page-canvas {
margin: 0 auto !important;
}
/*******************/
/* General styling */
/*******************/
body {
/* remove scrollbar */
overflow: hidden !important;
}
.page-canvas {
/* tweak page shadow */
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
}
.topbar {
/* hide top bar */
display: none !important;
}
.page-canvas, .buttons, .btn, input {
/* sharpen borders */
border-radius: 0 !important;
}
input {
/* tweak input padding */
padding: 5px 8px 4px !important;
}
button[type='submit'] {
/* style buttons */
border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
}
.tweetduck-helper {
/* custom login text */
margin-top: 15px !important;
font-weight: bold !important;
}

View File

@@ -0,0 +1,38 @@
/*****************************/
/* Fix min width and margins */
/*****************************/
.page-canvas {
width: auto !important;
max-width: 888px;
}
.signout-wrapper {
width: auto !important;
margin: 0 auto !important;
}
.signout {
margin: 60px 0 54px !important;
}
.buttons {
padding-bottom: 0 !important;
}
/*******************/
/* General styling */
/*******************/
.aside {
/* hide elements around dialog */
display: none;
}
.buttons button, .buttons a {
/* style buttons */
display: inline-block;
margin: 0 4px !important;
border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
}

View File

@@ -0,0 +1,126 @@
#tweetduck-update {
position: fixed;
bottom: 0;
width: 200px;
height: 178px;
z-index: 9999;
color: #fff;
background-color: rgb(32, 94, 138);
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
transition: transform 400ms cubic-bezier(.02, .01, .47, 1);
}
#tweetduck-update.hidden-below {
transform: translateY(178px);
}
#tweetduck-update .tdu-title {
font-size: 15px;
font-weight: bold;
margin: 8px 0 2px;
cursor: default;
}
#tweetduck-update .tdu-info {
display: inline-block;
font-size: 14px;
margin: 3px 0;
}
#tweetduck-update .tdu-showlog {
text-decoration: underline;
cursor: pointer;
}
#tweetduck-update .tdu-buttons button {
display: block;
margin: 7px auto 0;
padding: 4px 10px;
width: 80%;
height: 30px;
border: 0;
border-radius: 1px;
outline: none;
font-size: 14px;
color: #fff;
background-color: #419de0;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.5) !important;
transition: box-shadow 0.2s ease;
}
#tweetduck-update .tdu-buttons button:hover {
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
}
#tweetduck-update .tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
background-color: #607a8e;
color: #dfdfdf;
}
#tweetduck-changelog {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
}
#tweetduck-changelog-box {
position: absolute;
width: 60%;
height: 75%;
max-width: calc(90% - 200px);
max-height: 90%;
left: calc(50% + 100px);
top: 50%;
padding: 12px;
overflow-y: auto;
transform: translateX(-50%) translateY(-50%);
font-size: 14px;
color: #000;
background-color: #fff;
box-sizing: border-box;
}
#tweetduck-changelog h2 {
margin: 0 0 7px;
font-size: 23px;
}
#tweetduck-changelog h2 + br {
display: none;
}
#tweetduck-changelog h3 {
margin: 0 0 5px 7px;
font-size: 18px;
}
#tweetduck-changelog p {
margin: 8px 8px 0 6px;
}
#tweetduck-changelog p.li {
margin: 0 8px 2px 30px;
display: list-item;
}
#tweetduck-changelog p.l2 {
margin-left: 50px !important;
}
#tweetduck-changelog a {
color: #247fbb;
}
#tweetduck-changelog code {
padding: 0 4px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
color: #24292e;
background-color: rgba(27, 31, 35, 0.05);
}

View File

@@ -1,104 +1,7 @@
(function($, $TD){ (function($, $TD){
$(document).one("TD.ready", function(){ $(document).one("TD.ready", function(){
let css = $(` let css = $(`<style>#import "styles/introduction.css"</style>`).appendTo(document.head);
<style> let ele = $(`#import "markup/introduction.html"`).appendTo(".js-app");
#td-introduction-modal {
display: block;
}
#td-introduction-modal .mdl {
width: 90%;
max-width: 830px;
height: 328px;
}
#td-introduction-modal .mdl-inner {
padding-top: 0;
}
#td-introduction-modal .mdl-header-title {
cursor: default;
}
#td-introduction-modal .mdl-content {
padding: 4px 16px 0;
overflow-y: auto;
}
#td-introduction-modal p {
margin: 12px 0;
font-size: 1.4rem;
}
#td-introduction-modal p strong {
font-weight: normal;
text-shadow: 0 0 #000;
}
#td-introduction-modal .main-menu {
float: left;
width: 187px;
height: 124px;
margin-right: 12px;
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.33);
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALsAAAB8CAMAAAAGozFUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACZUExURQAAAAAANgAAYQA2YQA2iABhYQBhrDYAADYANjYAYTY2ADY2NjY2YTY2iDZhrDaIiDaIz2EAAGEANmEAYWE2NmE2iGFhNmFhYWGs8og2AIg2Nog2YYiIYYisiIjPrIjPz4jP8qxhAKxhNqzPiKzyrKzy8szMzM+INs/PiM/yrM/yz8/y8tfX1/Dw8PKsYfLPiPLyrPLyz/Ly8nG9GIQAAARDSURBVHja7ZyPd9IwEMdDxbI5cemcWJjbWrWrDi2Q//+PM7m7pOGHfcORlD6v7w1ok2wfwqXkvrs78TTcQzypoR6DZ//dcTA7szM7s8dgX2dCiJxGrG+Kvd+C1w619M+uoZqkGiy7AxvivI+XajURSWVO9AtjQ6UQqTIn18R+PxFSP82SCnuUOYxdfdjMzSmNn9mPMJa9p/QWpH5cZ5rjgqbanNQjOMnGS315nWl+7NGkqrmSqpb6hRuvm+PO++qygtkWKZ2YOW2EGBWr6dK3mTKHmcYeuu3r/fR5UawmUrXjY9tMLRFItez6abPYYd/MffbN4uHT89033UPVInfje1ir+Glbm7msGjKR1mbg/UFv7KFqYzGfwUhq6cbHZle1Jp3QZ65fjMwcize3hbEcu1ZnZkVCb+wBq8L86D4JGl1sm+E9AbMzO7MzO7MzO7Mze586QQqdcav7ggO3juMfF8Whlsj+6hXuzx17x0aWmswO/nBLbPaZ8RqatzdHsO/36In94WOltAOnXSZwoR7A+Qa3H5q/CEH+aA5+OfaQ6ASCYGAuZq3nAi4KjML2oH6T9qxX05+W3T5pt5+WQ5NUpA1s99jMpZt05zpiPxzVpOF1gukSFACPDN3+1p0l/3qbHf1rVBQO9gMFISz7ZnE/Xe6wX3oqXzc7KgoH+4GCEFoXA8VL20CuGprCTG6xkzZwyGZIUTjUDxSEwOybu4regzC3mxLXqm8zVhsoYUXai2aF5qQolL7KQB1QQeDvVWZndmZndmZn9nNn/3WuB9sMszM7szM7sw+WXXvEQgYTRYOyNxA9IEMJiyHZjSijWi1pUOxIDcqWiao6uSgalN0E70BYT7YncJ1CFI0074Uvg55KFI1i715U1SlF0bD3maRSGH4nfbn0VKJo+Pt77qKqTi2KRvlePZObI7PzXozZmZ3ZmZ3Zmf1/YjdO3qjY3xH0rBW8PN9jvDyv3cyR+R7DZvdCNq7pQk8iwfE243l7NUTR9CYSHLNWk8qPRsKcJu9CdJHgmHlXqos9vkhwPLsNuWptpi+R4Hh2G3KF+Vgte3yR4FXfq620Orw9QTleDpLd3M6Tvqed95HMzuzMzuzMzuw9sJep6v7XQSnEXsyBd0AZmN3WVzrAL2Rfvb8t/vK3OgukKM/ddelpkdlraVyLV7LvbZujsLuyG1CkxcURtGlalsQVbaHELfP43YYfeP9WRmGBfPWw7Bpc47dFWnaTsBy7K9ri6r3Q4w47BrSsbx7neeh5NwZTp7ZIy34ik2P3iraYxK22aguyb+c46RfvZGibgZsElfzRc9fB7oq2uJysHXvfZs+u0tDspl4RhqBgOAHGEXhpWu3Ks0VbqF6Lq9piC7vQIGszRSkDs+MfqFMbTkBJWF6almO3RVsocQsfS0yihOxXGgQpW2D4/+728p6A2Zmd2Zmd2c+SfaCHZh/u8QdspaeBZ15I7gAAAABJRU5ErkJggg==);
}
#td-introduction-modal .main-menu + p {
margin-top: 15px;
}
#td-introduction-modal footer {
padding: 10px 0;
}
#td-introduction-modal button {
margin-left: 8px;
}
#td-introduction-modal .anondata {
float: left;
margin: 5px 7px;
}
#td-introduction-modal .anondata input {
vertical-align: -10%;
}
#td-introduction-modal .anondata label {
cursor: pointer;
display: inline-block;
font-size: 14px;
}
</style>`).appendTo(document.head);
let ele = $(`
<div id="td-introduction-modal" class="ovl">
<div class="mdl is-inverted-dark">
<header class="mdl-header">
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
<a href="#" class="mdl-dismiss link-normal-dark"><i class="icon icon-close"></i></a>
</header>
<div class="mdl-inner">
<div class="mdl-content">
<p>Thank you for downloading TweetDuck!</p>
<p><a id="td-introduction-follow" href="#">Follow @TryMyAwesomeApp</a> for latest news and updates about the app.</p>
<div class="main-menu"></div>
<p><strong>Right-click anywhere</strong> or click <strong>Settings&nbsp;&nbsp;TweetDuck</strong> in the left panel to open the main menu. You can also right-click links, tweets, images and videos, and desktop notifications to access their respective context menus.</p>
<p>Click <strong>Show Guide</strong> to see awesome features TweetDuck offers, or view the guide later by going to <strong>About TweetDuck</strong> and clicking the help button on top.</p>
</div>
<footer class="txt-right">
<div class="anondata">
<input id="td-anonymous-data" type="checkbox" checked>
<label for="td-anonymous-data">Send anonymous usage data</label>
<label>&nbsp;(<a href="https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data" rel="nofollow">learn more</a>)</label>
</div>
<button class="btn btn-positive" data-guide><span class="label">Show Guide</span></button>
<button class="btn btn-positive"><span class="label">Close</span</button>
</footer>
</div>
</div>
</div>`).appendTo(".js-app"); /* TODO btn-positive is removed, check all files again */
let tdUser = null; let tdUser = null;
let loadTweetDuckUser = (onSuccess, onError) => { let loadTweetDuckUser = (onSuccess, onError) => {

View File

@@ -2,26 +2,26 @@
// //
// Variable: Collection of all <a> tags. // Variable: Collection of all <a> tags.
// //
var links = document.getElementsByTagName("A"); const links = document.getElementsByTagName("A");
// //
// Function: Adds an event listener to all elements in the array or collection. // Function: Adds an event listener to all elements in the array or collection.
// //
var addEventListener = function(collection, type, listener){ const addEventListener = function(collection, type, listener){
for(let index = 0; index < collection.length; index++){ for(let ele of collection){
collection[index].addEventListener(type, listener); ele.addEventListener(type, listener);
} }
}; };
// //
// Block: Hook into links to bypass default open function and t.co, and handle skipping notification when opening links. // Block: Hook into links to bypass default open function, and handle skipping notification when opening links.
// //
(function(){ (function(){
const onLinkClick = function(e){ const onLinkClick = function(e){
if (e.button === 0 || e.button === 1){ if (e.button === 0 || e.button === 1){
let ele = e.currentTarget; let ele = e.currentTarget;
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href")); $TD.openBrowser(ele.href);
e.preventDefault(); e.preventDefault();
if ($TDX.skipOnLinkClick){ if ($TDX.skipOnLinkClick){
@@ -38,27 +38,20 @@
addEventListener(links, "auxclick", onLinkClick); addEventListener(links, "auxclick", onLinkClick);
})(); })();
//
// Block: Allow bypassing of t.co in context menus.
//
addEventListener(links, "contextmenu", function(e){
$TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
});
// //
// Block: Expand shortened links on hover or display tooltip. // Block: Expand shortened links on hover or display tooltip.
// //
(function(){ (function(){
var prevMouseX = -1, prevMouseY = -1; let prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed; let tooltipTimer, tooltipDisplayed;
addEventListener(links, "mouseenter", function(e){ addEventListener(links, "mouseenter", function(e){
var me = e.currentTarget; let me = e.currentTarget;
var url = me.getAttribute("data-full-url"); let url = me.getAttribute("data-full-url");
return if !url; return if !url;
var text = me.textContent; let text = me.textContent;
return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
@@ -79,7 +72,7 @@
return if !e.currentTarget.hasAttribute("data-full-url"); return if !e.currentTarget.hasAttribute("data-full-url");
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
var prevText = e.currentTarget.getAttribute("td-prev-text"); let prevText = e.currentTarget.getAttribute("td-prev-text");
if (prevText){ if (prevText){
e.currentTarget.innerHTML = prevText; e.currentTarget.innerHTML = prevText;
@@ -96,7 +89,7 @@
addEventListener(links, "mousemove", function(e){ addEventListener(links, "mousemove", function(e){
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){ if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
var url = e.currentTarget.getAttribute("data-full-url"); let url = e.currentTarget.getAttribute("data-full-url");
return if !url; return if !url;
$TD.displayTooltip(url); $TD.displayTooltip(url);
@@ -117,7 +110,7 @@
// Block: Setup a handler for 'Show this thread'. // Block: Setup a handler for 'Show this thread'.
// //
(function(){ (function(){
var btn = document.getElementById("tduck-show-thread"); let btn = document.getElementById("tduck-show-thread");
return if !btn; return if !btn;
btn.addEventListener("click", function(){ btn.addEventListener("click", function(){
@@ -129,11 +122,10 @@
// Block: Setup a skip button. // Block: Setup a skip button.
// //
if (!document.body.hasAttribute("td-example-notification")){ if (!document.body.hasAttribute("td-example-notification")){
document.body.insertAdjacentHTML("afterbegin", ` document.body.children[0].insertAdjacentHTML("beforeend", `
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600"> <svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547"> <path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
</svg> </svg>`);
`);
document.getElementById("td-skip").addEventListener("click", function(){ document.getElementById("td-skip").addEventListener("click", function(){
$TD.loadNextNotification(); $TD.loadNextNotification();
@@ -150,4 +142,9 @@
document.body.addEventListener("mouseleave", function(){ document.body.addEventListener("mouseleave", function(){
document.body.classList.remove("td-hover"); document.body.classList.remove("td-hover");
}); });
//
// Block: Force a reset of scroll position on every load.
//
history.scrollRestoration = "manual";
})($TD, $TDX); })($TD, $TDX);

View File

@@ -1,6 +1,4 @@
(function(){ (function(){
var isReloading = false;
// //
// Class: Abstract plugin base class. // Class: Abstract plugin base class.
// //
@@ -107,12 +105,16 @@
// //
// Block: Setup a function to reload the page. // Block: Setup a function to reload the page.
// //
(function(){
let isReloading = false;
window.TDPF_requestReload = function(){ window.TDPF_requestReload = function(){
if (!isReloading){ if (!isReloading){
window.setTimeout(window.TDGF_reload, 1); window.setTimeout(window.TDGF_reload, 1);
isReloading = true; isReloading = true;
} }
}; };
})();
// //
// Block: Setup bridges to global functions. // Block: Setup bridges to global functions.
@@ -121,4 +123,7 @@
window.TDPF_playVideo = window.TDGF_playVideo; window.TDPF_playVideo = window.TDGF_playVideo;
window.TDPF_reloadColumns = window.TDGF_reloadColumns; window.TDPF_reloadColumns = window.TDGF_reloadColumns;
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent; window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
window.TDPF_injectMustache = window.TDGF_injectMustache;
#import "scripts/plugins.base.js"
})(); })();

View File

@@ -14,3 +14,5 @@ window.TD_PLUGINS = {
plugin.obj.run(); plugin.obj.run();
} }
}; };
#import "scripts/plugins.base.js"

View File

@@ -10,7 +10,7 @@
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0; let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => { $TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => {
let framesLeft = 5; // basic render is done in 1 frame, large media take longer let framesLeft = {frames}; // basic render is done in 1 frame, large media take longer
let onNextFrame = function(){ let onNextFrame = function(){
if (--framesLeft < 0){ if (--framesLeft < 0){

View File

@@ -150,6 +150,7 @@ button {
.activity-header .icon-user-filled { .activity-header .icon-user-filled {
vertical-align: sub !important; vertical-align: sub !important;
margin-right: 4px !important;
} }
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
@@ -220,6 +221,50 @@ a[data-full-url] {
bottom: 0 !important; bottom: 0 !important;
} }
/**********************************************************/
/* Prevent column icons from being hidden by column title */
/**********************************************************/
.column-header-title {
overflow: hidden !important;
}
.column-heading {
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.column-header-links {
max-width: 100% !important;
}
[data-td-icon="icon-message"] .column-header-links {
min-width: 86px;
}
/************************************************************/
/* Fix modal dialogs breaking when window size is too small */
/************************************************************/
.ovl:before, .overlay:before {
display: none !important;
}
.ovl, .overlay {
flex-direction: column;
justify-content: center;
}
.ovl[style="display: block;"] {
display: flex !important;
}
#tduck .overlay {
display: flex;
}
/*******************************************/ /*******************************************/
/* Fix general visual issues or annoyances */ /* Fix general visual issues or annoyances */
/*******************************************/ /*******************************************/
@@ -234,6 +279,37 @@ a[data-full-url] {
vertical-align: -10% !important; vertical-align: -10% !important;
} }
.column-title {
/* fix alignment of everything in column headers */
padding-top: 1px !important;
}
.column-title .attribution {
/* fix alignment of usernames in column headers */
margin: 0 0 0 6px !important;
}
.app-navigator .tooltip {
/* fix tooltips in navigation */
display: none !important;
}
.column-header-links .icon {
/* fix tooltips in column headers */
height: calc(1em + 8px) !important;
}
.column-header-temp:not(.js-column-header) {
/* fix missing column header padding in Edit List dialog */
padding-left: 10px;
padding-right: 10px;
}
.js-column-options .btn-options-tray {
/* fix underline on buttons in column options */
text-decoration: none !important;
}
#tduck .nav-user-info .hide-condensed { #tduck .nav-user-info .hide-condensed {
/* move login account info */ /* move login account info */
margin: 0px !important; margin: 0px !important;
@@ -241,7 +317,7 @@ a[data-full-url] {
} }
html[data-td-font='smallest'] .sprite-verified-mini { html[data-td-font='smallest'] .sprite-verified-mini {
/* fix cut off badge when zoomed in */ /* fix cut off badge in timelines */
width: 13px !important; width: 13px !important;
height: 13px !important; height: 13px !important;
background-position: -223px -99px !important; background-position: -223px -99px !important;
@@ -261,11 +337,21 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
background-position: -223px -97px !important; background-position: -223px -97px !important;
} }
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
/* fix cut off badge in follow chirps */
margin-top: -7px !important;
}
.keyboard-shortcut-list { .keyboard-shortcut-list {
/* fix keyboard navigation alignment */ /* fix keyboard navigation alignment */
vertical-align: top !important; vertical-align: top !important;
} }
.column-message.is-actionable span:hover > .icon-small-valigned {
/* add a visual response when hovering individual filter icons; black theme uses a value of 20 */
filter: saturate(10);
}
.tweet-detail-wrapper .js-media-gif-container { .tweet-detail-wrapper .js-media-gif-container {
/* GIFs in detail view don't trigger the pointer cursor */ /* GIFs in detail view don't trigger the pointer cursor */
cursor: pointer; cursor: pointer;
@@ -290,9 +376,9 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
/* Fix glaring visual issues that twitter hasn't fixed yet smh */ /* Fix glaring visual issues that twitter hasn't fixed yet smh */
/***************************************************************/ /***************************************************************/
.column-nav-link .attribution { .js-column-nav-list .attribution {
/* fix cut off account names */ /* fix cut off account names */
position: absolute !important; line-height: 1.1 !important;
} }
#tduck .js-docked-compose .js-drawer-close { #tduck .js-docked-compose .js-drawer-close {
@@ -311,25 +397,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
vertical-align: -15% !important; vertical-align: -15% !important;
} }
/************************************************/
/* Fix tooltips in navigation and column header */
/************************************************/
.app-navigator .tooltip {
display: none !important;
}
.js-column-header .column-header-link {
padding: 0 !important;
}
.js-column-header .column-header-link .icon {
width: calc(1em + 8px);
height: 100%;
padding: 9px 4px;
box-sizing: border-box;
}
/*******************************************/ /*******************************************/
/* Fix one pixel space below column header */ /* Fix one pixel space below column header */
/*******************************************/ /*******************************************/
@@ -342,10 +409,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
border-bottom: none !important; border-bottom: none !important;
} }
.is-options-open .column-type-icon {
bottom: 27px !important;
}
/********************************************/ /********************************************/
/* Fix cut off usernames in Messages column */ /* Fix cut off usernames in Messages column */
/********************************************/ /********************************************/

View File

@@ -2,7 +2,9 @@
/* General */ /* General */
/***********/ /***********/
body.td-notification { html, body {
height: auto !important;
overflow-x: hidden !important;
overflow-y: auto !important; overflow-y: auto !important;
} }
@@ -59,13 +61,25 @@ a[data-full-url] {
/* Fix general visual issues or annoyances */ /* Fix general visual issues or annoyances */
/*******************************************/ /*******************************************/
html[data-td-font='smallest'] .sprite-verified-mini {
/* fix cut off badge in timelines */
width: 13px !important;
height: 13px !important;
background-position: -223px -99px !important;
}
html[data-td-font='smallest'] .badge-verified:before { html[data-td-font='smallest'] .badge-verified:before {
/* fix cut off badge icon */ /* fix cut off badge in notifications */
width: 13px !important; width: 13px !important;
height: 13px !important; height: 13px !important;
background-position: -223px -98px !important; background-position: -223px -98px !important;
} }
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
/* fix cut off badge in follow chirps */
margin-top: -7px !important;
}
.account-inline .username { .account-inline .username {
vertical-align: 10% !important; vertical-align: 10% !important;
} }
@@ -88,6 +102,7 @@ html[data-td-font='smallest'] .badge-verified:before {
.activity-header .icon-user-filled { .activity-header .icon-user-filled {
vertical-align: sub !important; vertical-align: sub !important;
margin-right: 4px !important;
} }
.td-notification-padded .item-img { .td-notification-padded .item-img {
@@ -136,7 +151,7 @@ html[data-td-font='smallest'] .badge-verified:before {
#td-skip { #td-skip {
position: fixed; position: fixed;
left: 30px; left: 29px;
bottom: 10px; bottom: 10px;
z-index: 1000; z-index: 1000;
cursor: pointer; cursor: pointer;

View File

@@ -2,40 +2,21 @@
// //
// Function: Inject custom CSS into the page. // Function: Inject custom CSS into the page.
// //
var injectCSS = function(){ const injectCSS = function(){
if (!document.head){ if (!document.head){
setTimeout(injectCSS, 5); setTimeout(injectCSS, 5);
return; return;
} }
var style = document.createElement("style"); let style = document.createElement("style");
document.head.appendChild(style);
let addRule = (rule) => { style.innerText = `#import "styles/twitter.base.css"`;
style.sheet.insertRule(rule, 0);
};
addRule("body { overflow: hidden !important; }"); // remove scrollbar
addRule(".page-canvas { box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; }"); // change page box shadow
addRule(".topbar { display: none !important; }"); // hide top bar
addRule(".page-canvas, .buttons, .btn, input { border-radius: 0 !important; }"); // sharpen borders
addRule("input { padding: 5px 8px 4px !important; }"); // tweak input padding
addRule("button[type='submit'] { border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
addRule("#doc { width: 100%; height: 100%; margin: 0; position: absolute; display: table; }"); // center everything
addRule("#page-outer { display: table-cell; vertical-align: middle; }"); // center everything
addRule("#page-container { padding: 0 20px !important; width: 100% !important; box-sizing: border-box !important; }"); // center everything
addRule(".page-canvas { margin: 0 auto !important; }"); // center everything
if (location.pathname === "/logout"){ if (location.pathname === "/logout"){
addRule(".page-canvas { width: auto !important; max-width: 888px; }"); // fix min width style.innerText += `#import "styles/twitter.logout.css"`;
addRule(".signout-wrapper { width: auto !important; margin: 0 auto !important; }"); // fix min width and margins
addRule(".signout { margin: 60px 0 54px !important; }"); // fix dialog margins
addRule(".buttons { padding-bottom: 0 !important; }"); // fix dialog margins
addRule(".aside { display: none; }"); // hide elements around logout dialog
addRule(".buttons button, .buttons a { display: inline-block; margin: 0 4px !important; border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
} }
document.head.appendChild(style);
}; };
setTimeout(injectCSS, 1); setTimeout(injectCSS, 1);
@@ -45,7 +26,7 @@
// //
if (location.pathname === "/login"){ if (location.pathname === "/login"){
document.addEventListener("DOMContentLoaded", function(){ document.addEventListener("DOMContentLoaded", function(){
let openLinkExternally = function(e){ const openLinkExternally = function(e){
let href = e.currentTarget.getAttribute("href"); let href = e.currentTarget.getAttribute("href");
$TD.openBrowser(href[0] === '/' ? location.origin+href : href); $TD.openBrowser(href[0] === '/' ? location.origin+href : href);
@@ -53,10 +34,30 @@
e.stopPropagation(); e.stopPropagation();
}; };
let links = document.getElementsByTagName("A"); for(let link of document.getElementsByTagName("A")){
link.addEventListener("click", openLinkExternally);
}
for(let index = 0; index < links.length; index++){ let texts = document.querySelector(".page-canvas > div:last-child");
links[index].addEventListener("click", openLinkExternally);
if (texts){
texts.insertAdjacentHTML("beforeend", `<p class="tweetduck-helper">Used the TweetDuck app before? <a href="#">Import your profile »</a></p>`);
texts.querySelector(".tweetduck-helper > a").addEventListener("click", function(){
$TD.openProfileImport();
});
}
});
}
//
// Block: Fix broken Cancel button on logout page.
//
else if (location.pathname === "/logout"){
document.addEventListener("DOMContentLoaded", function(){
let cancel = document.querySelector(".buttons .cancel");
if (cancel && cancel.tagName === "A"){
cancel.href = "https://tweetdeck.twitter.com/";
} }
}); });
} }

View File

@@ -1,215 +1,110 @@
(function($, $TDU){ (function($TDU){
// //
// Function: Creates the update notification element. Removes the old one if already exists. // Function: Creates the update notification element. Removes the old one if already exists.
// //
var displayNotification = function(version, changelog){ const displayNotification = function(version, changelog){
// styles // styles
var css = $("#tweetduck-update-css"); let css = document.getElementById("tweetduck-update-css");
if (!css.length){ if (!css){
css = $(` css = document.createElement("style");
<style id='tweetduck-update-css'> css.id = "tweetduck-update-css";
#tweetduck-update { css.innerText = `#import "styles/update.css"`;
position: absolute; document.head.appendChild(css);
bottom: 0;
width: 200px;
height: 178px;
z-index: 9999;
color: #fff;
background-color: rgb(32, 94, 138);
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
}
#tweetduck-update .tdu-title {
font-size: 15px;
font-weight: bold;
margin: 8px 0 2px;
cursor: default;
}
#tweetduck-update .tdu-info {
display: inline-block;
font-size: 14px;
margin: 3px 0;
}
#tweetduck-update .tdu-showlog {
text-decoration: underline;
cursor: pointer;
}
#tweetduck-update .tdu-buttons button {
display: block;
margin: 7px auto 0;
padding: 4px 10px;
width: 80%;
height: 30px;
border: 0;
border-radius: 1px;
outline: none;
font-size: 14px;
color: #fff;
background-color: #419de0;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.5) !important;
transition: box-shadow 0.2s ease;
}
#tweetduck-update .tdu-buttons button:hover {
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
}
#tweetduck-update .tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
background-color: #607a8e;
color: #dfdfdf;
}
#tweetduck-changelog {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
}
#tweetduck-changelog-box {
position: absolute;
width: 60%;
height: 75%;
max-width: calc(90% - 200px);
max-height: 90%;
left: calc(50% + 100px);
top: 50%;
padding: 12px;
overflow-y: auto;
transform: translateX(-50%) translateY(-50%);
font-size: 14px;
color: #000;
background-color: #fff;
box-sizing: border-box;
}
#tweetduck-changelog h2 {
margin: 0 0 7px;
font-size: 23px;
}
#tweetduck-changelog h2 + br {
display: none;
}
#tweetduck-changelog h3 {
margin: 0 0 5px 7px;
font-size: 18px;
}
#tweetduck-changelog p {
margin: 8px 8px 0 6px;
}
#tweetduck-changelog p.li {
margin: 0 8px 2px 30px;
display: list-item;
}
#tweetduck-changelog p.l2 {
margin-left: 50px !important;
}
#tweetduck-changelog a {
color: #247fbb;
}
#tweetduck-changelog code {
padding: 0 4px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
color: #24292e;
background-color: rgba(27, 31, 35, 0.05);
}
</style>
`).appendTo(document.head);
} }
// changelog // changelog
var log = $("#tweetduck-changelog"); let log = document.getElementById("tweetduck-changelog");
if (!log.length){ if (!log){
var log = $(` log = document.createElement("div");
<div id='tweetduck-changelog'> log.id = "tweetduck-changelog";
log.style.display = "none";
log.innerHTML = `
<div id='tweetduck-changelog-box'> <div id='tweetduck-changelog-box'>
<h2>TweetDuck Update ${version}</h2> <h2>TweetDuck Update ${version}</h2>
${markdown(atob(changelog))} ${markdown(atob(changelog))}
</div> </div>`;
</div>
`).appendTo(document.body).css("display", "none"); document.body.appendChild(log);
} }
// notification // notification
var ele = $("#tweetduck-update"); let ele = document.getElementById("tweetduck-update");
var existed = ele.length > 0; let existed = !!ele;
if (existed){ if (existed){
ele.remove(); ele.remove();
} }
ele = $(` ele = document.createElement("div");
<div id='tweetduck-update'> ele.id = "tweetduck-update";
ele.innerHTML = `
<p class='tdu-title'>T&#8202;weetDuck Update ${version}</p> <p class='tdu-title'>T&#8202;weetDuck Update ${version}</p>
<p class='tdu-info tdu-showlog'>View update information</p> <p class='tdu-info tdu-showlog'>View update information</p>
<div class='tdu-buttons'> <div class='tdu-buttons'>
<button class='tdu-btn-download'>Update now</button> <button class='tdu-btn-download'>Update now</button>
<button class='tdu-btn-later'>Remind me later</button> <button class='tdu-btn-later'>Remind me later</button>
<button class='tdu-btn-ignore'>Ignore this update</button> <button class='tdu-btn-ignore'>Ignore this update</button>
</div> </div>`;
</div>
`).appendTo(document.body).css("display", existed ? "block" : "none");
// ui logic if (!existed){
var hide = function(){ ele.classList.add("hidden-below");
}
document.body.appendChild(ele);
// ui functions
const exitNow = function(){
ele.remove(); ele.remove();
log.remove(); log.remove();
css.remove(); css.remove();
}; };
var slide = function(){ const exitSlide = function(){
log.hide(); log.style.display = "none";
ele.slideUp(hide);
ele.classList.add("hidden-below");
setTimeout(exitNow, 400);
}; };
ele.children(".tdu-showlog").click(function(){ const onClick = function(element, callback){
log.toggle(); element.addEventListener("click", callback);
};
// ui listeners
onClick(ele.querySelector(".tdu-showlog"), function(){
log.style.display = window.getComputedStyle(log).display === "none" ? "block" : "none";
}); });
log.click(function(){ onClick(log, function(){
log.hide(); log.style.display = "none";
}).children().first().click(function(e){ });
onClick(log.children[0], function(e){
e.stopPropagation(); e.stopPropagation();
}); });
var buttonDiv = ele.children(".tdu-buttons").first(); onClick(ele.querySelector(".tdu-btn-download"), function(){
exitNow();
buttonDiv.children(".tdu-btn-download").click(function(){
hide();
$TDU.onUpdateAccepted(); $TDU.onUpdateAccepted();
}); });
buttonDiv.children(".tdu-btn-later").click(function(){ onClick(ele.querySelector(".tdu-btn-later"), function(){
$TDU.onUpdateDelayed(); $TDU.onUpdateDelayed();
slide(); exitSlide();
}); });
buttonDiv.children(".tdu-btn-ignore").click(function(){ onClick(ele.querySelector(".tdu-btn-ignore"), function(){
$TDU.onUpdateDismissed(); $TDU.onUpdateDismissed();
slide(); exitSlide();
}); });
// finalize notification
if (!existed){ if (!existed){
ele.slideDown(); ele.getBoundingClientRect(); // reflow
ele.classList.remove("hidden-below");
} }
return ele; return ele;
@@ -218,7 +113,7 @@
// //
// Function: Ghetto-converts markdown to HTML. // Function: Ghetto-converts markdown to HTML.
// //
var markdown = function(md){ const markdown = function(md){
return md.replace(/&/g, "&amp;") return md.replace(/&/g, "&amp;")
.replace(/</g, "&lt;") .replace(/</g, "&lt;")
.replace(/>/g, "&gt;") .replace(/>/g, "&gt;")
@@ -237,12 +132,17 @@
// //
// Block: Check updates on startup. // Block: Check updates on startup.
// //
if ("$" in window && typeof $._data === "function" && "TD" in $._data(document, "events")){
$(document).one("TD.ready", function(){ $(document).one("TD.ready", function(){
$TDU.triggerUpdateCheck(); $TDU.triggerUpdateCheck();
}); });
}
else{
$TDU.triggerUpdateCheck();
}
// //
// Block: Setup global functions. // Block: Setup global functions.
// //
window.TDUF_displayNotification = displayNotification; window.TDUF_displayNotification = displayNotification;
})($, $TDU); })($TDU);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" /> <Import Project="packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" /> <Import Project="packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props')" />
<Import Project="packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" /> <Import Project="packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props')" />
<Import Project="packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" /> <Import Project="packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -15,25 +15,13 @@
<AssemblyName>TweetDuck</AssemblyName> <AssemblyName>TweetDuck</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
<TargetFrameworkProfile> </PropertyGroup>
</TargetFrameworkProfile> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<PublishUrl>publish\</PublishUrl> <StartArguments>-datafolder TweetDuckDebug</StartArguments>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@@ -44,23 +32,15 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<UseVSHostingProcess>false</UseVSHostingProcess>
<LangVersion>7</LangVersion> <LangVersion>7</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DefineConstants>
</DefineConstants>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<LangVersion>7</LangVersion> <LangVersion>7</LangVersion>
</PropertyGroup> </PropertyGroup>
@@ -94,6 +74,8 @@
<Compile Include="Core\FormManager.cs" /> <Compile Include="Core\FormManager.cs" />
<Compile Include="Core\Handling\ContextMenuGuide.cs" /> <Compile Include="Core\Handling\ContextMenuGuide.cs" />
<Compile Include="Core\Handling\DragHandlerBrowser.cs" /> <Compile Include="Core\Handling\DragHandlerBrowser.cs" />
<Compile Include="Core\Handling\Filters\ResponseFilterBase.cs" />
<Compile Include="Core\Handling\Filters\ResponseFilterVendor.cs" />
<Compile Include="Core\Handling\General\BrowserProcessHandler.cs" /> <Compile Include="Core\Handling\General\BrowserProcessHandler.cs" />
<Compile Include="Core\Handling\ContextMenuBase.cs" /> <Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" /> <Compile Include="Core\Handling\ContextMenuBrowser.cs" />
@@ -327,11 +309,6 @@
<Compile Include="Updates\Events\UpdateEventArgs.cs" /> <Compile Include="Updates\Events\UpdateEventArgs.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5"> <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible> <Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
@@ -342,11 +319,6 @@
<ProductName>.NET Framework 3.5 SP1</ProductName> <ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install> <Install>false</Install>
</BootstrapperPackage> </BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
<Visible>False</Visible>
<ProductName>Windows Installer 4.5</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Core\FormBrowser.resx"> <EmbeddedResource Include="Core\FormBrowser.resx">
@@ -359,36 +331,54 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\icon.ico">
<TargetPath>icon.ico</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Resources\icon-small.ico" /> <None Include="Resources\Images\avatar.png" />
<None Include="Resources\icon-tray-new.ico" /> <None Include="Resources\Images\icon.ico" />
<None Include="Resources\icon-tray.ico" /> <None Include="Resources\Images\icon-tray-muted.ico" />
<None Include="Resources\Images\icon-muted.ico" />
<None Include="Resources\Images\icon-small.ico" />
<None Include="Resources\Images\icon-tray-new.ico" />
<None Include="Resources\Images\icon-tray.ico" />
<None Include="Resources\Images\spinner.apng" />
<None Include="Resources\PostBuild.ps1" /> <None Include="Resources\PostBuild.ps1" />
<None Include="Resources\spinner.apng" /> <None Include="Resources\Plugins\.debug\.meta" />
</ItemGroup> <None Include="Resources\Plugins\.debug\browser.js" />
<ItemGroup> <None Include="Resources\Plugins\clear-columns\.meta" />
<Folder Include="Resources\Plugins\" /> <None Include="Resources\Plugins\clear-columns\browser.js" />
</ItemGroup> <None Include="Resources\Plugins\edit-design\.meta" />
<ItemGroup> <None Include="Resources\Plugins\edit-design\browser.js" />
<Content Include="Resources\avatar.png" /> <None Include="Resources\Plugins\edit-design\modal.html" />
<Content Include="Resources\Scripts\code.js" /> <None Include="Resources\Plugins\edit-design\theme.black.css" />
<Content Include="Resources\Scripts\introduction.js" /> <None Include="Resources\Plugins\emoji-keyboard\.meta" />
<Content Include="Resources\Scripts\notification.js" /> <None Include="Resources\Plugins\emoji-keyboard\browser.js" />
<Content Include="Resources\Scripts\pages\error.html" /> <None Include="Resources\Plugins\emoji-keyboard\emoji-instructions.txt" />
<Content Include="Resources\Scripts\pages\example.html" /> <None Include="Resources\Plugins\emoji-keyboard\emoji-ordering.txt" />
<Content Include="Resources\Scripts\plugins.browser.js" /> <None Include="Resources\Plugins\reply-account\.meta" />
<Content Include="Resources\Scripts\plugins.js" /> <None Include="Resources\Plugins\reply-account\browser.js" />
<Content Include="Resources\Scripts\plugins.notification.js" /> <None Include="Resources\Plugins\reply-account\configuration.default.js" />
<Content Include="Resources\Scripts\styles\browser.css" /> <None Include="Resources\Plugins\templates\.meta" />
<Content Include="Resources\Scripts\styles\notification.css" /> <None Include="Resources\Plugins\templates\browser.js" />
<Content Include="Resources\Scripts\twitter.js" /> <None Include="Resources\Plugins\templates\modal.html" />
<Content Include="Resources\Scripts\update.js" /> <None Include="Resources\Plugins\timeline-polls\.meta" />
<None Include="Resources\Plugins\timeline-polls\browser.js" />
<None Include="Resources\Scripts\code.js" />
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
<None Include="Resources\Scripts\imports\styles\introduction.css" />
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
<None Include="Resources\Scripts\imports\styles\twitter.logout.css" />
<None Include="Resources\Scripts\imports\styles\update.css" />
<None Include="Resources\Scripts\introduction.js" />
<None Include="Resources\Scripts\notification.js" />
<None Include="Resources\Scripts\pages\error.html" />
<None Include="Resources\Scripts\pages\example.html" />
<None Include="Resources\Scripts\plugins.browser.js" />
<None Include="Resources\Scripts\plugins.notification.js" />
<None Include="Resources\Scripts\screenshot.js" />
<None Include="Resources\Scripts\styles\browser.css" />
<None Include="Resources\Scripts\styles\notification.css" />
<None Include="Resources\Scripts\twitter.js" />
<None Include="Resources\Scripts\update.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj"> <ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
@@ -420,6 +410,7 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
<Exec Command="del &quot;$(TargetDir)*.xml&quot;" /> <Exec Command="del &quot;$(TargetDir)*.xml&quot;" />
<Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" /> <Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" />
<Delete Files="$(TargetDir)widevinecdmadapter.dll" /> <Delete Files="$(TargetDir)widevinecdmadapter.dll" />
<Exec Command="&quot;$(ProjectDir)bld\UPDATE ONLY.bat&quot;" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" />
</Target> </Target>
<PropertyGroup> <PropertyGroup>
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent> <PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent>
@@ -428,13 +419,11 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props'))" /> <Error Condition="!Exists('packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props'))" />
<Error Condition="!Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props'))" /> <Error Condition="!Exists('packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props'))" /> <Error Condition="!Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets'))" /> <Error Condition="!Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props'))" /> <Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets'))" />
</Target> </Target>
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" /> <Import Project="packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" />
</Project> </Project>

View File

@@ -6,12 +6,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.cspr
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "subprocess\TweetDuck.Browser.csproj", "{B10B0017-819E-4F71-870F-8256B36A26AA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "subprocess\TweetDuck.Browser.csproj", "{B10B0017-819E-4F71-870F-8256B36A26AA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
EndProject 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}") = "TweetTest.System", "lib\TweetTest.System\TweetTest.System.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TweetTest.Unit", "lib\TweetTest.Unit\TweetTest.Unit.fsproj", "{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
@@ -26,9 +28,6 @@ Global
{B10B0017-819E-4F71-870F-8256B36A26AA}.Debug|x86.Build.0 = Debug|x86 {B10B0017-819E-4F71-870F-8256B36A26AA}.Debug|x86.Build.0 = Debug|x86
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.ActiveCfg = Release|x86 {B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.ActiveCfg = Release|x86
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.Build.0 = Release|x86 {B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.Build.0 = Release|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
@@ -37,6 +36,12 @@ Global
{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
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86 {72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Debug|x86
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.ActiveCfg = Debug|x86
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.Build.0 = Debug|x86
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.ActiveCfg = Debug|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web.Script.Serialization; using System.Web.Script.Serialization;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -28,7 +30,7 @@ namespace TweetDuck.Updates{
result.SetCanceled(); result.SetCanceled();
} }
else if (task.IsFaulted){ else if (task.IsFaulted){
result.SetException(task.Exception.InnerException); result.SetException(ExpandWebException(task.Exception.InnerException));
} }
else{ else{
try{ try{
@@ -59,5 +61,20 @@ namespace TweetDuck.Updates{
return new UpdateInfo(versionTag, releaseNotes, downloadUrl, installerFolder); return new UpdateInfo(versionTag, releaseNotes, downloadUrl, installerFolder);
} }
private static Exception ExpandWebException(Exception e){
if (e is WebException we && we.Response is HttpWebResponse response){
try{
using(Stream stream = response.GetResponseStream())
using(StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet ?? "utf-8"))){
return new Reporter.ExpandedLogException(e, reader.ReadToEnd());
}
}catch{
// whatever
}
}
return e;
}
} }
} }

View File

@@ -22,7 +22,7 @@ namespace TweetDuck.Updates{
this.VersionTag = versionTag; this.VersionTag = versionTag;
this.ReleaseNotes = releaseNotes; this.ReleaseNotes = releaseNotes;
this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");; this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");
} }
public void BeginSilentDownload(){ public void BeginSilentDownload(){

View File

@@ -1,30 +0,0 @@
// Copyright <20> 2010-2016 The CefSharp Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the name CefSharp nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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