Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
0195378c10 | |||
bc804c6a53 | |||
76b15f1971 | |||
c4d43c9d5b | |||
e8d3e530de | |||
e145adec58 | |||
e2dad3e477 | |||
27bdbde171 | |||
e9ec27169c | |||
2e24cb634c | |||
beb9046055 | |||
e57301952c | |||
7411279e48 | |||
16acfa85b5 | |||
41ef37f3f0 | |||
00d8538726 | |||
6eeb3f9895 | |||
d19dca6ea5 | |||
2008ccdaa4 | |||
ba2e62de3a | |||
2b62eb254d | |||
31f72b7957 | |||
fdc4616875 | |||
b7de261d25 | |||
ae78a5a026 | |||
fd2cf5d4d7 | |||
9f0997be1a | |||
dbade7f854 | |||
3cdc1e190a | |||
36bede7211 | |||
46689bb700 | |||
13e1a6543c |
@@ -44,6 +44,9 @@ namespace TweetDuck.Configuration{
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
public bool FirstRun { get; set; } = true;
|
||||
public bool AllowDataCollection { get; set; } = false;
|
||||
|
||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||
|
||||
|
@@ -11,6 +11,9 @@ using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Bridge{
|
||||
sealed class TweetDeckBridge{
|
||||
public static string FontSizeClass;
|
||||
public static string NotificationHeadContents;
|
||||
|
||||
public static string LastHighlightedTweetUrl = string.Empty;
|
||||
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||
private static string LastHighlightedTweetAuthors = string.Empty;
|
||||
@@ -22,6 +25,7 @@ namespace TweetDuck.Core.Bridge{
|
||||
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||
|
||||
public static void ResetStaticProperties(){
|
||||
FontSizeClass = NotificationHeadContents = null;
|
||||
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||
}
|
||||
|
||||
@@ -46,16 +50,18 @@ namespace TweetDuck.Core.Bridge{
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
public void LoadFontSizeClass(string fsClass){
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetFontSizeClass(fsClass);
|
||||
form.OnIntroductionClosed(showGuide, allowDataCollection);
|
||||
});
|
||||
}
|
||||
|
||||
public void LoadFontSizeClass(string fsClass){
|
||||
form.InvokeAsyncSafe(() => FontSizeClass = fsClass);
|
||||
}
|
||||
|
||||
public void LoadNotificationHeadContents(string headContents){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetHeadTag(headContents);
|
||||
});
|
||||
form.InvokeAsyncSafe(() => NotificationHeadContents = headContents);
|
||||
}
|
||||
|
||||
public void SetLastRightClickInfo(string type, string link){
|
||||
|
@@ -202,6 +202,7 @@ namespace TweetDuck.Core{
|
||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||
TweetDeckBridge.RestoreSessionData(e.Frame);
|
||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||
InjectBrowserCSS();
|
||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
||||
|
||||
@@ -210,6 +211,10 @@ namespace TweetDuck.Core{
|
||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||
}
|
||||
|
||||
if (Config.FirstRun){
|
||||
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,6 +430,10 @@ namespace TweetDuck.Core{
|
||||
|
||||
// javascript calls
|
||||
|
||||
public void InjectBrowserCSS(){
|
||||
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
|
||||
}
|
||||
|
||||
public void ReinjectCustomCSS(string css){
|
||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||
}
|
||||
@@ -439,6 +448,18 @@ namespace TweetDuck.Core{
|
||||
|
||||
// callback handlers
|
||||
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
if (Config.FirstRun){
|
||||
Config.FirstRun = false;
|
||||
Config.AllowDataCollection = allowDataCollection;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
if (showGuide){
|
||||
ShowChildForm(new FormGuide());
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenContextMenu(){
|
||||
contextMenu.Show(this, PointToClient(Cursor.Position));
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ namespace TweetDuck.Core{
|
||||
|
||||
public static void CloseAllDialogs(){
|
||||
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
||||
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
||||
if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){
|
||||
form.Close();
|
||||
}
|
||||
}
|
||||
|
15
Core/Handling/ContextMenuGuide.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
sealed class ContextMenuGuide : ContextMenuBase{
|
||||
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||
model.Clear();
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
if (HasDevTools){
|
||||
AddSeparator(model);
|
||||
AddDebugMenuItems(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Handling;
|
||||
using TweetDuck.Core.Handling.General;
|
||||
@@ -12,6 +13,18 @@ using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Notification{
|
||||
partial class FormNotificationBase : Form{
|
||||
protected static int FontSizeLevel{
|
||||
get{
|
||||
switch(TweetDeckBridge.FontSizeClass){
|
||||
case "largest": return 4;
|
||||
case "large": return 3;
|
||||
case "small": return 1;
|
||||
case "smallest": return 0;
|
||||
default: return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Point PrimaryLocation{
|
||||
get{
|
||||
UserConfig config = Program.UserConfig;
|
||||
|
@@ -33,7 +33,7 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
public bool RequiresResize{
|
||||
get{
|
||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel || CanResizeWindow;
|
||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel || CanResizeWindow;
|
||||
}
|
||||
|
||||
set{
|
||||
@@ -43,7 +43,7 @@ namespace TweetDuck.Core.Notification{
|
||||
}
|
||||
else{
|
||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
||||
prevFontSize = TweetNotification.FontSizeLevel;
|
||||
prevFontSize = FontSizeLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace TweetDuck.Core.Notification{
|
||||
get{
|
||||
switch(Program.UserConfig.NotificationSize){
|
||||
default:
|
||||
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*TweetNotification.FontSizeLevel));
|
||||
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel));
|
||||
|
||||
case TweetNotification.Size.Custom:
|
||||
return Program.UserConfig.CustomNotificationSize.Width;
|
||||
@@ -64,7 +64,7 @@ namespace TweetDuck.Core.Notification{
|
||||
get{
|
||||
switch(Program.UserConfig.NotificationSize){
|
||||
default:
|
||||
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel));
|
||||
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel));
|
||||
|
||||
case TweetNotification.Size.Custom:
|
||||
return Program.UserConfig.CustomNotificationSize.Height;
|
||||
|
@@ -1,27 +1,14 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Notification{
|
||||
sealed class TweetNotification{
|
||||
private static string FontSizeClass { get; set; }
|
||||
private static string HeadTag { get; set; }
|
||||
|
||||
private const string DefaultFontSizeClass = "medium";
|
||||
private const string DefaultHeadTag = @"<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 CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
|
||||
private const string DefaultHeadContents = @"<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>";
|
||||
|
||||
public static int FontSizeLevel{
|
||||
get{
|
||||
switch(FontSizeClass){
|
||||
case "largest": return 4;
|
||||
case "large": return 3;
|
||||
case "medium": return 2;
|
||||
case "small": return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
||||
|
||||
private static string ExampleTweetHTML;
|
||||
|
||||
@@ -39,14 +26,6 @@ namespace TweetDuck.Core.Notification{
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFontSizeClass(string newFSClass){
|
||||
FontSizeClass = newFSClass;
|
||||
}
|
||||
|
||||
public static void SetHeadTag(string headContents){
|
||||
HeadTag = headContents;
|
||||
}
|
||||
|
||||
public enum Position{
|
||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||
}
|
||||
@@ -88,8 +67,8 @@ namespace TweetDuck.Core.Notification{
|
||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.Append("<!DOCTYPE html>");
|
||||
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
|
||||
build.Append("<html class='os-windows txt-base-").Append(TweetDeckBridge.FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
||||
build.Append("<head>").Append(TweetDeckBridge.NotificationHeadContents ?? DefaultHeadContents);
|
||||
|
||||
if (enableCustomCSS){
|
||||
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
||||
|
3
Core/Other/FormAbout.Designer.cs
generated
@@ -131,11 +131,14 @@ namespace TweetDuck.Core.Other {
|
||||
this.Controls.Add(this.labelDescription);
|
||||
this.Controls.Add(this.pictureLogo);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.HelpButton = true;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormAbout";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.FormAbout_HelpButtonClicked);
|
||||
this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.FormAbout_HelpRequested);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit();
|
||||
this.tablePanelLinks.ResumeLayout(false);
|
||||
this.tablePanelLinks.PerformLayout();
|
||||
|
@@ -1,4 +1,6 @@
|
||||
using System.Windows.Forms;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other{
|
||||
@@ -21,5 +23,19 @@ namespace TweetDuck.Core.Other{
|
||||
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string);
|
||||
}
|
||||
|
||||
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
|
||||
ShowGuide();
|
||||
}
|
||||
|
||||
private void FormAbout_HelpButtonClicked(object sender, CancelEventArgs e){
|
||||
e.Cancel = true;
|
||||
ShowGuide();
|
||||
}
|
||||
|
||||
private void ShowGuide(){
|
||||
new FormGuide().Show();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
Core/Other/FormGuide.Designer.cs
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace TweetDuck.Core.Other {
|
||||
partial class FormGuide {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// FormGuide
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34)))));
|
||||
this.ClientSize = new System.Drawing.Size(424, 282);
|
||||
this.Icon = global::TweetDuck.Properties.Resources.icon;
|
||||
this.MinimumSize = new System.Drawing.Size(440, 320);
|
||||
this.Name = "FormGuide";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
71
Core/Other/FormGuide.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Handling;
|
||||
using TweetDuck.Core.Handling.General;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other{
|
||||
sealed partial class FormGuide : Form{
|
||||
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v1/";
|
||||
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
|
||||
public FormGuide(){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Guide";
|
||||
|
||||
FormBrowser owner = FormManager.TryFind<FormBrowser>();
|
||||
|
||||
if (owner != null){
|
||||
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
||||
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||
}
|
||||
|
||||
this.browser = new ChromiumWebBrowser(GuideUrl){
|
||||
MenuHandler = new ContextMenuGuide(),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
LifeSpanHandler = new LifeSpanHandler(),
|
||||
RequestHandler = new RequestHandlerBrowser()
|
||||
};
|
||||
|
||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||
browser.FrameLoadStart += browser_FrameLoadStart;
|
||||
|
||||
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
|
||||
browser.Dock = DockStyle.None;
|
||||
browser.Location = ControlExtensions.InvisibleLocation;
|
||||
Controls.Add(browser);
|
||||
|
||||
Disposed += (sender, args) => {
|
||||
Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged;
|
||||
browser.Dispose();
|
||||
};
|
||||
|
||||
Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
|
||||
}
|
||||
|
||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||
if (!e.IsLoading){
|
||||
this.InvokeAsyncSafe(() => {
|
||||
browser.Location = Point.Empty;
|
||||
browser.Dock = DockStyle.Fill;
|
||||
});
|
||||
|
||||
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||
}
|
||||
|
||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||
}
|
||||
}
|
||||
}
|
@@ -37,6 +37,7 @@ namespace TweetDuck.Core.Other{
|
||||
AddButton("System Tray", () => new TabSettingsTray());
|
||||
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
||||
AddButton("Sounds", () => new TabSettingsSounds());
|
||||
AddButton("Feedback", () => new TabSettingsFeedback());
|
||||
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
|
||||
|
||||
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
||||
|
@@ -147,7 +147,15 @@ namespace TweetDuck.Core.Other.Management{
|
||||
}
|
||||
|
||||
private void process_Exited(object sender, EventArgs e){
|
||||
switch(currentProcess.ExitCode){
|
||||
int exitCode = currentProcess.ExitCode;
|
||||
|
||||
currentProcess.Dispose();
|
||||
currentProcess = null;
|
||||
|
||||
currentPipe.Dispose();
|
||||
currentPipe = null;
|
||||
|
||||
switch(exitCode){
|
||||
case 3: // CODE_LAUNCH_FAIL
|
||||
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
||||
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||
@@ -163,12 +171,6 @@ namespace TweetDuck.Core.Other.Management{
|
||||
break;
|
||||
}
|
||||
|
||||
currentProcess.Dispose();
|
||||
currentProcess = null;
|
||||
|
||||
currentPipe.Dispose();
|
||||
currentPipe = null;
|
||||
|
||||
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
||||
}
|
||||
|
||||
|
134
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
Normal file
@@ -0,0 +1,134 @@
|
||||
namespace TweetDuck.Core.Other.Settings {
|
||||
partial class TabSettingsFeedback {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.panelFeedback = new System.Windows.Forms.Panel();
|
||||
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
|
||||
this.checkDataCollection = new System.Windows.Forms.CheckBox();
|
||||
this.labelDataCollection = new System.Windows.Forms.Label();
|
||||
this.labelFeedback = new System.Windows.Forms.Label();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.btnSendFeedback = new System.Windows.Forms.Button();
|
||||
this.panelFeedback.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelFeedback
|
||||
//
|
||||
this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelFeedback.Controls.Add(this.btnSendFeedback);
|
||||
this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
|
||||
this.panelFeedback.Controls.Add(this.checkDataCollection);
|
||||
this.panelFeedback.Controls.Add(this.labelDataCollection);
|
||||
this.panelFeedback.Location = new System.Drawing.Point(9, 31);
|
||||
this.panelFeedback.Name = "panelFeedback";
|
||||
this.panelFeedback.Size = new System.Drawing.Size(322, 80);
|
||||
this.panelFeedback.TabIndex = 1;
|
||||
//
|
||||
// labelDataCollectionLink
|
||||
//
|
||||
this.labelDataCollectionLink.AutoSize = true;
|
||||
this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10);
|
||||
this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
|
||||
this.labelDataCollectionLink.Location = new System.Drawing.Point(141, 60);
|
||||
this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelDataCollectionLink.Name = "labelDataCollectionLink";
|
||||
this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17);
|
||||
this.labelDataCollectionLink.TabIndex = 3;
|
||||
this.labelDataCollectionLink.TabStop = true;
|
||||
this.labelDataCollectionLink.Text = "(learn more)";
|
||||
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
|
||||
this.labelDataCollectionLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.labelDataCollectionLink_LinkClicked);
|
||||
//
|
||||
// checkDataCollection
|
||||
//
|
||||
this.checkDataCollection.AutoSize = true;
|
||||
this.checkDataCollection.Location = new System.Drawing.Point(6, 59);
|
||||
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 5, 0, 3);
|
||||
this.checkDataCollection.Name = "checkDataCollection";
|
||||
this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
|
||||
this.checkDataCollection.TabIndex = 2;
|
||||
this.checkDataCollection.Text = "Send Anonymous Data";
|
||||
this.checkDataCollection.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// labelDataCollection
|
||||
//
|
||||
this.labelDataCollection.AutoSize = true;
|
||||
this.labelDataCollection.Location = new System.Drawing.Point(3, 41);
|
||||
this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelDataCollection.Name = "labelDataCollection";
|
||||
this.labelDataCollection.Size = new System.Drawing.Size(79, 13);
|
||||
this.labelDataCollection.TabIndex = 1;
|
||||
this.labelDataCollection.Text = "Data Collection";
|
||||
//
|
||||
// labelFeedback
|
||||
//
|
||||
this.labelFeedback.AutoSize = true;
|
||||
this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelFeedback.Location = new System.Drawing.Point(6, 8);
|
||||
this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||
this.labelFeedback.Name = "labelFeedback";
|
||||
this.labelFeedback.Size = new System.Drawing.Size(80, 20);
|
||||
this.labelFeedback.TabIndex = 0;
|
||||
this.labelFeedback.Text = "Feedback";
|
||||
//
|
||||
// btnSendFeedback
|
||||
//
|
||||
this.btnSendFeedback.AutoSize = true;
|
||||
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3);
|
||||
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||
this.btnSendFeedback.Name = "btnSendFeedback";
|
||||
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
|
||||
this.btnSendFeedback.TabIndex = 0;
|
||||
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
|
||||
this.btnSendFeedback.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// TabSettingsFeedback
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.panelFeedback);
|
||||
this.Controls.Add(this.labelFeedback);
|
||||
this.Name = "TabSettingsFeedback";
|
||||
this.Size = new System.Drawing.Size(340, 122);
|
||||
this.panelFeedback.ResumeLayout(false);
|
||||
this.panelFeedback.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Panel panelFeedback;
|
||||
private System.Windows.Forms.CheckBox checkDataCollection;
|
||||
private System.Windows.Forms.Label labelDataCollection;
|
||||
private System.Windows.Forms.Label labelFeedback;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
|
||||
private System.Windows.Forms.Button btnSendFeedback;
|
||||
}
|
||||
}
|
30
Core/Other/Settings/TabSettingsFeedback.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
sealed partial class TabSettingsFeedback : BaseTabSettings{
|
||||
public TabSettingsFeedback(){
|
||||
InitializeComponent();
|
||||
|
||||
checkDataCollection.Checked = Config.AllowDataCollection;
|
||||
}
|
||||
|
||||
public override void OnReady(){
|
||||
btnSendFeedback.Click += btnSendFeedback_Click;
|
||||
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
||||
}
|
||||
|
||||
private void btnSendFeedback_Click(object sender, EventArgs e){
|
||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/issues/new");
|
||||
}
|
||||
|
||||
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
|
||||
Config.AllowDataCollection = checkDataCollection.Checked;
|
||||
}
|
||||
|
||||
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ namespace TweetDuck{
|
||||
public const string BrandName = "TweetDuck";
|
||||
public const string Website = "https://tweetduck.chylex.com";
|
||||
|
||||
public const string VersionTag = "1.9.1";
|
||||
public const string VersionTag = "1.10";
|
||||
|
||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||
|
||||
|
51
README.md
@@ -1,11 +1,56 @@
|
||||
# Build Instructions
|
||||
|
||||
The program was built using Visual Studio 2017. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet.
|
||||
### Setup
|
||||
|
||||
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 Framework 4 – 4.6 development tools
|
||||
* **Desktop development with C++**
|
||||
* VC++ 2017 v141 toolset
|
||||
|
||||
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
|
||||
```
|
||||
PM> Install-Package CefSharp.WinForms -Version 57.0.0
|
||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
||||
```
|
||||
|
||||
When building the `Release` configuration, the folder will only contain files intended for distribution (no debug symbols or other unnecessary files). You may package the files yourself, or use `bld/RUN BUILD.bat` to generate installer files using Inno Setup (make sure the Inno Setup binaries are on your PATH).
|
||||
### Debug
|
||||
|
||||
Built files are available in **bin/x86**, installer files are generated in **bld/Output**. If you decide to release a custom version publicly, please make it clear that it is not the original TweetDuck.
|
||||
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution.
|
||||
|
||||
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:
|
||||
```
|
||||
-datafolder TweetDuckDebug
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
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, open the **Output** view and see which part of the build failed.
|
||||
|
||||
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.
|
||||
|
||||
### 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).
|
||||
|
||||
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!
|
||||
|
||||
After the window closes, three installers will be generated inside the **bld/Output** folder:
|
||||
* **TweetDuck.exe**
|
||||
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
||||
* **TweetDuck.Update.exe**
|
||||
* This is a lightweight update installer that only contains the most important files that usually change across releases
|
||||
* It will automatically download and apply the full installer if the user's current version of CEF does not match (the download link is in `gen_upd.iss` and points to this repository by default)
|
||||
* **TweetDuck.Portable.exe**
|
||||
* 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
|
||||
|
||||
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.
|
||||
|
||||
### Code Notes
|
||||
|
||||
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.
|
||||
|
BIN
Resources/Guide/img/app-menu.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Guide/img/column-clear-header.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Resources/Guide/img/column-clear-preferences.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
Resources/Guide/img/column-preferences.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
Resources/Guide/img/icon.ico
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
Resources/Guide/img/new-tweet-emoji.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Resources/Guide/img/new-tweet-pin.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Resources/Guide/img/new-tweet-template-advanced.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
Resources/Guide/img/new-tweet-template-basic.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
Resources/Guide/img/options-manage-export.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Resources/Guide/img/options-manage-reset.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Resources/Guide/img/options-manage.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Resources/Guide/img/options-notifications-location.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Resources/Guide/img/options-notifications-size.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Guide/img/options-sounds.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Guide/img/settings-dropdown.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
Resources/Guide/img/settings-editdesign.png
Normal file
After Width: | Height: | Size: 23 KiB |
403
Resources/Guide/index.html
Normal file
@@ -0,0 +1,403 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TweetDuck - Guide</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta name="author" content="chylex">
|
||||
<meta name="description" content="TweetDuck guide">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="icon" href="img/icon.ico">
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
|
||||
<script type="text/javascript" src="script.js" async></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="guide">
|
||||
<section>
|
||||
|
||||
<article>
|
||||
<h2>General</h2>
|
||||
|
||||
<details>
|
||||
<summary id="main-menu">How to open the main menu</summary>
|
||||
<div>
|
||||
<p>The main menu gives you access to the following items:</p>
|
||||
<img src="img/app-menu.png" alt="">
|
||||
<p>There are two ways to open the main menu:</p>
|
||||
<ul>
|
||||
<li>Click <em>Settings</em> in the left panel, then select <em>TweetDuck</em></li>
|
||||
<li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li>
|
||||
</ul>
|
||||
<img src="img/settings-dropdown.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="customize-theme">How to customize the theme and layout</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Click <em>Settings</em> in the left panel</li>
|
||||
<li>Continue to <em>Edit layout & design</em></li>
|
||||
<li>Now you can customize many aspects of the website; note that unlike the default TweetDeck settings, the column width and font size can be configured here in much greater detail</li>
|
||||
</ol>
|
||||
<img src="img/settings-editdesign.png" alt="">
|
||||
<p>This is done using an official plugin called <em>Edit layout & design</em>, which is enabled by default. If the plugin is disabled, you can still access the default TweetDeck settings:</p>
|
||||
<ol>
|
||||
<li>Click <em>Settings</em> on the bottom left</li>
|
||||
<li>Continue to <em>Settings</em> (again)</li>
|
||||
<li>Here you can customize the theme, column width, and font size</li>
|
||||
</ol>
|
||||
<img src="img/settings-dropdown.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="emoji">How to add emoji to tweets</summary>
|
||||
<div>
|
||||
<p>When writing a new tweet, click the heart icon to open an emoji picker. If you're writing a reply, click the <em>Popout</em> icon first to bring the reply into the large panel.</p>
|
||||
<p>Then you can immediately type into the <em>Search</em> field, which accepts keywords separated by space. Pressing <em>Enter</em> in the search field (when not empty) will insert the first result into your tweet. Pressing <em>Escape</em> closes the emoji picker.</p>
|
||||
<p>You can also use your mouse to scroll through the emoji, click on the emoji to insert them, and change the skin tone.</p>
|
||||
<p>Emoji are provided by an official plugin called <em>Emoji keyboard</em>, which is enabled by default. The heart icon will not show if the plugin is disabled.</p>
|
||||
<img src="img/new-tweet-emoji.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="templates">How to use tweet templates</summary>
|
||||
<div>
|
||||
<p>To create a simple template to use when writing a new tweet or reply:</p>
|
||||
<ol>
|
||||
<li>Click <em>Manage templates</em> in the New Tweet panel; if you're writing a reply, click the <em>Popout</em> icon first to bring the reply into the large panel</li>
|
||||
<li>Click <em>New template</em> on the bottom right</li>
|
||||
<li>Fill in the template name and contents</li>
|
||||
<li>Click <em>Confirm</em> to create the template</li>
|
||||
</ol>
|
||||
<p>After you create a template, it will be added to the list. There are two icons next to each entry:</p>
|
||||
<ul>
|
||||
<li>Click the <em>pencil icon to edit</em> the template</li>
|
||||
<li>Click the <em>cross icon to delete</em> the template</li>
|
||||
</ul>
|
||||
<p>To use the template, <em>click the template name</em> to replace your current tweet text with the template, or click while holding <em>Shift</em> to append the template to your tweet instead. You can use the Shift+click functionality to quickly chain multiple templates.</p>
|
||||
<img src="img/new-tweet-template-basic.png" alt="">
|
||||
<p>When writing a template, you can use special <em>tokens</em> listed in the <em>Advanced</em> section. Here is an example of one of the tokens:</p>
|
||||
<img src="img/new-tweet-template-advanced.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="upload-from-clipboard">How to upload images from clipboard</summary>
|
||||
<div>
|
||||
<p>When writing a tweet/reply, press <em>Ctrl+V</em> to upload an image from clipboard. You can use this to quickly paste a selection from an image editor such as Paint, or after copying an image in your browser.</p>
|
||||
<p>Make sure you're in the tweet input field before you press Ctrl+V, otherwise the keyboard shortcut won't trigger.</p>
|
||||
<p>Note that this will only work when your clipboard contains the image itself; it will not work if you copy a file or URL.</p>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Columns</h2>
|
||||
|
||||
<details>
|
||||
<summary id="copy-tweet-links">How to copy links to tweets</summary>
|
||||
<div>
|
||||
<p>When you right-click anywhere inside a tweet, you will be given these options:</p>
|
||||
<ul>
|
||||
<li>Open tweet in browser</li>
|
||||
<li><em>Copy tweet address</em></li>
|
||||
<li>Screenshot tweet to clipboard</li>
|
||||
</ul>
|
||||
<p>If the tweet contains a quote, you will also be able to directly open the quote or copy its address.</p>
|
||||
<p>Note that these options will not appear when you right-click a private message.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="download-media">How to download images and videos</summary>
|
||||
<div>
|
||||
<p>When you right-click an image or a video thumbnail with a purple play button, you will be given these options:</p>
|
||||
<ul>
|
||||
<li>Open image/video in browser</li>
|
||||
<li>Copy image/video address</li>
|
||||
<li><em>Save image/video as...</em></li>
|
||||
</ul>
|
||||
<p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p>
|
||||
<p>Whenever possible, the username and quality are included in the filename by default, for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="screenshot-tweets">How to take screenshots of individual tweets</summary>
|
||||
<div>
|
||||
<p>When you right-click anywhere inside a tweet, you will be given these options:</p>
|
||||
<ul>
|
||||
<li>Open tweet in browser</li>
|
||||
<li>Copy tweet address</li>
|
||||
<li><em>Screenshot tweet to clipboard</em></li>
|
||||
</ul>
|
||||
<p>Taking a screenshot may take several seconds, especially if it contains large images or previews. After taking a screenshot, you can paste it into an image editor such as Paint.</p>
|
||||
<p>Note that these options will not appear when you right-click a private message.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="clear-columns">How to clear and restore column contents</summary>
|
||||
<div>
|
||||
<p>TweetDeck lets you clear a column, which can help keep things organized by hiding tweets you have already read. To clear a column, click the <em>slider icon</em> on top of the column, and then click the <em>Clear</em> button.</p>
|
||||
<p>If you need to revert this decision and restore a column, normally you would need to delete and re-create it, but in TweetDuck you can simply hold Shift which turns the Clear button into a <em>Restore</em> button.</p>
|
||||
<img src="img/column-clear-preferences.png" alt="">
|
||||
<p>If you clear columns frequently, you can enable an official plugin that lets you clear columns much quicker:</p>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em>, and select <em>Plugins</em> to open the list of available plugins</li>
|
||||
<li>Find an entry that says <em>Clear columns</em>, it will be somewhere near the bottom as the plugin is disabled by default</li>
|
||||
<li>Click <em>Enable</em> on the right side to enable the plugin and reload the browser</li>
|
||||
</ol>
|
||||
<p>Now you can clear...</p>
|
||||
<ul>
|
||||
<li>...a single column by clicking the <em>droplet icon</em> on top of each column</li>
|
||||
<li>...a single column by holding the <em>1-9 number key</em> and pressing <em>Delete</em></li>
|
||||
<li>...all columns by clicking <em>Clear columns</em> in the left panel</li>
|
||||
<li>...all columns by pressing <em>Alt+Delete</em></li>
|
||||
</ul>
|
||||
<img src="img/column-clear-header.png" alt="">
|
||||
<p>As mentioned before, hold Shift with any of these to restore the columns instead. Note that some keyboard layouts require using the Shift key when pressing number keys; if that is the case for you, please use the numpad or mouse instead.</p>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Notifications</h2>
|
||||
|
||||
<details>
|
||||
<summary id="enable-notifications">How to enable desktop or sound notifications</summary>
|
||||
<div>
|
||||
<p>New columns have disabled notifications by default. To enable them, click the <em>slider icon</em> on top of the column, and expand the <em>Preferences</em> section.</p>
|
||||
<p>Now you can toggle either, or both of the notification options:</p>
|
||||
<img src="img/column-preferences.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="move-resize-notifications">How to move or resize desktop notifications</summary>
|
||||
<div>
|
||||
<p>Open the <em>main menu</em>, select <em>Options</em>, and then click the <em>Notifications</em> tab. Here, you can customize many aspects of desktop notifications.</p>
|
||||
<p>Scroll down to the <em>Location</em> section where you can customize where the notification shows up. You can either pick one of the 4 corners of your screen and the distance from the corner, or select <em>Custom</em> and then you'll be able to freely move the example notification window.</p>
|
||||
<img src="img/options-notifications-location.png" alt="">
|
||||
<p>Scroll down to the <em>Size</em> section to customize the size of the notification window. By default, TweetDuck sets the size based on your font size setting, the zoom level you can customize in the General tab, and your system DPI. If you pick <em>Custom</em>, you will be able to freely resize the example notification window.</p>
|
||||
<img src="img/options-notifications-size.png" alt="">
|
||||
<p>Note that moving and resizing the notification only works while you're inside the Options dialog, that is to prevent accidental clicks messing up your settings.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="sound-notifications">How to customize sound notifications</summary>
|
||||
<div>
|
||||
<p>Open the <em>main menu</em>, select <em>Options</em>, and then click the <em>Sounds</em> tab. Here, you can pick a sound file that will be used instead of the default TweetDeck sound notification.</p>
|
||||
<p>Keep in mind that you're only linking to the sound file, so make sure not to delete the file afterwards, otherwise TweetDuck won't find it anymore.</p>
|
||||
<img src="img/options-sounds.png" alt="">
|
||||
<p>If you're unable to select MP3 files or other common audio file types, please ensure that you have Windows Media Player installed on your system, otherwise you will only be able to select basic WAV files.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="mute-notifications">How to temporarily mute all notifications</summary>
|
||||
<div>
|
||||
<p>There are two ways you can mute/unmute notifications:</p>
|
||||
<ul>
|
||||
<li>Open the <em>main menu</em> and click <em>Mute notifications</em></li>
|
||||
<li>If you've enabled the <em>tray icon</em>, right-click it and then click <em>Mute notifications</em></li>
|
||||
</ul>
|
||||
<p>The option persists across restarts – if you mute notifications and then restart TweetDuck, don't forget to unmute the notifications again.</p>
|
||||
<p>Unmuting notifications will display all missed desktop notifications (unless TweetDuck was restarted in the meantime).</p>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
|
||||
<article>
|
||||
<h2>Options</h2>
|
||||
|
||||
<details>
|
||||
<summary id="configure-tweetduck">How to configure TweetDuck</summary>
|
||||
<div>
|
||||
<p>Open the <em>main menu</em> and select <em>Options</em>. Here you can configure various parts of TweetDuck; the dialog is split into several tabs:</p>
|
||||
<ul>
|
||||
<li><em>General</em> tab for user interface, zoom, and update options</li>
|
||||
<li><em>System Tray</em> tab to enable and configure the tray icon</li>
|
||||
<li><em>Notifications</em> tab to configure desktop notifications</li>
|
||||
<li><em>Sounds</em> tab to set a custom sound notification</li>
|
||||
<li><em>Advanced</em> tab for highly technical options</li>
|
||||
</ul>
|
||||
<p>You can move your cursor over most options to display a <em>tooltip with a detailed explanation</em> of what that option does.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="manage-plugins">How to view and manage plugins</summary>
|
||||
<div>
|
||||
<p>TweetDuck has several offical plugins that extend the website and notifications with new functionality.</p>
|
||||
<p>Open the <em>main menu</em> and select <em>Plugins</em> to open the plugin list. Here you can see what each plugin does, and enable/disable them individually. </p>
|
||||
<p>If you want to install a custom plugin, click <em>Open Plugin Folder</em>. A plugin is a folder that contains a <em>.meta</em> file and several others, make sure you copy and paste the folder itself into the opened plugin folder. To verify that you installed it correctly, click <em>Reload All</em> (which also reloads the website) and the plugin should appear.</p>
|
||||
<p>Please, be careful when installing new plugins, and ensure that you get them from trustworthy sources. If you're unsure about a plugin, feel free to <a href="https://github.com/chylex/TweetDuck/issues/new" rel="nofollow">create an issue</a> and upload the plugin there (<a href="https://github.com" rel="nofollow">GitHub</a> account required).</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="manage-profile">How to backup your profile or move it to another computer</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em>, select <em>Options</em>, and click <em>Manage Options</em> on the bottom left</li>
|
||||
<li>Select <em>Export profile</em> and proceed with <em>Next</em></li>
|
||||
<li>Select items you want to save in your profile (note that <em>Plugin Data</em> includes data from official plugins, such as those that let you customize the website or create tweet templates)</li>
|
||||
<li>Click <em>Export Profile</em></li>
|
||||
</ol>
|
||||
<img src="img/options-manage.png" alt="" style="margin-right:6px">
|
||||
<img src="img/options-manage-export.png" alt="">
|
||||
<p>You can save your profile into a cloud storage (Dropbox, Google Drive, etc.) or an external drive, for example. When you want to restore it, follow the same steps but select <em>Import profile</em> and then select the file instead.</p>
|
||||
<p>When importing a profile, you will be again able to pick which items you want to restore. You can for example export a full profile including your login session, but then only import program options and plugin data if you want to login to a different account.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="restore-options">How to restore default options</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em>, select <em>Options</em>, and click <em>Manage Options</em> on the bottom left</li>
|
||||
<li>Select <em>Restore defaults</em> and proceed with <em>Next</em></li>
|
||||
<li>Select items you want reset (note that <em>Plugin Data</em> includes data from official plugins, such as those that let you customize the website or create tweet templates)</li>
|
||||
<li>Click <em>Restore Defaults</em></li>
|
||||
</ol>
|
||||
<img src="img/options-manage.png" alt="" style="margin-right:6px">
|
||||
<img src="img/options-manage-reset.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Efficiency</h2>
|
||||
|
||||
<details>
|
||||
<summary id="keyboard-shortcuts">How to use keyboard shortcuts</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Click <em>Settings</em> in the left panel</li>
|
||||
<li>Continue to <em>Keyboard shortcuts</em></li>
|
||||
<li>Here you can see most available keyboard shortcuts you can use in the browser window</li>
|
||||
</ol>
|
||||
<img src="img/settings-dropdown.png" alt="">
|
||||
<p>You can also often hold Ctrl or Shift to trigger alternative actions:</p>
|
||||
<ul>
|
||||
<li>When <em>selecting accounts</em>, hold <em>Shift</em> to select multiple accounts (can be configured in the Options)</li>
|
||||
<li>When <em>clearing columns</em>, hold <em>Shift</em> to restore the column instead</li>
|
||||
<li>When <em>clicking video thumbnails</em>, hold <em>Ctrl</em> to open them in your browser</li>
|
||||
</ul>
|
||||
<p>Finally, if you click into a desktop notification window, you can use these keyboard shortcuts:</p>
|
||||
<ul>
|
||||
<li><em>Enter</em> to skip the current notification</li>
|
||||
<li><em>Escape</em> to close the window (skips all notifications in the queue)</li>
|
||||
<li><em>Space</em> to pause/unpause the timer</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="extra-mouse-buttons">How to use the forward / back mouse buttons</summary>
|
||||
<div>
|
||||
<p>If you have a mouse that supports the forward and back buttons, you can use them in both the browser and a desktop notification. All you need to do is move the cursor over the window (even if it's not focused), and press one of the buttons.</p>
|
||||
<p>In the browser:</p>
|
||||
<ul>
|
||||
<li>Press <em>forward</em> over a <em>tweet to open it in detail view</em> (unlike clicking, this will not trigger links or media thumbnails)</li>
|
||||
<li>Press <em>back</em> anywhere to <em>close modal dialogs</em> or the <em>New Tweet panel</em>, or over a <em>column to return back from detail view</em> (if there are no dialogs or panels open, pressing the button outside a column will trigger it for all columns at once)</li>
|
||||
</ul>
|
||||
<p>In the desktop notification:</p>
|
||||
<ul>
|
||||
<li>Press <em>forward</em> to skip the current notification</li>
|
||||
<li>Press <em>back</em> to close the window (skips all notifications in the queue)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="pin-new-tweet">How to keep the New Tweet panel open</summary>
|
||||
<div>
|
||||
<p>Open the New Tweet panel, and click the <em>pin icon</em> on top. When the pin points to the left, the panel will stay open after tweeting or restarting TweetDuck.</p>
|
||||
<img src="img/new-tweet-pin.png" alt="">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="popout-replies">How to instantly popout replies</summary>
|
||||
<div>
|
||||
<p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel.</p>
|
||||
<p>Middle-clicks are usually done by pressing your mouse wheel as if it was a button. When using a laptop touchpad or certain mice, the ways of triggering a middle click vary.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="reply-account">How to change which account will be pre-selected for replies</summary>
|
||||
<div>
|
||||
<p>By default, TweetDeck pre-selects the account mentioned in the column header. The ability to change this is provided by an official plugin which is disabled by default, as it's a bit more difficult to setup, but it can be very powerful. To enable the plugin:</p>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em> and select <em>Plugins</em></li>
|
||||
<li>Find an entry that says <em>Custom reply account</em>, it will be somewhere near the bottom as the plugin is disabled by default</li>
|
||||
<li>Click <em>Enable</em> on the right side to enable the plugin</li>
|
||||
</ol>
|
||||
<p>After you enable the plugin, it will use your preferred account for all replies by default. If that's your intention, you can simply enable the plugin and leave it, otherwise continue reading:</p>
|
||||
<ol>
|
||||
<li>Click <em>Configure</em> next to the plugin to open a folder with the configuration file
|
||||
<li>Open <em>configuration.js</em> in a text editor that can edit and save JavaScript or any pure text files, therefore office suits or WordPad are not suitable; if you don't have any specific editor, use Notepad.</li>
|
||||
<li>The configuration file includes very detailed instructions – you can use one of the <em>presets</em>, a <em>specific account</em> for all replies, or use JavaScript to <em>fully customize</em> the reply behavior</li>
|
||||
</ol>
|
||||
<p>After editing the configuration, return back to Plugins and click <em>Reload All</em> on the bottom left. Now you can close Plugins and test if replies work the way you want.</p>
|
||||
<p>Note that this will not affect the Messages column, that one will always pre-select the account which received the private message.</p>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Advanced</h2>
|
||||
|
||||
<details>
|
||||
<summary id="dev-tools">How to open Chrome Dev Tools</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em>, select <em>Options</em>, and click the <em>Advanced</em> tab</li>
|
||||
<li>Click <em>Open Program Folder</em></li>
|
||||
<li>Download <a href="https://github.com/chylex/TweetDuck/raw/master/bld/Resources/devtools_resources.pak" rel="nofollow">devtools_resources.pak</a> and place it into the opened folder</li>
|
||||
<li>Click <em>Restart the Program</em></li>
|
||||
<li>Now, open the <em>main menu</em> again and you should see <em>Open dev tools</em>; you can also right-click inside a notification and see the same option (make sure to pause the notification first by clicking Freeze in the context menu)</li>
|
||||
</ol>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="custom-css">How to customize styles using CSS</summary>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Open the <em>main menu</em>, select <em>Options</em>, and click the <em>Advanced</em> tab</li>
|
||||
<li>Click <em>Edit CSS</em></li>
|
||||
</ol>
|
||||
<p>Now you can write custom CSS into the <em>Browser</em> and <em>Notification</em> sections.</p>
|
||||
<p>Note that the Browser section will immediately take effect as you type. You can also still access the browser and Dev Tools, as the dialog does not block the browser window.</p>
|
||||
<p>Basic knowledge of HTML and CSS is recommended. Mozilla Development Network has a huge library of resources on both <a href="https://developer.mozilla.org/en-US/Learn/Getting_started_with_the_web/HTML_basics" rel="nofollow">HTML</a> and <a href="https://developer.mozilla.org/en-US/Learn/Getting_started_with_the_web/CSS_basics" rel="nofollow">CSS</a>.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary id="plugin-development">How to develop plugins</summary>
|
||||
<div>
|
||||
<p>Before creating a plugin, you should have at least basic knowledge of <a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web" rel="nofollow">web development</a> (namely HTML, CSS, JavaScript), and several JS libraries TweetDeck uses, such as <a href="https://learn.jquery.com" rel="nofollow">jQuery 2</a>, <a href="https://mustache.github.io" rel="nofollow">Mustache</a>, <a href="https://github.com/ded/klass" rel="nofollow">klass</a>, and <a href="https://flightjs.github.io/" rel="nofollow">Flight</a>.</p>
|
||||
<p>Working with the TweetDeck source code involves a lot of reverse-engineering. You can visit <a href="https://github.com/DeckHack/discoveries" rel="nofollow">DeckHack</a> which is working to document its source code, and view <a href="https://github.com/chylex/TweetDuck/tree/master/Resources/Scripts" rel="nofollow">TweetDuck sources</a> which includes all scripts and official plugins.</p>
|
||||
<p>Once you're ready to start creating your own plugins, visit the official <a href="https://github.com/chylex/TweetDuck/wiki/Plugins%EA%9E%89-1.-The-Basics" rel="nofollow">plugin development documentation</a> which will explain the structure of a plugin, and show you all TweetDuck-specific functionality you cannot normally use in browsers.</p>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
84
Resources/Guide/script.js
Normal file
@@ -0,0 +1,84 @@
|
||||
var init = function(){
|
||||
if (!("open" in document.getElementsByTagName("details")[0])){
|
||||
var elements = document.getElementsByTagName("details");
|
||||
|
||||
var onClick = function(e){
|
||||
var summary = e.target;
|
||||
var parent = e.target.parentElement;
|
||||
var contents = e.target.nextElementSibling;
|
||||
|
||||
if (parent.hasAttribute("open")){
|
||||
parent.removeAttribute("open");
|
||||
summary.setAttribute("aria-expanded", "false");
|
||||
contents.setAttribute("aria-hidden", "true");
|
||||
contents.style.display = "none";
|
||||
}
|
||||
else{
|
||||
parent.setAttribute("open", "");
|
||||
summary.setAttribute("aria-expanded", "true");
|
||||
contents.setAttribute("aria-hidden", "false");
|
||||
contents.style.display = "block";
|
||||
}
|
||||
};
|
||||
|
||||
var onKey = function(e){
|
||||
if (e.keyCode === 13 || e.keyCode === 32){
|
||||
onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
for(var index = 0; index < elements.length; index++){
|
||||
var ele = elements[index];
|
||||
|
||||
if (ele.childElementCount === 2){
|
||||
var summary = ele.children[0];
|
||||
var contents = ele.children[1];
|
||||
|
||||
ele.style.display = "block";
|
||||
ele.setAttribute("role", "group");
|
||||
|
||||
summary.setAttribute("role", "button");
|
||||
summary.setAttribute("aria-expanded", "false");
|
||||
summary.setAttribute("tabindex", 0);
|
||||
summary.addEventListener("click", onClick);
|
||||
summary.addEventListener("keydown", onKey);
|
||||
|
||||
contents.setAttribute("aria-hidden", "true");
|
||||
contents.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("WebkitAppearance" in document.documentElement.style){
|
||||
var elements = document.getElementsByTagName("summary");
|
||||
|
||||
var onMouseDown = function(e){
|
||||
e.target.classList.add("webkit-workaround");
|
||||
};
|
||||
|
||||
var onMouseUp = function(e){
|
||||
e.target.classList.remove("webkit-workaround");
|
||||
e.target.blur();
|
||||
};
|
||||
|
||||
for(var index = 0; index < elements.length; index++){
|
||||
elements[index].addEventListener("mousedown", onMouseDown);
|
||||
elements[index].addEventListener("mouseup", onMouseUp);
|
||||
}
|
||||
}
|
||||
|
||||
if (location.hash.length > 1){
|
||||
var element = document.getElementById(location.hash.substring(1));
|
||||
|
||||
if (element && element.tagName === "SUMMARY"){
|
||||
element.click();
|
||||
element.scrollIntoView(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (document.readyState !== "loading"){
|
||||
init();
|
||||
}
|
||||
else{
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
}
|
132
Resources/Guide/style.css
Normal file
@@ -0,0 +1,132 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
|
||||
background-color: #222;
|
||||
color: #ddd;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#guide {
|
||||
width: 100%;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
#guide section {
|
||||
flex: 1 1 0;
|
||||
margin: 0 24px;
|
||||
min-width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media(max-width: 408px) {
|
||||
#guide section {
|
||||
min-width: calc(100vw - 48px);
|
||||
}
|
||||
}
|
||||
|
||||
#guide h2 {
|
||||
margin: 20px 0 10px;
|
||||
font-size: 32px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#guide details {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#guide details[open] {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
#guide summary {
|
||||
display: inline-block;
|
||||
margin: 0 0 11px;
|
||||
padding: 4px 10px;
|
||||
font-size: 19px;
|
||||
font-weight: bold;
|
||||
color: #afdfff;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
#guide summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#guide summary.webkit-workaround {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#guide summary::before {
|
||||
content: "▶";
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
vertical-align: top;
|
||||
margin-right: 6px;
|
||||
padding-top: 2px;
|
||||
transform-origin: 35% 50%;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
#guide summary:hover, #guide details[open] summary {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#guide details[open] summary::before {
|
||||
transform: rotateZ(90deg) translate(0.5px, -3px);
|
||||
}
|
||||
|
||||
#guide details > div {
|
||||
margin-bottom: 22px;
|
||||
padding: 0 25px 20px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#guide details > div > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#guide details > div > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
em {
|
||||
color: #f8d88b;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #8bc6f8;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
p, li {
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 2px groove rgba(255, 255, 255, 0.6);
|
||||
max-width: 100%;
|
||||
}
|
@@ -8,7 +8,7 @@ Edit layout & design
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.1.2
|
||||
1.1.3
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
@@ -108,7 +108,7 @@ enabled(){
|
||||
let menu = $(".js-dropdown-content").children("ul").first();
|
||||
return if menu.length === 0;
|
||||
|
||||
let itemTD = menu.children("[data-std]").first();
|
||||
let itemTD = menu.children("[data-tweetduck]").first();
|
||||
return if itemTD.length === 0;
|
||||
|
||||
if (!itemTD.prev().hasClass("drp-h-divider")){
|
||||
@@ -116,7 +116,7 @@ enabled(){
|
||||
}
|
||||
|
||||
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout & design</a></li>');
|
||||
itemTD.after(itemEditDesign);
|
||||
itemEditDesign.insertAfter(itemTD);
|
||||
|
||||
itemEditDesign.on("click", "a", this.openEditDesignDialog);
|
||||
|
||||
|
@@ -9,7 +9,7 @@ Emoji keyboard
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.3.1
|
||||
1.4
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
@@ -66,6 +66,7 @@ enabled(){
|
||||
this.currentSpanner = null;
|
||||
|
||||
var wasSearchFocused = false;
|
||||
var lastEmojiKeyword, lastEmojiPosition, lastEmojiLength;
|
||||
|
||||
var hideKeyboard = (refocus) => {
|
||||
$(this.currentKeyboard).remove();
|
||||
@@ -81,8 +82,14 @@ enabled(){
|
||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||
|
||||
if (refocus){
|
||||
$(".js-compose-text", ".js-docked-compose").focus();
|
||||
this.composeInput.focus();
|
||||
|
||||
if (lastEmojiKeyword && lastEmojiPosition === 0){
|
||||
document.execCommand("insertText", false, lastEmojiKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
lastEmojiKeyword = null;
|
||||
};
|
||||
|
||||
var generateEmojiHTML = skinTone => {
|
||||
@@ -229,7 +236,7 @@ enabled(){
|
||||
};
|
||||
|
||||
var insertEmoji = (src, alt) => {
|
||||
let input = $(".js-compose-text", ".js-docked-compose");
|
||||
let input = this.composeInput;
|
||||
|
||||
let val = input.val();
|
||||
let posStart = input[0].selectionStart;
|
||||
@@ -241,6 +248,8 @@ enabled(){
|
||||
input[0].selectionStart = posStart+alt.length;
|
||||
input[0].selectionEnd = posStart+alt.length;
|
||||
|
||||
lastEmojiKeyword = null;
|
||||
|
||||
if (wasSearchFocused){
|
||||
$(".emoji-keyboard-search").children("input").focus();
|
||||
}
|
||||
@@ -270,6 +279,92 @@ enabled(){
|
||||
}
|
||||
};
|
||||
|
||||
this.composeInputKeyDownEvent = function(e){
|
||||
if (lastEmojiKeyword && (e.keyCode === 8 || e.keyCode === 27)){ // backspace, escape
|
||||
let ele = $(this)[0];
|
||||
|
||||
if (ele.selectionStart === lastEmojiPosition){
|
||||
ele.selectionStart -= lastEmojiLength; // selects the emoji
|
||||
document.execCommand("insertText", false, lastEmojiKeyword);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
lastEmojiKeyword = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.composeInputKeyPressEvent = function(e){
|
||||
if (String.fromCharCode(e.which) === ':'){
|
||||
let ele = $(this);
|
||||
let val = ele.val();
|
||||
|
||||
let firstColon = val.lastIndexOf(':', ele[0].selectionStart);
|
||||
return if firstColon === -1;
|
||||
|
||||
let search = val.substring(firstColon+1, ele[0].selectionStart);
|
||||
return if !/^[a-z_]+$/i.test(search);
|
||||
|
||||
let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
||||
return if keywords.length === 0;
|
||||
|
||||
let foundName = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw)));
|
||||
|
||||
if (foundName.length === 0){
|
||||
return;
|
||||
}
|
||||
|
||||
lastEmojiKeyword = `:${search}:`;
|
||||
lastEmojiPosition = lastEmojiLength = 0;
|
||||
|
||||
if (foundName.length === 1){
|
||||
let foundIndex = me.emojiNames.indexOf(foundName[0]);
|
||||
let foundEmoji;
|
||||
|
||||
for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){
|
||||
let realArray = array.filter(ele => ele !== "___");
|
||||
|
||||
if (foundIndex >= realArray.length){
|
||||
foundIndex -= realArray.length;
|
||||
}
|
||||
else{
|
||||
foundEmoji = realArray[foundIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundEmoji){
|
||||
e.preventDefault();
|
||||
|
||||
ele.val(val.substring(0, firstColon)+foundEmoji+val.substring(ele[0].selectionStart));
|
||||
ele[0].selectionEnd = ele[0].selectionStart = firstColon+foundEmoji.length;
|
||||
ele.trigger("change");
|
||||
ele.focus();
|
||||
|
||||
lastEmojiPosition = firstColon+foundEmoji.length;
|
||||
lastEmojiLength = foundEmoji.length;
|
||||
}
|
||||
}
|
||||
else if (foundName.length > 1){
|
||||
e.preventDefault();
|
||||
ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart));
|
||||
ele[0].selectionEnd = ele[0].selectionStart = firstColon;
|
||||
ele.trigger("change");
|
||||
|
||||
if (!me.currentKeyboard){
|
||||
$(".emoji-keyboard-popup-btn").click();
|
||||
}
|
||||
|
||||
$(".emoji-keyboard-search").children("input").focus().val("");
|
||||
document.execCommand("insertText", false, keywords.join(" "));
|
||||
}
|
||||
}
|
||||
else{
|
||||
lastEmojiKeyword = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.composeInputFocusEvent = function(e){
|
||||
wasSearchFocused = false;
|
||||
};
|
||||
@@ -300,15 +395,18 @@ enabled(){
|
||||
|
||||
ready(){
|
||||
this.composeDrawer = $("[data-drawer='compose']");
|
||||
this.composeInput = $(".js-compose-text", ".js-docked-compose");
|
||||
|
||||
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
|
||||
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
||||
|
||||
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||
$(".js-compose-text", ".js-docked-compose").on("focus", this.composeInputFocusEvent);
|
||||
$(document).on("click", this.documentClickEvent);
|
||||
$(document).on("keydown", this.documentKeyEvent);
|
||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||
this.composeInput.on("keydown", this.composeInputKeyDownEvent);
|
||||
this.composeInput.on("keypress", this.composeInputKeyPressEvent);
|
||||
this.composeInput.on("focus", this.composeInputFocusEvent);
|
||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||
|
||||
// HTML generation
|
||||
@@ -429,10 +527,12 @@ disabled(){
|
||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||
$(".emoji-keyboard-popup-btn").remove();
|
||||
|
||||
$(".js-compose-text", ".js-docked-compose").off("focus", this.composeInputFocusEvent);
|
||||
$(document).off("click", this.documentClickEvent);
|
||||
$(document).off("keydown", this.documentKeyEvent);
|
||||
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
||||
this.composeInput.off("keydown", this.composeInputKeyDownEvent);
|
||||
this.composeInput.off("keypress", this.composeInputKeyPressEvent);
|
||||
this.composeInput.off("focus", this.composeInputFocusEvent);
|
||||
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
||||
|
||||
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||
|
@@ -8,7 +8,7 @@ Custom reply account
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.2.1
|
||||
1.2.2
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
@@ -1,12 +1,4 @@
|
||||
{
|
||||
/*
|
||||
* WARNING
|
||||
* -------
|
||||
*
|
||||
* Make sure you are editing 'configuration.js' and not the default configuration file, as the default one will be replaced with each update.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Simple way of configuring the plugin
|
||||
* ------------------------------------
|
||||
|
@@ -11,12 +11,22 @@ function Rewrite-File{
|
||||
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Include *.js -Recurse){
|
||||
ForEach($file in Get-ChildItem -Include *.js -Exclude configuration.default.js -Recurse){
|
||||
$lines = Get-Content -Path $file.FullName
|
||||
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
|
||||
$lines = $lines | % { $_.TrimStart() }
|
||||
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
|
||||
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
|
||||
,$lines | Rewrite-File $file
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Include *.css -Recurse){
|
||||
$lines = Get-Content -Path $file.FullName
|
||||
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
||||
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
|
||||
$lines = $lines -Replace '^(\S.*?) {$', '$1{'
|
||||
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Include *.html -Recurse){
|
||||
$lines = Get-Content -Path $file.FullName
|
||||
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
|
||||
|
@@ -270,7 +270,9 @@
|
||||
tags.push("<style type='text/css'>");
|
||||
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color
|
||||
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls
|
||||
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon
|
||||
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
|
||||
tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
|
||||
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
|
||||
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
|
||||
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
|
||||
tags.push("</style>");
|
||||
@@ -328,9 +330,8 @@
|
||||
let menu = $(".js-dropdown-content").children("ul").first();
|
||||
return if menu.length === 0;
|
||||
|
||||
menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>');
|
||||
|
||||
let button = menu.children("[data-std]");
|
||||
let button = $('<li class="is-selectable" data-tweetduck><a href="#" data-action>TweetDuck</a></li>');
|
||||
button.insertBefore(menu.children(".drp-h-divider").last());
|
||||
|
||||
button.on("click", "a", function(){
|
||||
$TD.openContextMenu();
|
||||
@@ -349,10 +350,6 @@
|
||||
// Block: Expand shortened links on hover or display tooltip.
|
||||
//
|
||||
(function(){
|
||||
var cutStart = function(str, search){
|
||||
return str.startsWith(search) ? str.substr(search.length) : str;
|
||||
};
|
||||
|
||||
var prevMouseX = -1, prevMouseY = -1;
|
||||
var tooltipTimer, tooltipDisplayed;
|
||||
|
||||
@@ -364,13 +361,8 @@
|
||||
|
||||
if ($TDX.expandLinksOnHover){
|
||||
tooltipTimer = window.setTimeout(function(){
|
||||
let expanded = me.attr("data-full-url");
|
||||
expanded = cutStart(expanded, "https://");
|
||||
expanded = cutStart(expanded, "http://");
|
||||
expanded = cutStart(expanded, "www.");
|
||||
|
||||
me.attr("td-prev-text", text);
|
||||
me.text(expanded);
|
||||
me.text(me.attr("data-full-url").replace(/^https?:\/\/(www\.)?/, ""));
|
||||
}, 200);
|
||||
}
|
||||
else{
|
||||
@@ -503,8 +495,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
var processMedia = function(media){
|
||||
return media.filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||
var processMedia = function(chirp){
|
||||
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||
};
|
||||
|
||||
app.delegate("section.js-column", {
|
||||
@@ -531,7 +523,7 @@
|
||||
let tweetUrl = tweet.getChirpURL();
|
||||
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
||||
let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||
let imageList = tweet.quotedTweet ? processMedia(tweet.quotedTweet.getMedia()) : tweet.hasImage() ? processMedia(tweet.getMedia()) : "";
|
||||
let imageList = tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : tweet.hasImage() ? processMedia(tweet) : "";
|
||||
|
||||
updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
|
||||
}
|
||||
@@ -597,6 +589,7 @@
|
||||
|
||||
if (isReply){
|
||||
selectedTweet.find(".is-conversation").removeClass("is-conversation");
|
||||
selectedTweet.find(".thread").remove();
|
||||
}
|
||||
|
||||
selectedTweet.find(".js-poll-link").remove();
|
||||
@@ -833,74 +826,24 @@
|
||||
// Block: Inject custom CSS and layout into the page.
|
||||
//
|
||||
(function(){
|
||||
let styleOfficial = document.createElement("style");
|
||||
document.head.appendChild(styleOfficial);
|
||||
|
||||
let addRule = (rule) => {
|
||||
styleOfficial.sheet.insertRule(rule, 0);
|
||||
var createStyle = function(id, styles){
|
||||
let ele = document.createElement("style");
|
||||
ele.id = id;
|
||||
ele.innerText = styles;
|
||||
document.head.appendChild(ele);
|
||||
};
|
||||
|
||||
addRule("a[data-full-url] { word-break: break-all; }"); // break long urls
|
||||
addRule(".keyboard-shortcut-list { vertical-align: top; }"); // fix keyboard navigation alignment
|
||||
addRule(".account-inline .username { vertical-align: 10%; }"); // move usernames a bit higher
|
||||
addRule(".character-count-compose { width: 40px !important; }"); // fix strangely wide character count element
|
||||
addRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #9f51cf; }"); // change play icon on mp4s
|
||||
|
||||
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
|
||||
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
||||
addRule(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon in notifications
|
||||
|
||||
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, dialogs, menus, media previews
|
||||
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select { border-radius: 0 !important; }"); // square-ify dropdowns, inputs, quoted tweets, and account selectors
|
||||
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
||||
|
||||
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
||||
addRule(".accs-header { padding-left: 0 !important; }"); // fix retweet account selector heading
|
||||
|
||||
addRule(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { border-radius: 0; }"); // square-ify scroll bars
|
||||
addRule(".antiscroll-scrollbar-vertical { margin-top: 0; }"); // square-ify scroll bars
|
||||
addRule(".antiscroll-scrollbar-horizontal { margin-left: 0; }"); // square-ify scroll bars
|
||||
addRule(".scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { width: 8px; }"); // square-ify scroll bars
|
||||
addRule(".scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { height: 8px; }"); // square-ify scroll bars
|
||||
addRule(".app-columns-container::-webkit-scrollbar { height: 9px !important; }"); // square-ify scroll bars
|
||||
|
||||
addRule(".is-condensed .app-header-inner { padding-top: 10px !important; }"); // add extra padding to menu buttons when condensed
|
||||
addRule(".is-condensed .btn-compose { padding: 8px !important; }"); // fix compose button icon when condensed
|
||||
addRule(".app-header:not(.is-condensed) .nav-user-info { padding: 0 5px; }"); // add padding to user info
|
||||
|
||||
addRule(".app-title { display: none; }"); // hide TweetDeck logo
|
||||
addRule(".nav-user-info { bottom: 10px !important; }"); // move user info
|
||||
addRule(".app-navigator { bottom: 50px !important; }"); // move navigation
|
||||
addRule(".column-navigator-overflow { bottom: 192px !important; }"); // move column list
|
||||
addRule(".app-navigator .tooltip { display: none !important; }"); // hide broken tooltips in the menu
|
||||
|
||||
addRule(".column .column-header { height: 49px !important; }"); // fix one pixel space below column header
|
||||
addRule(".column:not(.is-options-open) .column-header { border-bottom: none; }"); // fix one pixel space below column header
|
||||
addRule(".is-options-open .column-type-icon { bottom: 27px; }"); // fix one pixel space below column header
|
||||
|
||||
addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications
|
||||
addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications
|
||||
addRule(".account-bio.padding-t--5 { padding-top: 2px !important; }"); // decrease padding on follow notifications
|
||||
|
||||
addRule("html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { color: #000; border-color: #000; opacity: 0.25; }"); // make follow notification button nicer
|
||||
addRule("html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu { color: #fff; border-color: #fff; opacity: 0.25; }"); // make follow notification button nicer
|
||||
|
||||
addRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }"); // remove weird border in the column container scrollbar
|
||||
addRule(".app-columns-container { bottom: 0 !important; }"); // move column container scrollbar to bottom to fit updated style
|
||||
|
||||
addRule(".js-column-header .column-header-link { padding: 0; }"); // fix column header tooltip hover box
|
||||
addRule(".js-column-header .column-header-link .icon { padding: 9px 4px; width: calc(1em + 8px); height: 100%; box-sizing: border-box; }"); // fix column header tooltip hover box
|
||||
|
||||
addRule("#td-compose-drawer-pin { margin: 17px 4px 0 0; transition: transform 0.1s ease; fill: #fff; float: right; cursor: pointer; }"); // replace 'stay open' checkbox with a pin icon
|
||||
addRule(".js-docked-compose footer { display: none; }"); // replace 'stay open' checkbox with a pin icon
|
||||
addRule(".compose-content { bottom: 0 !important; }"); // replace 'stay open' checkbox with a pin icon
|
||||
addRule(".js-docked-compose .js-drawer-close { margin: 20px 0 0 !important; }"); // fix close drawer button because twitter is fucking incompetent
|
||||
window.TDGF_injectBrowserCSS = function(styles){
|
||||
if (!document.getElementById("tweetduck-browser-css")){
|
||||
createStyle("tweetduck-browser-css", styles);
|
||||
}
|
||||
};
|
||||
|
||||
window.TDGF_reinjectCustomCSS = function(styles){
|
||||
$("#tweetduck-custom-css").remove();
|
||||
|
||||
if (styles && styles.length){
|
||||
$(document.head).append("<style type='text/css' id='tweetduck-custom-css'>"+styles+"</style>");
|
||||
createStyle("tweetduck-custom-css", styles);
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -935,19 +878,38 @@
|
||||
$TD.playVideo(url);
|
||||
};
|
||||
|
||||
app.delegate(".js-gif-play", "click", function(e){
|
||||
var getVideoTweetLink = function(obj){
|
||||
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();
|
||||
return link.attr("href");
|
||||
}
|
||||
|
||||
app.delegate(".js-gif-play", {
|
||||
click: function(e){
|
||||
let src = !e.ctrlKey && $(this).closest(".js-media-gif-container").find("video").attr("src");
|
||||
|
||||
if (src){
|
||||
playVideo(src);
|
||||
}
|
||||
else{
|
||||
let parent = $(e.target).closest(".js-tweet").first();
|
||||
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
|
||||
$TD.openBrowser(link.attr("href"));
|
||||
$TD.openBrowser(getVideoTweetLink($(this)));
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
},
|
||||
|
||||
mousedown: function(e){
|
||||
if (e.button === 1){
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
mouseup: function(e){
|
||||
if (e.button === 1){
|
||||
$TD.openBrowser(getVideoTweetLink($(this)));
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused");
|
||||
@@ -1120,31 +1082,34 @@
|
||||
});
|
||||
};
|
||||
|
||||
(function(){
|
||||
var lastActivity = Date.now();
|
||||
|
||||
$(document).click(function(e){
|
||||
lastActivity = Date.now();
|
||||
});
|
||||
|
||||
window.TDGF_tryRunCleanup = function(){
|
||||
// no recent activity
|
||||
return false if Date.now()-lastActivity < 15e3;
|
||||
|
||||
// no modals are visible
|
||||
return false if $("#open-modal").is(":visible") || !$(".js-modals-container").is(":empty");
|
||||
return false if $(".js-modal").is(":visible") || !$(".js-modals-container").is(":empty");
|
||||
|
||||
// all columns are in a default state
|
||||
return false if $("section.js-column").is(".is-shifted-1,.is-shifted-2");
|
||||
|
||||
// all textareas are empty
|
||||
if ($("textarea").is(function(){
|
||||
return $(this).val().length > 0;
|
||||
})){
|
||||
return false;
|
||||
}
|
||||
return false if Array.prototype.some.call(document.getElementsByTagName("textarea"), ele => ele.value.length > 0);
|
||||
|
||||
// all columns are scrolled to top
|
||||
if ($(".js-column-scroller").is(function(){
|
||||
return $(this).scrollTop() > 0;
|
||||
})){
|
||||
return false;
|
||||
}
|
||||
return false if Array.prototype.some.call(document.getElementsByClassName("js-column-scroller"), ele => ele.scrollTop > 0);
|
||||
|
||||
// cleanup
|
||||
window.TDGF_reload();
|
||||
return true;
|
||||
};
|
||||
})();
|
||||
|
||||
if (window.TD_SESSION && window.TD_SESSION.gc){
|
||||
var state;
|
||||
|
97
Resources/Scripts/introduction.js
Normal file
@@ -0,0 +1,97 @@
|
||||
(function($, $TD){
|
||||
$(document).one("TD.ready", function(){
|
||||
let css = $(`
|
||||
<style>
|
||||
#td-introduction-modal {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl {
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
height: 372px;
|
||||
}
|
||||
|
||||
#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 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><strong>Right-click anywhere</strong> or click <strong>Settings – TweetDuck</strong> in the left panel to open the main menu, where you can access the <strong>Options</strong>, <strong>Plugins</strong>, and more.</p>
|
||||
<p>You can also right-click links, media, tweets, notifications, etc. to access their context menu.</p>
|
||||
<p>If you're using TweetDuck for the first time, check out the <strong>guide</strong> that answers common questions and showcases important features. You can open the main menu, select <strong>About TweetDuck</strong>, and click the help button to view the guide later.</p>
|
||||
<p>Before you continue, please consider helping development by allowing TweetDuck to send anonymous data in the future. You can always disable it in <strong>Options – Feedback</strong>.</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> (<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");
|
||||
|
||||
ele.find("button, a.mdl-dismiss").click(function(){
|
||||
let showGuide = $(this)[0].hasAttribute("data-guide");
|
||||
let allowDataCollection = $("#td-anonymous-data").is(":checked");
|
||||
|
||||
ele.fadeOut(200, function(){
|
||||
$TD.onIntroductionClosed(showGuide, allowDataCollection);
|
||||
ele.remove();
|
||||
css.remove();
|
||||
});
|
||||
});
|
||||
});
|
||||
})($, $TD);
|
@@ -42,10 +42,6 @@
|
||||
// Block: Expand shortened links on hover or display tooltip.
|
||||
//
|
||||
(function(){
|
||||
var cutStart = function(str, search){
|
||||
return str.startsWith(search) ? str.substr(search.length) : str;
|
||||
};
|
||||
|
||||
var prevMouseX = -1, prevMouseY = -1;
|
||||
var tooltipTimer, tooltipDisplayed;
|
||||
|
||||
@@ -60,13 +56,8 @@
|
||||
|
||||
if ($TDX.expandLinksOnHover){
|
||||
tooltipTimer = window.setTimeout(function(){
|
||||
var expanded = url;
|
||||
expanded = cutStart(expanded, "https://");
|
||||
expanded = cutStart(expanded, "http://");
|
||||
expanded = cutStart(expanded, "www.");
|
||||
|
||||
me.setAttribute("td-prev-text", text);
|
||||
me.innerHTML = expanded;
|
||||
me.innerHTML = url.replace(/^https?:\/\/(www\.)?/, "");
|
||||
}, 200);
|
||||
}
|
||||
else{
|
||||
@@ -112,11 +103,11 @@
|
||||
// Block: Setup a skip button.
|
||||
//
|
||||
if (!document.body.hasAttribute("td-example-notification")){
|
||||
document.body.insertAdjacentHTML("afterbegin", [
|
||||
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">',
|
||||
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">',
|
||||
'</svg>'
|
||||
].join(""));
|
||||
document.body.insertAdjacentHTML("afterbegin", `
|
||||
<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">
|
||||
</svg>
|
||||
`);
|
||||
|
||||
document.getElementById("td-skip").addEventListener("click", function(){
|
||||
$TD.loadNextNotification();
|
||||
|
239
Resources/Scripts/styles/browser.css
Normal file
@@ -0,0 +1,239 @@
|
||||
/***********************/
|
||||
/* Redesign scrollbars */
|
||||
/***********************/
|
||||
|
||||
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.antiscroll-scrollbar-vertical {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.antiscroll-scrollbar-horizontal {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.app-columns-container::-webkit-scrollbar {
|
||||
height: 9px !important;
|
||||
}
|
||||
|
||||
.app-columns-container::-webkit-scrollbar-track {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.app-columns-container {
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
/********************/
|
||||
/* Square-ify stuff */
|
||||
/********************/
|
||||
|
||||
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner {
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
.compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select, .prf-header, .accs li, .accs img {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* Hide TweetDeck logo */
|
||||
/***********************/
|
||||
|
||||
.app-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-user-info {
|
||||
bottom: 10px !important;
|
||||
}
|
||||
|
||||
.app-navigator {
|
||||
bottom: 50px !important;
|
||||
}
|
||||
|
||||
.column-navigator-overflow {
|
||||
bottom: 192px !important;
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* Tweak collapsed left panel layout */
|
||||
/*************************************/
|
||||
|
||||
.is-condensed .app-header-inner {
|
||||
padding-top: 10px !important;
|
||||
}
|
||||
|
||||
.is-condensed .btn-compose {
|
||||
padding: 8px !important;
|
||||
}
|
||||
|
||||
.app-header:not(.is-condensed) .nav-user-info {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/* Tweak notification layout and design */
|
||||
/****************************************/
|
||||
|
||||
.activity-header {
|
||||
align-items: center !important;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.activity-header .tweet-timestamp {
|
||||
line-height: unset;
|
||||
}
|
||||
|
||||
.account-bio.padding-t--5 {
|
||||
/* follow notification padding */
|
||||
padding-top: 2px !important;
|
||||
}
|
||||
|
||||
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
|
||||
color: #000;
|
||||
border-color: #000;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* Tweaks for features */
|
||||
/***********************/
|
||||
|
||||
a[data-full-url] {
|
||||
/* break long urls */
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.character-count-compose {
|
||||
/* fix strangely wide character count element */
|
||||
width: 40px !important;
|
||||
}
|
||||
|
||||
.is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot {
|
||||
/* change play icon on mp4s */
|
||||
color: #9f51cf;
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
/* Replace 'Stay open' with a pin icon */
|
||||
/***************************************/
|
||||
|
||||
#td-compose-drawer-pin {
|
||||
margin: 17px 4px 0 0;
|
||||
float: right;
|
||||
fill: #fff;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.js-docked-compose footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.compose-content {
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
/* Fix general visual issues or annoyances */
|
||||
/*******************************************/
|
||||
|
||||
.account-inline .username {
|
||||
/* move usernames a bit higher */
|
||||
vertical-align: 10%;
|
||||
}
|
||||
|
||||
.txt-base-smallest .sprite-verified-mini {
|
||||
/* fix cut off badge when zoomed in */
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
background-position: -223px -99px !important;
|
||||
}
|
||||
|
||||
.txt-base-smallest .badge-verified:before {
|
||||
/* fix cut off badge in notifications */
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
background-position: -223px -98px !important;
|
||||
}
|
||||
|
||||
.accs-header {
|
||||
/* fix retweet account selector heading */
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.keyboard-shortcut-list {
|
||||
/* fix keyboard navigation alignment */
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.tweet-detail-wrapper .js-media-gif-container {
|
||||
/* GIFs in detail view don't trigger the pointer cursor */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* Fix glaring visual issues because twitter is fucking incompetent */
|
||||
/********************************************************************/
|
||||
|
||||
.column-nav-link .attribution {
|
||||
/* fix cut off account names */
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.js-docked-compose .js-drawer-close {
|
||||
/* fix close drawer button position */
|
||||
margin: 20px 0 0 !important;
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
/* Fix tooltips in navigation and column header */
|
||||
/************************************************/
|
||||
|
||||
.app-navigator .tooltip {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.js-column-header .column-header-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.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 */
|
||||
/*******************************************/
|
||||
|
||||
.column .column-header {
|
||||
height: 49px !important;
|
||||
}
|
||||
|
||||
.column:not(.is-options-open) .column-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.is-options-open .column-type-icon {
|
||||
bottom: 27px;
|
||||
}
|
57
Resources/Scripts/styles/notification.css
Normal file
@@ -0,0 +1,57 @@
|
||||
/* General */
|
||||
|
||||
body:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scroll-styled-v::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.scroll-styled-v::-webkit-scrollbar-thumb {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.scroll-styled-v::-webkit-scrollbar-track {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
/* Media */
|
||||
|
||||
.media-size-medium {
|
||||
max-height: 240px;
|
||||
height: calc(100vh - 16px) !important;
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
.js-quote-detail .media-size-medium {
|
||||
height: calc(100vh - 28px) !important;
|
||||
}
|
||||
|
||||
.js-media.margin-vm, .js-media-preview-container.margin-vm {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* Skip button */
|
||||
|
||||
#td-skip {
|
||||
position: fixed;
|
||||
left: 30px;
|
||||
bottom: 10px;
|
||||
z-index: 1000;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.td-hover #td-skip {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
#td-skip:hover {
|
||||
opacity: 1;
|
||||
}
|
@@ -88,6 +88,7 @@
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Core\FormManager.cs" />
|
||||
<Compile Include="Core\Handling\ContextMenuGuide.cs" />
|
||||
<Compile Include="Core\Handling\DragHandlerBrowser.cs" />
|
||||
<Compile Include="Core\Handling\General\BrowserProcessHandler.cs" />
|
||||
<Compile Include="Core\Handling\ContextMenuBase.cs" />
|
||||
@@ -133,6 +134,12 @@
|
||||
<Compile Include="Core\Other\FormAbout.Designer.cs">
|
||||
<DependentUpon>FormAbout.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\FormGuide.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\FormGuide.Designer.cs">
|
||||
<DependentUpon>FormGuide.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\FormMessage.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@@ -171,6 +178,12 @@
|
||||
<DependentUpon>DialogSettingsRestart.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\Management\BrowserProcesses.cs" />
|
||||
<Compile Include="Core\Other\Settings\TabSettingsFeedback.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\Settings\TabSettingsFeedback.Designer.cs">
|
||||
<DependentUpon>TabSettingsFeedback.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\Settings\TabSettingsTray.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
@@ -333,12 +346,15 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Scripts\code.js" />
|
||||
<Content Include="Resources\Scripts\introduction.js" />
|
||||
<Content Include="Resources\Scripts\notification.js" />
|
||||
<Content Include="Resources\Scripts\pages\error.html" />
|
||||
<Content Include="Resources\Scripts\pages\example.html" />
|
||||
<Content Include="Resources\Scripts\plugins.browser.js" />
|
||||
<Content Include="Resources\Scripts\plugins.js" />
|
||||
<Content Include="Resources\Scripts\plugins.notification.js" />
|
||||
<Content Include="Resources\Scripts\styles\browser.css" />
|
||||
<Content Include="Resources\Scripts\styles\notification.css" />
|
||||
<Content Include="Resources\Scripts\twitter.js" />
|
||||
<Content Include="Resources\Scripts\update.js" />
|
||||
</ItemGroup>
|
||||
|
@@ -5,7 +5,7 @@ using System.Windows.Forms;
|
||||
|
||||
namespace TweetDuck.Video{
|
||||
static class Program{
|
||||
internal const string Version = "1.2.0.0";
|
||||
internal const string Version = "1.2.1.0";
|
||||
|
||||
// referenced in VideoPlayer
|
||||
// set by task manager -- public const int CODE_PROCESS_KILLED = 1;
|
||||
|