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

Compare commits

..

56 Commits
1.7.6 ... 1.8

Author SHA1 Message Date
fb13695ca5 Release 1.8 2017-06-16 19:56:57 +02:00
20c76d06f7 Fix templates not triggering textarea change 2017-06-16 19:43:48 +02:00
339a11f649 Remove empty line in unit test code 2017-06-16 19:43:25 +02:00
0989400d87 Fix column header icon position with old icons in edit-design plugin 2017-06-16 18:32:19 +02:00
52aacf602d Tweak formatting of edit-design plugin 2017-06-16 18:30:36 +02:00
54d70a6a17 Fix edit-design plugin to revert icons in notifications too 2017-06-16 08:03:22 +02:00
d980e09e0f Fix {ajax} token resolve order in template plugin 2017-06-16 04:22:44 +02:00
2e4cb12817 Add template name check and safeguard empty ajax token in template plugin 2017-06-16 03:50:16 +02:00
7b91cb2e96 Fix escaping and update modal text in template plugin 2017-06-16 03:34:30 +02:00
95c04a8abc Work on template plugin (keep button active, tweak code) 2017-06-16 02:04:44 +02:00
25822fefdb Add option to revert icons to edit-design plugin 2017-06-16 00:40:23 +02:00
d800ee2d28 Fix template plugin to handle combining {ajax} and {cursor} together 2017-06-11 01:05:15 +02:00
2a51371aca Add {ajax} token to template plugin 2017-06-11 00:25:42 +02:00
ee5d1a47dc Add {cursor} token to template plugin 2017-06-10 23:26:04 +02:00
b330b74347 Implement token reading in template plugin 2017-06-10 23:25:55 +02:00
11fa13f0bb Tweak google analytics detection to work on twitter.com 2017-06-08 18:43:46 +02:00
21400d72b3 Block TweetDeck's google analytics script 2017-06-08 18:39:40 +02:00
a710cb9d4f Make middle click on tweet reply icon open the compose drawer 2017-06-08 14:17:46 +02:00
3326ad52ce Work on template plugin (basic template impl, modal tweaks) 2017-06-07 19:25:38 +02:00
c9560df851 Implement template management, buttons, and persistency in template plugin 2017-06-07 17:22:31 +02:00
74cb45118e Force tweet actions to stay visible when replying in edit-design plugin 2017-06-07 14:41:39 +02:00
c79bf19e51 Add a section with advanced tips to template plugin and update layout 2017-06-07 00:34:27 +02:00
961bec0a2f Add basic layout and styles for the template plugin 2017-06-06 23:23:06 +02:00
89e4977cd1 Bump version of emoji keyboard plugin 2017-06-06 11:11:57 +02:00
bfe16475db Tweak update installer to explicitly list needed files instead of excluding unneeded ones 2017-06-06 04:41:17 +02:00
915d36867c Fix emoji keyboard position and leaking outside the window 2017-06-06 03:27:30 +02:00
48435af407 Add emoji search 2017-06-05 23:07:31 +02:00
86b6ec5212 Tweak emoji names to only use one array when loading file 2017-06-05 21:56:43 +02:00
775e70bc45 Rewrite HTML generation in emoji-keyboard & add emoji names to elements 2017-06-05 21:54:39 +02:00
9f565447d0 Remove emoji characters from emoji-ordering.txt 2017-06-05 19:35:41 +02:00
88d27bc29d Add instructions for updating the emoji order file in emoji-keyboard plugin 2017-06-05 19:32:04 +02:00
172ae87ac6 Merge pull request #129 from chylex/remove_legacy
Remove legacy code (plugins and installers)
2017-06-05 17:49:19 +02:00
91d572235e Make $id and $token properties in plugin objects unmodifiable 2017-06-05 17:28:45 +02:00
64d32dcb75 Delete plugin properties when disabling them 2017-06-05 17:28:01 +02:00
564b4283b6 Rewrite plugin reloading when enabled/disabled and refactor core plugin scripts 2017-06-05 14:49:34 +02:00
ca4d374a81 Fix errors in edit-design plugin when disabling/enabling 2017-06-05 14:43:14 +02:00
a753806d7b Fix 'Edit CEF arguments' restart prompt and tweak dialog text 2017-06-03 18:03:34 +02:00
bd1692cea3 Rename Settings to Options 2017-06-03 17:24:43 +02:00
b7ce089f08 Fix audio playback error leading to the wrong tab in Settings 2017-06-03 17:03:33 +02:00
8a6b47c5db Fix naming and tooltips in profile export/import UI 2017-06-03 16:37:23 +02:00
9f1fc4df18 Decrease compression level in update installer 2017-06-03 16:03:31 +02:00
c018a2a7bc Move sound notification handling to a separate library 2017-06-03 15:32:18 +02:00
a1aebab114 Update installers (remove outdated code, exclude .txt files in updater) 2017-06-03 15:30:10 +02:00
e30702e1d8 Move CefSharp license to bld/Resources/ 2017-06-03 13:01:39 +02:00
008ff4b055 Fix edit-design plugin resetting TweetDeck settings on first run 2017-05-31 18:33:21 +02:00
d7bba22e19 Remove legacy configuration for list of disabled plugins 2017-05-31 17:57:55 +02:00
2b9a910533 Remove legacy installation data from installers 2017-05-31 17:17:47 +02:00
118ebcc627 Fix update installer removing devtools_resources.pak 2017-05-28 18:19:37 +02:00
c741767b11 Release 1.7.7 2017-05-28 18:12:34 +02:00
4a09358e14 Make browser process LARGEADDRESSAWARE to fix crash with high memory usage 2017-05-28 03:33:09 +02:00
3f4ea1af08 Remove old plugin config migration code 2017-05-27 13:30:57 +02:00
35bb196832 Refactor file and folder paths in Program 2017-05-27 13:26:02 +02:00
cb5b50dd42 Fix one pixel line between column header and content 2017-05-26 16:34:42 +02:00
8652272526 Fix column header icons not showing tooltips with mouse near their edge 2017-05-26 16:14:02 +02:00
0f32504fde Add tooltips to clear-columns plugin 2017-05-26 15:50:07 +02:00
4735c21fc0 Rename "Reset" to "Restore" in clear-columns plugin and code.js 2017-05-26 15:17:12 +02:00
50 changed files with 2814 additions and 1938 deletions

View File

@@ -46,7 +46,6 @@ namespace TweetDuck.Configuration{
public bool EnableUpdateCheck { get; set; } public bool EnableUpdateCheck { get; set; }
public string DismissedUpdate { get; set; } public string DismissedUpdate { get; set; }
[Obsolete] public PluginConfig Plugins { get; set; } // TODO remove eventually
public WindowState PluginsWindow { get; set; } public WindowState PluginsWindow { get; set; }
public string CustomCefArgs { get; set; } public string CustomCefArgs { get; set; }
@@ -237,7 +236,7 @@ namespace TweetDuck.Configuration{
Program.Reporter.Log(e.ToString()); Program.Reporter.Log(e.ToString());
} }
else if (firstException != null){ else if (firstException != null){
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, you may lose your settings and list of enabled plugins.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
} }
} }
} }

View File

@@ -11,7 +11,6 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot; using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Notification.Sound;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
@@ -20,6 +19,7 @@ using TweetDuck.Plugins.Events;
using TweetDuck.Resources; using TweetDuck.Resources;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetDuck.Updates.Events; using TweetDuck.Updates.Events;
using TweetLib.Audio.Utils;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed partial class FormBrowser : Form{ sealed partial class FormBrowser : Form{
@@ -38,7 +38,7 @@ namespace TweetDuck.Core{
private FormWindowState prevState; private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager; private TweetScreenshotManager notificationScreenshotManager;
private ISoundNotificationPlayer soundNotification; private SoundNotification soundNotification;
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){ public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
InitializeComponent(); InitializeComponent();
@@ -312,12 +312,12 @@ namespace TweetDuck.Core{
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+e.Message, MessageBoxIcon.Error)){ using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+e.Message, MessageBoxIcon.Error)){
form.CancelButton = form.AddButton("Ignore"); form.CancelButton = form.AddButton("Ignore");
Button btnOpenSettings = form.AddButton("Open Settings"); Button btnOpenSettings = form.AddButton("View Options");
btnOpenSettings.Width += 16; btnOpenSettings.Width += 16;
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y); btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){ if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
OpenSettings(FormSettings.TabIndexNotification); OpenSettings(FormSettings.TabIndexSounds);
} }
} }
} }
@@ -427,7 +427,7 @@ namespace TweetDuck.Core{
} }
if (soundNotification == null){ if (soundNotification == null){
soundNotification = SoundNotification.New(); soundNotification = new SoundNotification();
soundNotification.PlaybackError += soundNotification_PlaybackError; soundNotification.PlaybackError += soundNotification_PlaybackError;
} }

View File

@@ -20,7 +20,7 @@ namespace TweetDuck.Core.Handling{
private const string TitleReloadBrowser = "Reload browser"; private const string TitleReloadBrowser = "Reload browser";
private const string TitleMuteNotifications = "Mute notifications"; private const string TitleMuteNotifications = "Mute notifications";
private const string TitleSettings = "Settings"; private const string TitleSettings = "Options";
private const string TitlePlugins = "Plugins"; private const string TitlePlugins = "Plugins";
private const string TitleAboutProgram = "About "+Program.BrandName; private const string TitleAboutProgram = "About "+Program.BrandName;

View File

@@ -5,5 +5,13 @@ namespace TweetDuck.Core.Handling{
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
browser.Reload(); browser.Reload();
} }
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
return CefReturnValue.Cancel;
}
return CefReturnValue.Continue;
}
} }
} }

View File

@@ -1,12 +0,0 @@
using System;
namespace TweetDuck.Core.Notification.Sound{
interface ISoundNotificationPlayer : IDisposable{
string SupportedFormats { get; }
event EventHandler<PlaybackErrorEventArgs> PlaybackError;
void Play(string file);
void Stop();
}
}

View File

@@ -1,28 +1,29 @@
using System.Runtime.InteropServices; using System;
using TweetDuck.Core.Notification.Sound; using TweetLib.Audio;
using TweetLib.Audio.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
static class SoundNotification{ sealed class SoundNotification : IDisposable{
private static bool? IsWMPAvailable; public string SupportedFormats => player.SupportedFormats;
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
public static ISoundNotificationPlayer New(){ private readonly AudioPlayer player;
if (IsWMPAvailable.HasValue){
if (IsWMPAvailable.Value){ public SoundNotification(){
return new SoundPlayerImplWMP(); this.player = AudioPlayer.New();
} this.player.PlaybackError += Player_PlaybackError;
else{
return new SoundPlayerImplFallback();
}
} }
try{ public void Play(string file){
SoundPlayerImplWMP implWMP = new SoundPlayerImplWMP(); player.Play(file);
IsWMPAvailable = true; }
return implWMP;
}catch(COMException){ private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
IsWMPAvailable = false; PlaybackError?.Invoke(this, e);
return new SoundPlayerImplFallback(); }
}
public void Dispose(){
player.Dispose();
} }
} }
} }

View File

@@ -8,7 +8,7 @@ using TweetDuck.Updates;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormSettings : Form{ sealed partial class FormSettings : Form{
public const int TabIndexNotification = 1; public const int TabIndexSounds = 2;
private readonly FormBrowser browser; private readonly FormBrowser browser;
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4); private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Other{
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, int startTabIndex = 0){ public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, int startTabIndex = 0){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings"; Text = Program.BrandName+" Options";
this.browser = browser; this.browser = browser;
this.browser.PauseNotification(); this.browser.PauseNotification();

View File

@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Other.Settings{
public virtual void OnClosing(){} public virtual void OnClosing(){}
protected static void PromptRestart(){ protected static void PromptRestart(){
if (MessageBox.Show("The application must restart for the setting to take place. Do you want to restart now?", Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){ if (MessageBox.Show("The application must restart for the option to take place. Do you want to restart now?", Program.BrandName+" Options", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){
Program.Restart(); Program.Restart();
} }
} }

View File

@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){ public DialogSettingsCSS(Action<string> reinjectBrowserCSS){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings - CSS"; Text = Program.BrandName+" Options - CSS";
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;

View File

@@ -10,7 +10,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCefArgs(){ public DialogSettingsCefArgs(){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Settings - CEF Arguments"; Text = Program.BrandName+" Options - CEF Arguments";
textBoxArgs.EnableMultilineShortcuts(); textBoxArgs.EnableMultilineShortcuts();
textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? ""; textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? "";
@@ -31,7 +31,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count; int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count;
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All arguments will be removed from the settings. Continue?" : count+(count == 1 ? " argument" : " arguments")+" will be added to the settings. Continue?"; string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){ if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;

View File

@@ -66,9 +66,8 @@
this.cbConfig.Name = "cbConfig"; this.cbConfig.Name = "cbConfig";
this.cbConfig.Size = new System.Drawing.Size(106, 17); this.cbConfig.Size = new System.Drawing.Size(106, 17);
this.cbConfig.TabIndex = 0; this.cbConfig.TabIndex = 0;
this.cbConfig.Text = "Program Settings"; this.cbConfig.Text = "Program Options";
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update settings.\r\nIncludes a list of disabled plugin" + this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update options.");
"s.");
this.cbConfig.UseVisualStyleBackColor = true; this.cbConfig.UseVisualStyleBackColor = true;
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged); this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged);
// //

View File

@@ -79,8 +79,8 @@ namespace TweetDuck.Core.Other.Settings{
if (form.DialogResult == DialogResult.OK){ if (form.DialogResult == DialogResult.OK){
Config.CustomCefArgs = form.CefArgs; Config.CustomCefArgs = form.CefArgs;
form.Dispose();
PromptRestart(); PromptRestart();
form.Dispose();
} }
else form.Dispose(); else form.Dispose();
}; };
@@ -131,8 +131,8 @@ namespace TweetDuck.Core.Other.Settings{
OverwritePrompt = true, OverwritePrompt = true,
DefaultExt = "tdsettings", DefaultExt = "tdsettings",
FileName = Program.BrandName+".tdsettings", FileName = Program.BrandName+".tdsettings",
Title = "Export "+Program.BrandName+" Settings", Title = "Export "+Program.BrandName+" Profile",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings" Filter = Program.BrandName+" Profile (*.tdsettings)|*.tdsettings"
}){ }){
if (dialog.ShowDialog() != DialogResult.OK){ if (dialog.ShowDialog() != DialogResult.OK){
return; return;
@@ -146,7 +146,7 @@ namespace TweetDuck.Core.Other.Settings{
ExportManager manager = new ExportManager(file, plugins); ExportManager manager = new ExportManager(file, plugins);
if (!manager.Export(flags)){ if (!manager.Export(flags)){
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" settings.", true, manager.LastException); Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" profile.", true, manager.LastException);
} }
} }
@@ -156,8 +156,8 @@ namespace TweetDuck.Core.Other.Settings{
using(OpenFileDialog dialog = new OpenFileDialog{ using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
DereferenceLinks = true, DereferenceLinks = true,
Title = "Import "+Program.BrandName+" Settings", Title = "Import "+Program.BrandName+" Profile",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings" Filter = Program.BrandName+" Profile (*.tdsettings)|*.tdsettings"
}){ }){
if (dialog.ShowDialog() != DialogResult.OK){ if (dialog.ShowDialog() != DialogResult.OK){
return; return;
@@ -183,12 +183,12 @@ namespace TweetDuck.Core.Other.Settings{
} }
} }
else{ else{
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" settings.", true, manager.LastException); Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" profile.", true, manager.LastException);
} }
} }
private void btnReset_Click(object sender, EventArgs e){ private void btnReset_Click(object sender, EventArgs e){
if (MessageBox.Show("This will reset all of your program settings. Plugins will not be affected. Do you want to proceed?", "Reset "+Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){ if (MessageBox.Show("This will reset all of your program options. Plugins will not be affected. Do you want to proceed?", "Reset "+Program.BrandName+" Options", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
Program.ResetConfig(); Program.ResetConfig();
((FormSettings)ParentForm).ReloadUI(); ((FormSettings)ParentForm).ReloadUI();
} }

View File

@@ -3,16 +3,16 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Sound; using TweetLib.Audio.Utils;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsSounds : BaseTabSettings{ partial class TabSettingsSounds : BaseTabSettings{
private readonly ISoundNotificationPlayer soundNotification; private readonly SoundNotification soundNotification;
public TabSettingsSounds(){ public TabSettingsSounds(){
InitializeComponent(); InitializeComponent();
soundNotification = SoundNotification.New(); soundNotification = new SoundNotification();
soundNotification.PlaybackError += sound_PlaybackError; soundNotification.PlaybackError += sound_PlaybackError;
tbCustomSound.Text = Config.NotificationSoundPath; tbCustomSound.Text = Config.NotificationSoundPath;

View File

@@ -73,22 +73,6 @@ namespace TweetDuck.Plugins{
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){ if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
string dataFolder = GetPluginFolder(PluginFolder.Data); string dataFolder = GetPluginFolder(PluginFolder.Data);
if (!Directory.Exists(dataFolder)){ // config migration
string originalFile = Path.Combine(GetPluginFolder(PluginFolder.Root), ConfigFile);
if (File.Exists(originalFile)){
try{
Directory.CreateDirectory(dataFolder);
File.Copy(originalFile, configPath, false);
File.Delete(originalFile); // will fail without write perms in program folder, ignore if so
}catch{
// ignore
}
return;
}
}
try{ try{
Directory.CreateDirectory(dataFolder); Directory.CreateDirectory(dataFolder);
File.Copy(defaultConfigPath, configPath, false); File.Copy(defaultConfigPath, configPath, false);

View File

@@ -5,9 +5,7 @@ using System.Text;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
[Serializable]
sealed class PluginConfig{ sealed class PluginConfig{
[field:NonSerialized]
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
public IEnumerable<string> DisabledPlugins => Disabled; public IEnumerable<string> DisabledPlugins => Disabled;
@@ -18,14 +16,6 @@ namespace TweetDuck.Plugins{
"official/reply-account" "official/reply-account"
}; };
public void ImportLegacy(PluginConfig config){
Disabled.Clear();
foreach(string plugin in config.Disabled){
Disabled.Add(plugin);
}
}
public void SetEnabled(Plugin plugin, bool enabled){ public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){ if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled)); InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));

View File

@@ -42,29 +42,14 @@ namespace TweetDuck.Plugins{
this.Config = new PluginConfig(); this.Config = new PluginConfig();
this.Bridge = new PluginBridge(this); this.Bridge = new PluginBridge(this);
LoadConfig(); Config.Load(configPath);
Config.InternalPluginChangedState += Config_InternalPluginChangedState; Config.InternalPluginChangedState += Config_InternalPluginChangedState;
Program.UserConfigReplaced += Program_UserConfigReplaced; Program.UserConfigReplaced += Program_UserConfigReplaced;
} }
private void LoadConfig(){
#pragma warning disable 612
if (Program.UserConfig.Plugins != null){
Config.ImportLegacy(Program.UserConfig.Plugins);
Config.Save(configPath);
Program.UserConfig.Plugins = null;
Program.UserConfig.Save();
}
#pragma warning restore 612
else{
Config.Load(configPath);
}
}
private void Program_UserConfigReplaced(object sender, EventArgs e){ private void Program_UserConfigReplaced(object sender, EventArgs e){
LoadConfig(); Config.Load(configPath);
Reload(); Reload();
} }

View File

@@ -1,4 +1,4 @@
using System.Text; using System.Globalization;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
@@ -8,22 +8,29 @@ namespace TweetDuck.Plugins{
} }
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){ public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
StringBuilder build = new StringBuilder(2*pluginIdentifier.Length+pluginContents.Length+165); return PluginGen
.Replace("%params", environment.GetScriptVariables())
.Replace("%id", pluginIdentifier)
.Replace("%token", pluginToken.ToString(CultureInfo.InvariantCulture))
.Replace("%contents", pluginContents);
}
build.Append("(function(").Append(environment.GetScriptVariables()).Append("){"); private const string PluginGen = "(function(%params,$d){let tmp={id:'%id',obj:new class extends PluginBase{%contents}};$d(tmp.obj,'$id',{value:'%id'});$d(tmp.obj,'$token',{value:%token});window.TD_PLUGINS.install(tmp);})(%params,Object.defineProperty);";
build.Append("let tmp={"); /* PluginGen
build.Append("id:\"").Append(pluginIdentifier).Append("\",");
build.Append("obj:new class extends PluginBase{").Append(pluginContents).Append("}");
build.Append("};");
build.Append("tmp.obj.$id=\"").Append(pluginIdentifier).Append("\";"); (function(%params, $i, $d){
build.Append("tmp.obj.$token=").Append(pluginToken).Append(";"); let tmp = {
build.Append("window.TD_PLUGINS.install(tmp);"); id: '%id',
obj: new class extends PluginBase{%contents}
};
build.Append("})(").Append(environment.GetScriptVariables()).Append(");"); $d(tmp.obj, '$id', { value: '%id' });
$d(tmp.obj, '$token', { value: %token });
return build.ToString(); window.TD_PLUGINS.install(tmp);
} })(%params, Object.defineProperty);
*/
} }
} }

View File

@@ -20,8 +20,8 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.7.6"; public const string VersionTag = "1.8";
public const string VersionFull = "1.7.6.0"; public const string VersionFull = "1.8.0.0";
public static readonly Version Version = new Version(VersionTag); public static readonly Version Version = new Version(VersionTag);
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");
@@ -29,17 +29,19 @@ namespace TweetDuck{
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath(); public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath();
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
private static readonly string ErrorLogFilePath = Path.Combine(StoragePath, "TD_Log.txt");
private static readonly string ConsoleLogFilePath = Path.Combine(StoragePath, "TD_Console.txt");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts"); public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
public static uint WindowRestoreMessage; public static uint WindowRestoreMessage;
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
@@ -231,7 +233,7 @@ namespace TweetDuck{
File.Delete(UserConfigFilePath); File.Delete(UserConfigFilePath);
File.Delete(UserConfig.GetBackupFile(UserConfigFilePath)); File.Delete(UserConfig.GetBackupFile(UserConfigFilePath));
}catch(Exception e){ }catch(Exception e){
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the settings.", true, e); Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the options.", true, e);
return; return;
} }

View File

@@ -39,7 +39,7 @@ using TweetDuck;
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
[assembly: CLSCompliant(false)] [assembly: CLSCompliant(true)]
#if DEBUG #if DEBUG
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]

View File

@@ -3,7 +3,7 @@ Clear columns
[description] [description]
- Adds buttons and keyboard shortcuts to quickly clear columns - Adds buttons and keyboard shortcuts to quickly clear columns
- Hold Shift when clicking or using a keyboard shortcut to reset the column instead - Hold Shift when clicking or using a keyboard shortcut to restore the column instead
[author] [author]
chylex chylex

View File

@@ -38,7 +38,7 @@ enabled(){
$(document).off("mousemove", this.eventKeyUp); $(document).off("mousemove", this.eventKeyUp);
} }
$("#clear-columns-btn-all").text(pressed ? "Reset all" : "Clear all"); $("#clear-columns-btn-all").text(pressed ? "Restore columns" : "Clear columns");
} }
}; };
@@ -84,7 +84,7 @@ enabled(){
replaceMustache("column/column_header.mustache", "</header>", [ replaceMustache("column/column_header.mustache", "</header>", [
'{{^isTemporary}}', '{{^isTemporary}}',
'<a class="column-header-link" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">', '<a class="column-header-link" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">',
'<i class="icon icon-clear-timeline"></i>', '<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i>',
'</a>', '</a>',
'{{/isTemporary}}', '{{/isTemporary}}',
'</header>' '</header>'
@@ -94,7 +94,7 @@ enabled(){
'<dd class="keyboard-shortcut-definition" style="white-space:nowrap">', '<dd class="keyboard-shortcut-definition" style="white-space:nowrap">',
'<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19', '<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19',
'</dd><dd class="keyboard-shortcut-definition">', '</dd><dd class="keyboard-shortcut-definition">',
'<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all', '<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns',
'</dd></dl><dl' '</dd></dl><dl'
].join("")); ].join(""));
@@ -116,11 +116,18 @@ ready(){
// add clear all button // add clear all button
$("nav.app-navigator").first().append([ $("nav.app-navigator").first().append([
'<a class="link-clean cf app-nav-link padding-h--10" data-title="Clear all" data-action="td-clearcolumns-doall">', '<a id="clear-columns-btn-all-parent" class="js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">',
'<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>', '<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>',
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed txt-size--16">Clear all</div>', '<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed txt-size--16">Clear columns</div>',
'</a></nav>' '</a></nav>'
].join("")); ].join(""));
// setup tooltip handling
var tooltipEvents = $._data($(".js-header-action")[0]).events;
if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){
$("#clear-columns-btn-all-parent").on("mouseover", tooltipEvents.mouseover[0].handler).on("mouseout", tooltipEvents.mouseout[0].handler);
}
} }
disabled(){ disabled(){

View File

@@ -8,7 +8,7 @@ Edit layout & design
chylex chylex
[version] [version]
1.0 1.1
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -7,6 +7,7 @@ constructor(){
enabled(){ enabled(){
// elements & data // elements & data
this.css = null; this.css = null;
this.icons = null;
this.htmlModal = null; this.htmlModal = null;
this.config = null; this.config = null;
@@ -18,11 +19,14 @@ enabled(){
revertReplies: false, revertReplies: false,
themeColorTweaks: true, themeColorTweaks: true,
roundedScrollBars: false, roundedScrollBars: false,
revertIcons: true,
smallComposeTextSize: false, smallComposeTextSize: false,
optimizeAnimations: true, optimizeAnimations: true,
avatarRadius: 10 avatarRadius: 10
}; };
this.firstTimeLoad = null;
// modal dialog loading // modal dialog loading
$TDP.readFileRoot(this.$token, "modal.html").then(contents => { $TDP.readFileRoot(this.$token, "modal.html").then(contents => {
this.htmlModal = contents; this.htmlModal = contents;
@@ -32,30 +36,58 @@ enabled(){
// configuration // configuration
const configFile = "config.json"; const configFile = "config.json";
this.tmpConfig = null; this.tmpConfig = null;
this.currentStage = 0;
var loadConfigObject = obj => { this.onStageReady = () => {
this.tmpConfig = obj || {}; if (this.currentStage === 0){
this.currentStage = 1;
if (TD.ready){
this.onAppReady();
} }
else if (this.tmpConfig !== null){
this.injectDeciderReplyHook(this.tmpConfig.revertReplies);
};
this.onAppReady = () => {
if (this.tmpConfig !== null){
this.config = $.extend(this.defaultConfig, this.tmpConfig); this.config = $.extend(this.defaultConfig, this.tmpConfig);
this.tmpConfig = null; this.tmpConfig = null;
this.reinjectAll(); this.reinjectAll();
if (this.firstTimeLoad){
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config));
}
} }
}; };
var loadConfigObject = obj => {
this.tmpConfig = obj || {};
this.firstTimeLoad = obj === null;
this.onStageReady();
this.injectDeciderReplyHook(obj && obj.revertReplies);
};
if (this.$$wasLoadedBefore){
this.onStageReady();
}
else{
$(document).one("dataSettingsValues", () => {
switch(TD.settings.getColumnWidth()){
case "wide": this.defaultConfig.columnWidth = "350px"; break;
case "narrow": this.defaultConfig.columnWidth = "270px"; break;
}
switch(TD.settings.getFontSize()){
case "small": this.defaultConfig.fontSize = "13px"; break;
case "medium": this.defaultConfig.fontSize = "14px"; break;
case "large": this.defaultConfig.fontSize = "15px"; break;
case "largest": this.defaultConfig.fontSize = "16px"; break;
}
this.$$wasLoadedBefore = true;
this.onStageReady();
});
}
$TDP.checkFileExists(this.$token, configFile).then(exists => { $TDP.checkFileExists(this.$token, configFile).then(exists => {
if (!exists){ if (!exists){
loadConfigObject(null); loadConfigObject(null);
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.defaultConfig));
} }
else{ else{
$TDP.readFile(this.$token, configFile, true).then(contents => { $TDP.readFile(this.$token, configFile, true).then(contents => {
@@ -208,7 +240,7 @@ enabled(){
_render: () => $(this.htmlModal), _render: () => $(this.htmlModal),
destroy: function(){ destroy: function(){
if (this.reloadPage){ if (this.reloadPage){
location.reload(); window.TDPF_requestReload();
return; return;
} }
@@ -227,7 +259,7 @@ enabled(){
}; };
TD.decider.updateForGuestId(); TD.decider.updateForGuestId();
this.$pluginSettings.requiresPageReload = enable; this.$requiresReload = enable;
}; };
// animation optimization // animation optimization
@@ -282,13 +314,13 @@ enabled(){
}; };
this.onWindowFocusEvent = () => { this.onWindowFocusEvent = () => {
if (this.config.optimizeAnimations){ if (this.config && this.config.optimizeAnimations){
injectOptimizations(true); injectOptimizations(true);
} }
}; };
this.onWindowBlurEvent = () => { this.onWindowBlurEvent = () => {
if (this.config.optimizeAnimations){ if (this.config && this.config.optimizeAnimations){
disableOptimizations(); disableOptimizations();
clearOptimizationTimer(); clearOptimizationTimer();
} }
@@ -301,6 +333,11 @@ enabled(){
} }
this.css = window.TDPF_createCustomStyle(this); this.css = window.TDPF_createCustomStyle(this);
if (this.icons){
document.head.removeChild(this.icons);
this.icons = null;
}
}; };
this.reinjectAll = () => { this.reinjectAll = () => {
@@ -338,8 +375,9 @@ enabled(){
if (this.config.hideTweetActions){ if (this.config.hideTweetActions){
this.css.insert(".tweet-action { opacity: 0; }"); this.css.insert(".tweet-action { opacity: 0; }");
this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5; }");
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }"); this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }");
this.css.insert(".tweet:hover .tweet-action, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1; visibility: visible !important }"); this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }");
} }
if (this.config.moveTweetActionsToRight){ if (this.config.moveTweetActionsToRight){
@@ -367,6 +405,98 @@ enabled(){
this.css.insert(".activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }"); this.css.insert(".activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }");
} }
if (this.config.revertIcons){
this.icons = document.createElement("style");
this.icons.innerHTML = `
@font-face {
font-family: 'tweetdeckold';
src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff");
font-weight: normal;
font-style: normal;
}
.icon-twitter-bird:before{content:"\\f000";font-family:tweetdeckold}
.icon-mention:before{content:"\\f001";font-family:tweetdeckold}
.icon-following:before{content:"\\f002";font-family:tweetdeckold}
.icon-message:before{content:"\\f003";font-family:tweetdeckold}
.icon-home:before{content:"\\f004";font-family:tweetdeckold}
.icon-hashtag:before{content:"\\f005";font-family:tweetdeckold}
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
.icon-favorite:before{content:"\\f055";font-family:tweetdeckold}
.icon-retweet:before{content:"\\f008";font-family:tweetdeckold}
.icon-drafts:before{content:"\\f009";font-family:tweetdeckold}
.icon-search:before{content:"\\f00a";font-family:tweetdeckold}
.icon-trash:before{content:"\\f00c";font-family:tweetdeckold}
.icon-close:before{content:"\\f00d";font-family:tweetdeckold}
.icon-arrow-r:before,.Icon--caretRight:before{content:"\\f00e";font-family:tweetdeckold}
.icon-arrow-l:before,.Icon--caretLeft:before{content:"\\f00f";font-family:tweetdeckold}
.icon-protected:before{content:"\\f013";font-family:tweetdeckold}
.icon-list:before{content:"\\f014";font-family:tweetdeckold}
.icon-camera:before{content:"\\f015";font-family:tweetdeckold}
.icon-more:before{content:"\\f016";font-family:tweetdeckold}
.icon-settings:before{content:"\\f018";font-family:tweetdeckold}
.icon-notifications:before{content:"\\f019";font-family:tweetdeckold}
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
.icon-activity:before{content:"\\f01c";font-family:tweetdeckold}
.icon-trending:before{content:"\\f01d";font-family:tweetdeckold}
.icon-minus:before{content:"\\f01e";font-family:tweetdeckold}
.icon-plus:before{content:"\\f01f";font-family:tweetdeckold}
.icon-geo:before{content:"\\f020";font-family:tweetdeckold}
.icon-check:before{content:"\\f021";font-family:tweetdeckold}
.icon-schedule:before{content:"\\f022";font-family:tweetdeckold}
.icon-dot:before{content:"\\f023";font-family:tweetdeckold}
.icon-user:before{content:"\\f024";font-family:tweetdeckold}
.icon-content:before{content:"\\f025";font-family:tweetdeckold}
.icon-arrow-d:before,.Icon--caretDown:before{content:"\\f026";font-family:tweetdeckold}
.icon-arrow-u:before{content:"\\f027";font-family:tweetdeckold}
.icon-share:before{content:"\\f028";font-family:tweetdeckold}
.icon-info:before{content:"\\f029";font-family:tweetdeckold}
.icon-verified:before{content:"\\f02a";font-family:tweetdeckold}
.icon-translator:before{content:"\\f02b";font-family:tweetdeckold}
.icon-blocked:before{content:"\\f02c";font-family:tweetdeckold}
.icon-constrain:before{content:"\\f02d";font-family:tweetdeckold}
.icon-play-video:before{content:"\\f02e";font-family:tweetdeckold}
.icon-empty:before{content:"\\f02f";font-family:tweetdeckold}
.icon-clear-input:before{content:"\\f030";font-family:tweetdeckold}
.icon-compose:before{content:"\\f031";font-family:tweetdeckold}
.icon-mark-read:before{content:"\\f032";font-family:tweetdeckold}
.icon-arrow-r-double:before{content:"\\f033";font-family:tweetdeckold}
.icon-arrow-l-double:before{content:"\\f034";font-family:tweetdeckold}
.icon-follow:before{content:"\\f035";font-family:tweetdeckold}
.icon-image:before{content:"\\f036";font-family:tweetdeckold}
.icon-popout:before{content:"\\f037";font-family:tweetdeckold}
.icon-move:before{content:"\\f039";font-family:tweetdeckold}
.icon-compose-grid:before{content:"\\f03a";font-family:tweetdeckold}
.icon-compose-minigrid:before{content:"\\f03b";font-family:tweetdeckold}
.icon-compose-list:before{content:"\\f03c";font-family:tweetdeckold}
.icon-edit:before{content:"\\f040";font-family:tweetdeckold}
.icon-clear-timeline:before{content:"\\f041";font-family:tweetdeckold}
.icon-sliders:before{content:"\\f042";font-family:tweetdeckold}
.icon-custom-timeline:before{content:"\\f043";font-family:tweetdeckold}
.icon-compose-dm:before{content:"\\f044";font-family:tweetdeckold}
.icon-bg-dot:before{content:"\\f045";font-family:tweetdeckold}
.icon-user-team-mgr:before{content:"\\f046";font-family:tweetdeckold}
.icon-user-switch:before{content:"\\f047";font-family:tweetdeckold}
.icon-conversation:before{content:"\\f048";font-family:tweetdeckold}
.icon-dataminr:before{content:"\\f049";font-family:tweetdeckold}
.icon-link:before{content:"\\f04a";font-family:tweetdeckold}
.icon-flash:before{content:"\\f050";font-family:tweetdeckold}
.icon-pointer-u:before{content:"\\f051";font-family:tweetdeckold}
.icon-analytics:before{content:"\\f054";font-family:tweetdeckold}
.icon-heart:before{content:"\\f055";font-family:tweetdeckold}
.icon-calendar:before{content:"\\f056";font-family:tweetdeckold}
.icon-attachment:before{content:"\\f057";font-family:tweetdeckold}
.icon-play:before{content:"\\f058";font-family:tweetdeckold}
.icon-bookmark:before{content:"\\f059";font-family:tweetdeckold}
.icon-play-badge:before{content:"\\f060";font-family:tweetdeckold}
.icon-gif-badge:before{content:"\\f061";font-family:tweetdeckold}
.icon-poll:before{content:"\\f062";font-family:tweetdeckold}
.column-header .column-type-icon { bottom: 26px !important }`;
document.head.appendChild(this.icons);
}
if (this.config.columnWidth[0] === '/'){ if (this.config.columnWidth[0] === '/'){
let cols = this.config.columnWidth.slice(1); let cols = this.config.columnWidth.slice(1);
@@ -399,13 +529,25 @@ enabled(){
default: TD.settings.setFontSize(parseInt(this.config.fontSize, 10) >= 16 ? "largest" : "smallest"); break; default: TD.settings.setFontSize(parseInt(this.config.fontSize, 10) >= 16 ? "largest" : "smallest"); break;
} }
$TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", [ $TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", `
"<style type='text/css'>", <style type='text/css'>
".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }", .txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important }
".avatar { border-radius: "+this.config.avatarRadius+"% !important }", .avatar { border-radius: ${this.config.avatarRadius}% !important }
(this.config.revertReplies ? ".activity-header + .tweet .tweet-context { margin-left: -35px } .activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }" : ""),
"</style>" ${this.config.revertReplies ? `
].join("")); .activity-header + .tweet .tweet-context { margin-left: -35px }
.activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }
` : ``}
${this.config.revertIcons ? `
@font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal }
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
.icon-favorite:before{content:"\\f055";font-family:tweetdeckold}
.icon-retweet:before{content:"\\f008";font-family:tweetdeckold}
.icon-follow:before{content:"\\f035";font-family:tweetdeckold}
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
` : ``}
</style>`);
}; };
this.uiShowActionsMenuEvent = () => { this.uiShowActionsMenuEvent = () => {
@@ -416,21 +558,6 @@ enabled(){
} }
ready(){ ready(){
// configuration
switch(TD.settings.getColumnWidth()){
case "wide": this.defaultConfig.columnWidth = "350px"; break;
case "narrow": this.defaultConfig.columnWidth = "270px"; break;
}
switch(TD.settings.getFontSize()){
case "small": this.defaultConfig.fontSize = "13px"; break;
case "medium": this.defaultConfig.fontSize = "14px"; break;
case "large": this.defaultConfig.fontSize = "15px"; break;
case "largest": this.defaultConfig.fontSize = "16px"; break;
}
this.onAppReady();
// optimization events // optimization events
$(window).on("focus", this.onWindowFocusEvent); $(window).on("focus", this.onWindowFocusEvent);
$(window).on("blur", this.onWindowBlurEvent); $(window).on("blur", this.onWindowBlurEvent);
@@ -448,6 +575,10 @@ disabled(){
this.css.remove(); this.css.remove();
} }
if (this.icons){
document.head.removeChild(this.icons);
}
if (this.optimizations){ if (this.optimizations){
this.optimizations.remove(); this.optimizations.remove();
} }

View File

@@ -106,6 +106,10 @@
<input data-td-key="roundedScrollBars" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="roundedScrollBars" class="js-theme-checkbox touch-larger-label" type="checkbox">
Rounded scroll bars Rounded scroll bars
</label> </label>
<label class="checkbox">
<input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox">
Revert icon design
</label>
<label class="checkbox"> <label class="checkbox">
<input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
Small compose tweet font size Small compose tweet font size

View File

@@ -9,7 +9,7 @@ Emoji keyboard
chylex chylex
[version] [version]
1.0 1.1
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -1,5 +1,6 @@
enabled(){ enabled(){
this.selectedSkinTone = ""; this.selectedSkinTone = "";
this.currentKeywords = [];
this.skinToneList = [ this.skinToneList = [
"", "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF" "", "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF"
@@ -20,9 +21,10 @@ enabled(){
this.emojiURL = "https://ton.twimg.com/tweetdeck-web/web/assets/emoji/"; this.emojiURL = "https://ton.twimg.com/tweetdeck-web/web/assets/emoji/";
this.emojiHTML1 = ""; // no skin tones, prepended this.emojiData1 = []; // no skin tones, prepended
this.emojiHTML2 = {}; // contains emojis with skin tones this.emojiData2 = {}; // contains emojis with skin tones
this.emojiHTML3 = ""; // no skin tones, appended this.emojiData3 = []; // no skin tones, appended
this.emojiNames = [];
var me = this; var me = this;
@@ -33,6 +35,8 @@ enabled(){
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }"); this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
this.css.insert(".emoji-keyboard-list .separator { height: 26px }"); this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }"); this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 2px 2px 0 0 }");
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 2px 2px }"); this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 2px 2px }");
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }"); this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }"); this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
@@ -54,26 +58,70 @@ enabled(){
// keyboard generation // keyboard generation
this.currentKeyboard = null; this.currentKeyboard = null;
this.currentSpanner = null;
var hideKeyboard = () => { var hideKeyboard = () => {
$(this.currentKeyboard).remove(); $(this.currentKeyboard).remove();
this.currentKeyboard = null; this.currentKeyboard = null;
$(this.currentSpanner).remove();
this.currentSpanner = null;
this.composePanelScroller.trigger("scroll");
$(".emoji-keyboard-popup-btn").removeClass("is-selected"); $(".emoji-keyboard-popup-btn").removeClass("is-selected");
$(".js-compose-text").first().focus(); $(".js-compose-text").first().focus();
}; };
var generateEmojiHTML = skinTone => { var generateEmojiHTML = skinTone => {
return (this.emojiHTML1+this.emojiHTML2[skinTone]+this.emojiHTML3).replace(/u#/g, this.emojiURL); let index = 0;
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that most emoji will not show up properly in the text box above, but they will display in the tweet.</p>" ];
for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){
for(let emoji of array){
if (emoji === "___"){
html.push("<div class='separator'></div>");
}
else{
html.push(TD.util.cleanWithEmoji(emoji).replace(' class="emoji" draggable="false"', ''));
index++;
}
}
}
return html.join("");
};
var updateFilters = () => {
let keywords = this.currentKeywords;
let container = $(this.currentKeyboard.children[1]);
let emoji = container.children("img");
let info = container.children("p:first");
let separators = container.children("div");
if (keywords.length === 0){
info.css("display", "block");
separators.css("display", "block");
emoji.css("display", "inline");
}
else{
info.css("display", "none");
separators.css("display", "none");
emoji.css("display", "none");
emoji.filter(index => keywords.every(kw => me.emojiNames[index].includes(kw))).css("display", "inline");
}
}; };
var selectSkinTone = skinTone => { var selectSkinTone = skinTone => {
let selectedEle = this.currentKeyboard.children[1].querySelector("[data-tone='"+this.selectedSkinTone+"']"); let selectedEle = this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']");
selectedEle && selectedEle.classList.remove("sel"); selectedEle && selectedEle.classList.remove("sel");
this.selectedSkinTone = skinTone; this.selectedSkinTone = skinTone;
this.currentKeyboard.children[0].innerHTML = generateEmojiHTML(skinTone); this.currentKeyboard.children[1].innerHTML = generateEmojiHTML(skinTone);
this.currentKeyboard.children[1].querySelector("[data-tone='"+this.selectedSkinTone+"']").classList.add("sel"); this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']").classList.add("sel");
updateFilters();
}; };
this.generateKeyboard = (input, left, top) => { this.generateKeyboard = (input, left, top) => {
@@ -103,10 +151,19 @@ enabled(){
e.stopPropagation(); e.stopPropagation();
}); });
var search = document.createElement("div");
search.innerHTML = "<input type='text' placeholder='Search...'>";
search.classList.add("emoji-keyboard-search");
var skintones = document.createElement("div"); var skintones = document.createElement("div");
skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join(""); skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join("");
skintones.classList.add("emoji-keyboard-skintones"); skintones.classList.add("emoji-keyboard-skintones");
outer.appendChild(search);
outer.appendChild(keyboard);
outer.appendChild(skintones);
$(".js-app").append(outer);
skintones.addEventListener("click", function(e){ skintones.addEventListener("click", function(e){
if (e.target.hasAttribute("data-tone")){ if (e.target.hasAttribute("data-tone")){
selectSkinTone(e.target.getAttribute("data-tone") || ""); selectSkinTone(e.target.getAttribute("data-tone") || "");
@@ -115,23 +172,36 @@ enabled(){
e.stopPropagation(); e.stopPropagation();
}); });
outer.appendChild(keyboard); search.addEventListener("click", function(e){
outer.appendChild(skintones); e.stopPropagation();
document.body.appendChild(outer); });
var searchInput = search.children[0];
searchInput.focus();
searchInput.addEventListener("input", function(e){
me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
updateFilters();
e.stopPropagation();
});
searchInput.addEventListener("focus", function(){
$(this).select();
});
this.currentKeyboard = outer; this.currentKeyboard = outer;
selectSkinTone(this.selectedSkinTone); selectSkinTone(this.selectedSkinTone);
this.currentSpanner = document.createElement("div");
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
this.composePanelScroller.trigger("scroll");
}; };
this.prevTryPasteImage = window.TDGF_tryPasteImage; var getKeyboardTop = () => {
var prevTryPasteImageF = this.prevTryPasteImage; let button = $(".emoji-keyboard-popup-btn");
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
window.TDGF_tryPasteImage = function(){
if (me.currentKeyboard){
hideKeyboard();
}
return prevTryPasteImageF.apply(this, arguments);
}; };
// event handlers // event handlers
@@ -139,18 +209,22 @@ enabled(){
this.emojiKeyboardButtonClickEvent = function(e){ this.emojiKeyboardButtonClickEvent = function(e){
if (me.currentKeyboard){ if (me.currentKeyboard){
hideKeyboard(); hideKeyboard();
$(this).blur();
} }
else{ else{
var pos = $(this).offset(); me.generateKeyboard($(".js-compose-text").first(), $(this).offset().left, getKeyboardTop());
me.generateKeyboard($(".js-compose-text").first(), pos.left, pos.top+$(this).outerHeight()+8);
$(this).addClass("is-selected"); $(this).addClass("is-selected");
} }
$(this).blur();
e.stopPropagation(); e.stopPropagation();
}; };
this.composerScrollEvent = function(e){
if (me.currentKeyboard){
me.currentKeyboard.style.marginTop = (-$(this).scrollTop())+"px";
}
};
this.documentClickEvent = function(e){ this.documentClickEvent = function(e){
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){ if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
hideKeyboard(); hideKeyboard();
@@ -164,18 +238,21 @@ enabled(){
} }
}; };
/* this.uploadFilesEvent = function(e){
* TODO if (me.currentKeyboard){
* ---- me.currentKeyboard.style.top = getKeyboardTop()+"px";
* add emoji search if I can be bothered }
* lazy emoji loading }
*/
} }
ready(){ ready(){
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); $(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
$(document).on("click", this.documentClickEvent); $(document).on("click", this.documentClickEvent);
$(document).on("keydown", this.documentKeyEvent); $(document).on("keydown", this.documentKeyEvent);
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
// HTML generation // HTML generation
@@ -190,18 +267,14 @@ ready(){
}; };
$TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => { $TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => {
let generated1 = [];
let generated2 = {};
let generated3 = [];
for(let skinTone of this.skinToneList){ for(let skinTone of this.skinToneList){
generated2[skinTone] = []; this.emojiData2[skinTone] = [];
} }
// declaration inserters // declaration inserters
let addDeclaration1 = decl => { let addDeclaration1 = decl => {
generated1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("")); this.emojiData1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
}; };
let addDeclaration2 = (tone, decl) => { let addDeclaration2 = (tone, decl) => {
@@ -209,16 +282,16 @@ ready(){
if (tone === null){ if (tone === null){
for(let skinTone of this.skinToneList){ for(let skinTone of this.skinToneList){
generated2[skinTone].push(gen); this.emojiData2[skinTone].push(gen);
} }
} }
else{ else{
generated2[tone].push(gen); this.emojiData2[tone].push(gen);
} }
}; };
let addDeclaration3 = decl => { let addDeclaration3 = decl => {
generated3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("")); this.emojiData3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
}; };
// line reading // line reading
@@ -228,9 +301,9 @@ ready(){
for(let line of contents.split("\n")){ for(let line of contents.split("\n")){
if (line[0] === '@'){ if (line[0] === '@'){
switch(skinToneState){ switch(skinToneState){
case 0: generated1.push("___"); break; case 0: this.emojiData1.push("___"); break;
case 1: this.skinToneList.forEach(skinTone => generated2[skinTone].push("___")); break; case 1: this.skinToneList.forEach(skinTone => this.emojiData2[skinTone].push("___")); break;
case 2: generated3.push("___"); break; case 2: this.emojiData3.push("___"); break;
} }
if (line[1] === '1'){ if (line[1] === '1'){
@@ -239,9 +312,15 @@ ready(){
else if (line[1] === '2'){ else if (line[1] === '2'){
skinToneState = 2; skinToneState = 2;
} }
continue;
} }
else if (skinToneState === 1){
let decl = line.slice(0, line.indexOf(';')); let semicolon = line.indexOf(';');
let decl = line.slice(0, semicolon);
let desc = line.slice(semicolon+1).toLowerCase();
if (skinToneState === 1){
let skinIndex = decl.indexOf('$'); let skinIndex = decl.indexOf('$');
if (skinIndex !== -1){ if (skinIndex !== -1){
@@ -249,35 +328,24 @@ ready(){
let declPost = decl.slice(skinIndex+1); let declPost = decl.slice(skinIndex+1);
for(let skinTone of this.skinToneNonDefaultList){ for(let skinTone of this.skinToneNonDefaultList){
generated2[skinTone].pop(); this.emojiData2[skinTone].pop();
addDeclaration2(skinTone, declPre+skinTone+declPost); addDeclaration2(skinTone, declPre+skinTone+declPost);
} }
} }
else{ else{
addDeclaration2(null, decl); addDeclaration2(null, decl);
this.emojiNames.push(desc);
} }
} }
else if (skinToneState === 2){ else if (skinToneState === 2){
addDeclaration3(line.slice(0, line.indexOf(';'))); addDeclaration3(decl);
this.emojiNames.push(desc);
} }
else if (skinToneState === 0){ else if (skinToneState === 0){
addDeclaration1(line.slice(0, line.indexOf(';'))); addDeclaration1(decl);
this.emojiNames.push(desc);
} }
} }
// final processing
let urlRegex = new RegExp(this.emojiURL.replace(/\./g, "\\."), "g");
let process = str => TD.util.cleanWithEmoji(str).replace(/ class=\"emoji\" draggable=\"false\"/g, "").replace(urlRegex, "u#").replace(/___/g, "<div class='separator'></div>");
let start = "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that most emoji will not show up properly in the text box above, but they will display in the tweet.</p>";
this.emojiHTML1 = start+process(generated1.join(""));
for(let skinTone of this.skinToneList){
this.emojiHTML2[skinTone] = process(generated2[skinTone].join(""));
}
this.emojiHTML3 = process(generated3.join(""));
}).catch(err => { }).catch(err => {
$TD.alert("error", "Problem loading emoji keyboard: "+err.message); $TD.alert("error", "Problem loading emoji keyboard: "+err.message);
}); });
@@ -290,12 +358,17 @@ disabled(){
$(this.currentKeyboard).remove(); $(this.currentKeyboard).remove();
} }
window.TDGF_tryPasteImage = this.prevTryPasteImage; if (this.currentSpanner){
$(this.currentSpanner).remove();
}
this.composePanelScroller.off("scroll", this.composerScrollEvent);
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent); $(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
$(".emoji-keyboard-popup-btn").remove(); $(".emoji-keyboard-popup-btn").remove();
$(document).off("click", this.documentClickEvent); $(document).off("click", this.documentClickEvent);
$(document).off("keydown", this.documentKeyEvent); $(document).off("keydown", this.documentKeyEvent);
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
} }

View File

@@ -0,0 +1,70 @@
Emoji list: http://unicode.org/emoji/charts/emoji-ordering.html
Emoji order: http://unicode.org/emoji/charts/emoji-ordering.txt
------------------------
Remove unnecessary info:
Search: \s;.+?#.+?\s
Replace: ;
Search: U+
Replace:
-----------------------------
Replace skin tone variations:
Example:
1F9D2;child
1F9D2 1F3FB; child: light skin tone
1F9D2 1F3FC; child: medium-light skin tone
1F9D2 1F3FD; child: medium skin tone
1F9D2 1F3FE; child: medium-dark skin tone
1F9D2 1F3FF; child: dark skin tone
1F9D2;child
1F9D2 $;child
TODO: Update this section with exact regexes
----------------
Move some emoji:
1F443 $;nose
> 1F91D;handshake
1F463;footprints
1F939 $ 200D 2640 FE0F;woman juggling
> 1F6CC;person in bed
> 1F6CC $;person in bed
> 1F6C0;person taking bath
> 1F6C0 $;person taking bath
1F46B;man and woman holding hands
------------------
Remove some emoji:
1F469 $ 200D 1F692;woman firefighter
> remove all non-gendered duplicates below here
3030;wavy dash
> remove copyright
> remove registered trademark
> remove trademark
0023 FE0F 20E3;keycap
1F441;eye
> remove eye in speech bubble
1F445;tongue
-------------------------
Add preprocessor symbols:
@ = group separator
@1 = enable skin tones below
@2 = disable skin tones below

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
[name]
Templates
[description]
- Adds a templating system for tweets
[author]
chylex
[version]
1.0
[website]
https://tweetduck.chylex.com
[requires]
1.5.3

View File

@@ -0,0 +1,472 @@
enabled(){
let me = this;
// configuration
this.config = {
templates: {} // identifier: { name, contents }
};
const configFile = "config.json";
$TDP.checkFileExists(this.$token, configFile).then(exists => {
if (!exists){
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config));
}
else{
$TDP.readFile(this.$token, configFile, true).then(contents => {
try{
$.extend(true, this.config, JSON.parse(contents));
}catch(err){
// why :(
}
}).catch(err => {
$TD.alert("error", "Problem loading configuration for the template plugin: "+err.message);
});
}
});
this.saveConfig = () => {
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)).catch(err => {
$TD.alert("error", "Problem saving configuration for the template plugin: "+err.message);
});
};
// button
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--9"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="js-tweet-type-button">', buttonHTML+'<div class="js-tweet-type-button">');
var dockedComposePanel = $(".js-docked-compose");
if (dockedComposePanel.length){
dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML);
}
// css
this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }");
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; text-align: right; }");
this.css.insert(".templates-modal-bottom button { margin-left: 4px; }");
this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }");
this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
this.css.insert(".template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
this.css.insert(".template-list li[data-template] { cursor: pointer; }");
this.css.insert(".template-list li[data-template]:hover { background-color: #d8d8d8; }");
this.css.insert(".template-list li span { white-space: nowrap; }");
this.css.insert(".template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
this.css.insert(".template-list li .icon:hover { opacity: 1; }");
this.css.insert(".template-list li .template-actions { float: right; }");
this.css.insert(".template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }");
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
this.css.insert(".template-editor-form input, .template-editor-form textarea { color: #111; background-color: #fff; border: none; border-radius: 0; }");
this.css.insert(".template-editor-form input:focus, .template-editor-form textarea:focus { box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6); }");
this.css.insert(".template-editor-form textarea { height: 146px; font-size: 14px; padding: 10px; resize: none; }");
this.css.insert(".template-editor-form .template-editor-tips-button { cursor: pointer; }");
this.css.insert(".template-editor-form .template-editor-tips-button .icon { font-size: 12px; vertical-align: -5%; margin-left: 4px; }");
this.css.insert(".template-editor-form .template-editor-tips { display: none; }");
this.css.insert(".template-editor-form .template-editor-tips p { margin: 10px 0; }");
this.css.insert(".template-editor-form .template-editor-tips p:first-child { margin-top: 0; }");
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n+1) { margin-top: 5px; padding-left: 6px; font-family: monospace; }");
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n) { margin-top: 1px; padding-left: 14px; opacity: 0.66; }");
this.css.insert(".invisible { display: none !important; }");
// template implementation
var readTemplateTokens = (contents, tokenData) => {
let startIndex = -1;
let endIndex = -1;
let data = [];
let tokenNames = Object.keys(tokenData);
for(let currentIndex = 0; currentIndex < contents.length; currentIndex++){
if (contents[currentIndex] === '\\'){
contents = contents.substring(0, currentIndex)+contents.substring(currentIndex+1);
continue;
}
else if (contents[currentIndex] !== '{'){
continue;
}
startIndex = currentIndex+1;
for(; startIndex < contents.length; startIndex++){
if (!tokenNames.some(name => contents[startIndex] === name[startIndex-currentIndex-1])){
break;
}
}
endIndex = startIndex;
let token = contents.substring(currentIndex+1, startIndex);
let replacement = tokenData[token] || "";
let entry = [ token, currentIndex ];
if (contents[endIndex] === '#'){
++endIndex;
let bracketCount = 1;
for(; endIndex < contents.length; endIndex++){
if (contents[endIndex] === '{'){
++bracketCount;
}
else if (contents[endIndex] === '}'){
if (--bracketCount === 0){
entry.push(contents.substring(startIndex+1, endIndex));
break;
}
}
else if (contents[endIndex] === '#'){
entry.push(contents.substring(startIndex+1, endIndex));
startIndex = endIndex;
}
else if (contents[endIndex] === '\\'){
contents = contents.substring(0, endIndex)+contents.substring(endIndex+1);
}
}
}
else if (contents[endIndex] !== '}'){
continue;
}
data.push(entry);
contents = contents.substring(0, currentIndex)+replacement+contents.substring(endIndex+1);
currentIndex += replacement.length;
}
return [ contents, data ];
};
var doAjaxRequest = (index, url, evaluator) => {
return new Promise((resolve, reject) => {
if (!url){
resolve([ index, "{ajax}" ]);
return;
}
$.get(url, function(data){
if (evaluator){
resolve([ index, eval(evaluator.replace(/\$/g, "'"+data.replace(/(["'\\\n\r\u2028\u2029])/g, "\\$1")+"'"))]);
}
else{
resolve([ index, data ]);
}
}, "text").fail(function(){
resolve([ index, "" ]);
});
});
};
var useTemplate = (contents, append) => {
let ele = $(".js-compose-text");
if (ele.length === 0)return;
let value = append ? ele.val()+contents : contents;
let prevLength = value.length;
let tokens = null;
[value, tokens] = readTemplateTokens(value, {
"cursor": "",
"ajax": "(...)"
});
ele.val(value);
ele.trigger("change");
ele.focus();
ele[0].selectionStart = ele[0].selectionEnd = value.length;
let promises = [];
let indexOffset = 0;
for(let token of tokens){
switch(token[0]){
case "cursor":
let [, index1, length ] = token;
ele[0].selectionStart = index1;
ele[0].selectionEnd = index1+(length | 0 || 0);
break;
case "ajax":
let [, index2, evaluator, url ] = token;
if (!url){
url = evaluator;
evaluator = null;
}
promises.push(doAjaxRequest(index2, url, evaluator));
break;
}
}
if (promises.length > 0){
let selStart = ele[0].selectionStart;
let selEnd = ele[0].selectionEnd;
ele.prop("disabled", true);
Promise.all(promises).then(values => {
const placeholderLen = 5; // "(...)".length
let indexOffset = 0;
for(let value of values){
let diff = value[1].length-placeholderLen;
let realIndex = indexOffset+value[0];
let val = ele.val();
ele.val(val.substring(0, realIndex)+value[1]+val.substring(realIndex+placeholderLen));
indexOffset += diff;
}
ele.prop("disabled", false);
ele.trigger("change");
ele.focus();
ele[0].selectionStart = selStart+indexOffset;
ele[0].selectionEnd = selEnd+indexOffset;
});
}
if (!append){
hideTemplateModal();
}
};
// modal dialog
this.editingTemplate = null;
var showTemplateModal = () => {
$(".manage-templates-btn").addClass("active");
let html = `
<div class="templates-modal-wrap">
<div class="templates-modal">
<div class="template-list">
<ul></ul>
<div class="templates-modal-bottom">
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
</div>
</div>
<div class="template-editor invisible">
<div class="template-editor-form">
<div class="compose-text-title">Template Name</div>
<input name="template-name" type="text">
<div class="compose-text-title">Contents</div>
<textarea name="template-contents" class="compose-text scroll-v scroll-styled-v scroll-styled-h scroll-alt"></textarea>
<div class="compose-text-title template-editor-tips-button">Advanced <i class="icon icon-arrow-d"></i></div>
<div class="template-editor-tips">
<p>You can use the following tokens. All tokens except for <span style="font-family: monospace">{ajax}</span> can only be used once.</p>
<ul>
<li>{cursor}</li>
<li>Location where the cursor is placed</li>
<li>{cursor#&lt;selectionlength&gt;}</li>
<li>Places cursor and selects a set amount of characters</li>
<li>{ajax#&lt;url&gt;}</li>
<li>Replaced with the result of a cross-origin ajax request</li>
<li>{ajax#&lt;eval&gt;#&lt;url&gt;}</li>
<li>Allows parsing the ajax request using <span style="font-family: monospace">$</span> as the placeholder for the result<br>Example: <span style="font-family: monospace">$.substring(0,5)</span></li>
</ul>
<p>To use special characters in the tweet text, escape them with a backslash:
<br><span style="font-family: monospace">&nbsp; \\{&nbsp; \\}&nbsp; \\#&nbsp; \\\\</span>
</p>
</div>
</div>
<div class="templates-modal-bottom">
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
<button data-action="editor-confirm" class="btn btn-positive"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
</div>
</div>
</div>
</div>`;
/* TODO possibly implement this later
<li>{paste}</li>
<li>Paste text or an image from clipboard</li>
<li>{paste#text}</li>
<li>Paste only if clipboard has text</li>
<li>{paste#image}</li>
<li>Paste only if clipboard has an image</li>
*/
$(".js-app-content").prepend(html);
let ele = $(".templates-modal-wrap").first();
ele.on("click", "li[data-template]", function(e){
let template = me.config.templates[$(this).attr("data-template")];
useTemplate(template.contents, e.shiftKey);
});
ele.on("click", "li[data-template] i[data-action]", function(e){
let identifier = $(this).closest("li").attr("data-template");
switch($(this).attr("data-action")){
case "edit-template":
let editor = $(".template-editor");
if (editor.hasClass("invisible")){
toggleEditor();
}
let template = me.config.templates[identifier];
$("[name='template-name']", editor).val(template.name);
$("[name='template-contents']", editor).val(template.contents);
me.editingTemplate = identifier;
break;
case "delete-template":
delete me.config.templates[identifier];
onTemplatesUpdated(true);
if (me.editingTemplate === identifier){
me.editingTemplate = null;
}
break;
}
e.stopPropagation();
});
ele.on("click", ".template-editor-tips-button", function(e){
$(this).children(".icon").toggleClass("icon-arrow-d icon-arrow-u");
ele.find(".template-editor-tips").toggle();
});
ele.on("click", "button", function(e){
switch($(this).attr("data-action")){
case "new-template":
case "editor-cancel":
toggleEditor();
break;
case "editor-confirm":
let editor = $(".template-editor");
if (me.editingTemplate !== null){
delete me.config.templates[me.editingTemplate];
}
let name = $("[name='template-name']", editor).val();
let identifier = name.toLowerCase().replace(/[^a-z0-9]/g, "")+"-"+(Math.random().toString(36).substring(2, 7));
if (name.trim().length === 0){
alert("Please, include a name for your template.");
$("[name='template-name']", editor).focus();
return;
}
me.config.templates[identifier] = {
name: name,
contents: $("[name='template-contents']", editor).val()
};
toggleEditor();
onTemplatesUpdated(true);
break;
}
$(this).blur();
});
onTemplatesUpdated(false);
};
var hideTemplateModal = function(){
$(".templates-modal-wrap").remove();
$(".manage-templates-btn").removeClass("active");
};
var toggleEditor = function(){
let editor = $(".template-editor");
$("[name]", editor).val("");
if ($("button[data-action='new-template']", ".template-list").add(editor).toggleClass("invisible").hasClass("invisible")){
me.editingTemplate = null;
}
};
var onTemplatesUpdated = (save) => {
let eles = [];
for(let identifier of Object.keys(this.config.templates)){
eles.push(`<li data-template="${identifier}">
<span class="template-name">${this.config.templates[identifier].name}</span>
<span class="template-actions"><i class="icon icon-edit" data-action="edit-template"></i><i class="icon icon-close" data-action="delete-template"></i></span>
</li>`);
}
if (eles.length === 0){
eles.push("<li>No templates available</li>");
}
$(".template-list").children("ul").html(eles.join(""));
if (save){
this.saveConfig();
}
};
// event handlers
this.manageTemplatesButtonClickEvent = function(e){
if ($(".templates-modal-wrap").length){
hideTemplateModal();
}
else{
showTemplateModal();
}
$(this).blur();
};
this.drawerToggleEvent = function(e, data){
if (data.activeDrawer === null){
hideTemplateModal();
}
};
}
ready(){
$(".manage-templates-btn").on("click", this.manageTemplatesButtonClickEvent);
$(document).on("uiDrawerActive", this.drawerToggleEvent);
}
disabled(){
this.css.remove();
$(".manage-templates-btn").remove();
$(".templates-modal-wrap").remove();
$(document).off("uiDrawerActive", this.drawerToggleEvent);
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
}

View File

@@ -530,7 +530,7 @@
}); });
// //
// Block: Hold Shift to reset cleared column. // Block: Hold Shift to restore cleared column.
// //
(function(){ (function(){
var holdingShift = false; var holdingShift = false;
@@ -538,7 +538,7 @@
var updateShiftState = (pressed) => { var updateShiftState = (pressed) => {
if (pressed != holdingShift){ if (pressed != holdingShift){
holdingShift = pressed; holdingShift = pressed;
$("button[data-action='clear']").children("span").text(holdingShift ? "Reset" : "Clear"); $("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear");
} }
}; };
@@ -595,6 +595,26 @@
}); });
}); });
//
// Block: Make middle click on tweet reply icon open the compose drawer.
//
app.delegate(".js-reply-action", "mousedown", function(e){
if (e.which === 2){
if ($("[data-drawer='compose']").hasClass("is-hidden")){
$(document).trigger("uiDrawerShowDrawer", {
drawer: "compose",
withAnimation: true
});
window.setTimeout(() => $(this).trigger("click"), 1);
}
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
});
// //
// Block: Work around clipboard HTML formatting. // Block: Work around clipboard HTML formatting.
// //
@@ -617,12 +637,18 @@
styleOfficial.sheet.insertRule(".app-navigator .tooltip { display: none !important; }", 0); // hide broken tooltips in the menu styleOfficial.sheet.insertRule(".app-navigator .tooltip { display: none !important; }", 0); // hide broken tooltips in the menu
styleOfficial.sheet.insertRule(".account-inline .username { vertical-align: 10%; }", 0); // move usernames a bit higher styleOfficial.sheet.insertRule(".account-inline .username { vertical-align: 10%; }", 0); // move usernames a bit higher
styleOfficial.sheet.insertRule(".column .column-header { height: 49px !important; }", 0); // fix one pixel space below column header
styleOfficial.sheet.insertRule(".column:not(.is-options-open) .column-header { border-bottom: none; }", 0); // fix one pixel space below column header
styleOfficial.sheet.insertRule(".activity-header { align-items: center !important; margin-bottom: 4px; }", 0); // tweak alignment of avatar and text in notifications styleOfficial.sheet.insertRule(".activity-header { align-items: center !important; margin-bottom: 4px; }", 0); // tweak alignment of avatar and text in notifications
styleOfficial.sheet.insertRule(".activity-header .tweet-timestamp { line-height: unset }", 0); // fix timestamp position in notifications styleOfficial.sheet.insertRule(".activity-header .tweet-timestamp { line-height: unset }", 0); // fix timestamp position in notifications
styleOfficial.sheet.insertRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }", 0); // remove weird border in the column container scrollbar styleOfficial.sheet.insertRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }", 0); // remove weird border in the column container scrollbar
styleOfficial.sheet.insertRule(".app-columns-container { bottom: 0 !important; }", 0); // move column container scrollbar to bottom to fit updated style styleOfficial.sheet.insertRule(".app-columns-container { bottom: 0 !important; }", 0); // move column container scrollbar to bottom to fit updated style
styleOfficial.sheet.insertRule(".js-column-header .column-header-link { padding: 0; }", 0); // fix column header tooltip hover box
styleOfficial.sheet.insertRule(".js-column-header .column-header-link .icon { padding: 9px 4px; width: calc(1em + 8px); height: 100%; box-sizing: border-box; }", 0); // fix column header tooltip hover box
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']), .is-gif .js-media-gif-container { cursor: alias; }", 0); // change cursor on unsupported videos styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']), .is-gif .js-media-gif-container { cursor: alias; }", 0); // change cursor on unsupported videos
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #bd3d37; }", 0); // change play icon color on unsupported videos styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #bd3d37; }", 0); // change play icon color on unsupported videos

View File

@@ -1,10 +1,12 @@
(function(){ (function(){
var isReloading = false;
// //
// Class: Abstract plugin base class. // Class: Abstract plugin base class.
// //
window.PluginBase = class{ window.PluginBase = class{
constructor(pluginSettings){ constructor(pluginSettings){
this.$pluginSettings = pluginSettings || {}; this.$requiresReload = !!(pluginSettings && pluginSettings.requiresPageReload);
} }
enabled(){} enabled(){}
@@ -49,11 +51,11 @@
} }
setState(plugin, enable){ setState(plugin, enable){
let reloading = plugin.obj.$pluginSettings.requiresPageReload; let reloading = plugin.obj.$requiresReload;
if (enable && this.isDisabled(plugin)){ if (enable && this.isDisabled(plugin)){
if (reloading){ if (reloading){
location.reload(); window.TDPF_requestReload();
} }
else{ else{
this.disabled.splice(this.disabled.indexOf(plugin.id), 1); this.disabled.splice(this.disabled.indexOf(plugin.id), 1);
@@ -63,11 +65,17 @@
} }
else if (!enable && !this.isDisabled(plugin)){ else if (!enable && !this.isDisabled(plugin)){
if (reloading){ if (reloading){
location.reload(); window.TDPF_requestReload();
} }
else{ else{
this.disabled.push(plugin.id); this.disabled.push(plugin.id);
plugin.obj.disabled(); plugin.obj.disabled();
for(let key of Object.keys(plugin.obj)){
if (key[0] !== '$'){
delete plugin.obj[key];
}
}
} }
} }
} }
@@ -84,4 +92,14 @@
window.TDPF_setPluginState = function(identifier, enable){ window.TDPF_setPluginState = function(identifier, enable){
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable); window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
}; };
//
// Block: Setup global function to reload the page.
//
window.TDPF_requestReload = function(){
if (!isReloading){
window.setTimeout(() => location.reload(), 1);
isReloading = true;
}
};
})(); })();

View File

@@ -1,12 +1,8 @@
(function(){
// //
// Class: Abstract plugin base class. // Class: Abstract plugin base class.
// //
window.PluginBase = class{ window.PluginBase = class{
constructor(pluginSettings){ constructor(){}
this.$pluginSettings = pluginSettings || {};
}
run(){} run(){}
}; };
@@ -18,4 +14,3 @@
plugin.obj.run(); plugin.obj.run();
} }
}; };
})();

View File

@@ -123,10 +123,6 @@
<DependentUpon>FormNotificationTweet.cs</DependentUpon> <DependentUpon>FormNotificationTweet.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Notification\SoundNotification.cs" /> <Compile Include="Core\Notification\SoundNotification.cs" />
<Compile Include="Core\Notification\Sound\SoundPlayerImplFallback.cs" />
<Compile Include="Core\Notification\Sound\SoundPlayerImplWMP.cs" />
<Compile Include="Core\Notification\Sound\ISoundNotificationPlayer.cs" />
<Compile Include="Core\Notification\Sound\PlaybackErrorEventArgs.cs" />
<Compile Include="Core\Notification\TweetNotification.cs" /> <Compile Include="Core\Notification\TweetNotification.cs" />
<Compile Include="Core\Other\FormAbout.cs"> <Compile Include="Core\Other\FormAbout.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
@@ -208,7 +204,6 @@
</Compile> </Compile>
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" /> <Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
<Compile Include="Core\Utils\InjectedHTML.cs" /> <Compile Include="Core\Utils\InjectedHTML.cs" />
<Compile Include="Core\Utils\NativeCoreAudio.cs" />
<Compile Include="Core\Utils\TwoKeyDictionary.cs" /> <Compile Include="Core\Utils\TwoKeyDictionary.cs" />
<Compile Include="Core\Utils\WindowState.cs" /> <Compile Include="Core\Utils\WindowState.cs" />
<Compile Include="Core\Utils\WindowsUtils.cs" /> <Compile Include="Core\Utils\WindowsUtils.cs" />
@@ -340,17 +335,10 @@
<Content Include="Resources\Scripts\update.js" /> <Content Include="Resources\Scripts\update.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<COMReference Include="WMPLib"> <ProjectReference Include="lib\TweetLib.Audio\TweetLib.Audio.csproj">
<Guid>{6BF52A50-394A-11D3-B153-00C04F79FAA6}</Guid> <Project>{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}</Project>
<VersionMajor>1</VersionMajor> <Name>TweetLib.Audio</Name>
<VersionMinor>0</VersionMinor> </ProjectReference>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj"> <ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project> <Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
<Name>TweetDuck.Browser</Name> <Name>TweetDuck.Browser</Name>
@@ -362,7 +350,7 @@
xcopy "$(ProjectDir)LICENSE.md" "$(TargetDir)" /Y xcopy "$(ProjectDir)LICENSE.md" "$(TargetDir)" /Y
del "$(TargetDir)LICENSE.txt" del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE.md" "LICENSE.txt" ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y xcopy "$(ProjectDir)bld\Resources\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y
rmdir "$(TargetDir)scripts" /S /Q rmdir "$(TargetDir)scripts" /S /Q
@@ -373,8 +361,8 @@ mkdir "$(TargetDir)plugins"
mkdir "$(TargetDir)plugins\official" mkdir "$(TargetDir)plugins\official"
mkdir "$(TargetDir)plugins\user" mkdir "$(TargetDir)plugins\user"
xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
rmdir "$(ProjectDir)\bin\Debug" rmdir "$(ProjectDir)bin\Debug"
rmdir "$(ProjectDir)\bin\Release" rmdir "$(ProjectDir)bin\Release"
rmdir "$(TargetDir)plugins\official\.debug" /S /Q rmdir "$(TargetDir)plugins\official\.debug" /S /Q

View File

@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26430.6 VisualStudioVersion = 15.0.26430.12
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
EndProject EndProject
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "subpro
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Audio", "lib\TweetLib.Audio\TweetLib.Audio.csproj", "{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
@@ -26,6 +28,10 @@ Global
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Debug|x86.ActiveCfg = Debug|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Debug|x86.Build.0 = Debug|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Release|x86.ActiveCfg = Release|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -39,7 +39,7 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
[Files] [Files]
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,d3dcompiler_43.dll,widevinecdmadapter.dll" Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,widevinecdmadapter.dll"
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
@@ -48,13 +48,10 @@ Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks:
[Run] [Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
[InstallDelete]
Type: files; Name: "{app}\td-log.txt"
Type: filesandordirs; Name: "{app}\plugins\official\design-revert"
Type: filesandordirs; Name: "{localappdata}\TD_Plugins\official\design-revert"
[UninstallDelete] [UninstallDelete]
Type: files; Name: "{app}\debug.log" Type: files; Name: "{app}\*.*"
Type: filesandordirs; Name: "{app}\locales"
Type: filesandordirs; Name: "{app}\scripts"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache" Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache" Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"

View File

@@ -36,15 +36,11 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Files] [Files]
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,d3dcompiler_43.dll,widevinecdmadapter.dll" Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,widevinecdmadapter.dll"
[Run] [Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
[InstallDelete]
Type: filesandordirs; Name: "{app}\plugins\official\design-revert"
Type: filesandordirs; Name: "{app}\portable\storage\TD_Plugins\official\design-revert"
[Code] [Code]
var UpdatePath: String; var UpdatePath: String;

View File

@@ -29,9 +29,9 @@ Uninstallable=TDIsUninstallable
UninstallDisplayName={#MyAppName} UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName} UninstallDisplayIcon={app}\{#MyAppExeName}
PrivilegesRequired=lowest PrivilegesRequired=lowest
Compression=lzma Compression=lzma/normal
SolidCompression=yes SolidCompression=True
InternalCompressLevel=max InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss> #include <idp.iss>
@@ -41,7 +41,10 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Files] [Files]
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,*.dll,*.pak,*.bin,*.dat,CefSharp.BrowserSubprocess.exe" Source: "..\bin\x86\Release\TweetDuck.*"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\TweetLib.*"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\scripts\*.*"; DestDir: "{app}\scripts"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\bin\x86\Release\plugins\*.*"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
@@ -49,17 +52,6 @@ Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUnin
[Run] [Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Parameters: "{code:TDGetRunArgs}"; Flags: nowait postinstall shellexec Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Parameters: "{code:TDGetRunArgs}"; Flags: nowait postinstall shellexec
[InstallDelete]
Type: files; Name: "{app}\*.xml"
Type: files; Name: "{app}\*.js"
Type: files; Name: "{app}\d3dcompiler_43.dll"
Type: files; Name: "{app}\widevinecdmadapter.dll"
Type: files; Name: "{app}\devtools_resources.pak"
Type: files; Name: "{app}\CefSharp.BrowserSubprocess.exe"
Type: files; Name: "{app}\td-log.txt"
Type: files; Name: "{app}\debug.log"
Type: files; Name: "{localappdata}\{#MyAppName}\ChromeDWriteFontCache"
[UninstallDelete] [UninstallDelete]
Type: files; Name: "{app}\*.*" Type: files; Name: "{app}\*.*"
Type: filesandordirs; Name: "{app}\locales" Type: filesandordirs; Name: "{app}\locales"

View File

@@ -0,0 +1,37 @@
using System;
using System.Runtime.InteropServices;
using TweetLib.Audio.Impl;
using TweetLib.Audio.Utils;
namespace TweetLib.Audio{
public abstract class AudioPlayer : IDisposable{
private static bool? IsWMPAvailable;
public static AudioPlayer New(){
if (IsWMPAvailable.HasValue){
if (IsWMPAvailable.Value){
return new SoundPlayerImplWMP();
}
else{
return new SoundPlayerImplFallback();
}
}
try{
SoundPlayerImplWMP implWMP = new SoundPlayerImplWMP();
IsWMPAvailable = true;
return implWMP;
}catch(COMException){
IsWMPAvailable = false;
return new SoundPlayerImplFallback();
}
}
public abstract string SupportedFormats { get; }
public abstract event EventHandler<PlaybackErrorEventArgs> PlaybackError;
public abstract void Play(string file);
public abstract void Stop();
public abstract void Dispose();
}
}

View File

@@ -1,12 +1,13 @@
using System; using System;
using System.IO; using System.IO;
using System.Media; using System.Media;
using TweetLib.Audio.Utils;
namespace TweetDuck.Core.Notification.Sound{ namespace TweetLib.Audio.Impl{
sealed class SoundPlayerImplFallback : ISoundNotificationPlayer{ sealed class SoundPlayerImplFallback : AudioPlayer{
string ISoundNotificationPlayer.SupportedFormats => "*.wav"; public override string SupportedFormats => "*.wav";
public event EventHandler<PlaybackErrorEventArgs> PlaybackError; public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly SoundPlayer player; private readonly SoundPlayer player;
private bool ignorePlaybackError; private bool ignorePlaybackError;
@@ -17,7 +18,7 @@ namespace TweetDuck.Core.Notification.Sound{
}; };
} }
void ISoundNotificationPlayer.Play(string file){ public override void Play(string file){
if (player.SoundLocation != file){ if (player.SoundLocation != file){
player.SoundLocation = file; player.SoundLocation = file;
ignorePlaybackError = false; ignorePlaybackError = false;
@@ -34,11 +35,11 @@ namespace TweetDuck.Core.Notification.Sound{
} }
} }
void ISoundNotificationPlayer.Stop(){ public override void Stop(){
player.Stop(); player.Stop();
} }
void IDisposable.Dispose(){ public override void Dispose(){
player.Dispose(); player.Dispose();
} }

View File

@@ -1,13 +1,13 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TweetDuck.Core.Utils; using TweetLib.Audio.Utils;
using WMPLib; using WMPLib;
namespace TweetDuck.Core.Notification.Sound{ namespace TweetLib.Audio.Impl{
sealed class SoundPlayerImplWMP : ISoundNotificationPlayer{ sealed class SoundPlayerImplWMP : AudioPlayer{
string ISoundNotificationPlayer.SupportedFormats => "*.wav;*.mp3;*.mp2;*.m4a;*.mid;*.midi;*.rmi;*.wma;*.aif;*.aifc;*.aiff;*.snd;*.au"; public override string SupportedFormats => "*.wav;*.mp3;*.mp2;*.m4a;*.mid;*.midi;*.rmi;*.wma;*.aif;*.aifc;*.aiff;*.snd;*.au";
public event EventHandler<PlaybackErrorEventArgs> PlaybackError; public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly WindowsMediaPlayer player; private readonly WindowsMediaPlayer player;
private bool wasTryingToPlay; private bool wasTryingToPlay;
@@ -29,7 +29,7 @@ namespace TweetDuck.Core.Notification.Sound{
player.MediaError += player_MediaError; player.MediaError += player_MediaError;
} }
void ISoundNotificationPlayer.Play(string file){ public override void Play(string file){
wasTryingToPlay = true; wasTryingToPlay = true;
try{ try{
@@ -48,7 +48,7 @@ namespace TweetDuck.Core.Notification.Sound{
} }
} }
void ISoundNotificationPlayer.Stop(){ public override void Stop(){
try{ try{
player.controls.stop(); player.controls.stop();
}catch{ }catch{
@@ -56,7 +56,7 @@ namespace TweetDuck.Core.Notification.Sound{
} }
} }
void IDisposable.Dispose(){ public override void Dispose(){
player.close(); player.close();
Marshal.ReleaseComObject(player); Marshal.ReleaseComObject(player);
} }

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TweetDuck Audio Library")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TweetDuck Audio Library")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e9e1fd1b-f480-45b7-9970-be2ecfd309ac")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TweetLib.Audio</RootNamespace>
<AssemblyName>TweetLib.Audio</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ResolveComReferenceSilent>True</ResolveComReferenceSilent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="AudioPlayer.cs" />
<Compile Include="Utils\NativeCoreAudio.cs" />
<Compile Include="Utils\PlaybackErrorEventArgs.cs" />
<Compile Include="Impl\SoundPlayerImplFallback.cs" />
<Compile Include="Impl\SoundPlayerImplWMP.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<COMReference Include="WMPLib">
<Guid>{6BF52A50-394A-11D3-B153-00C04F79FAA6}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace TweetDuck.Core.Utils{ namespace TweetLib.Audio.Utils{
static class NativeCoreAudio{ static class NativeCoreAudio{
private const int EDATAFLOW_RENDER = 0; private const int EDATAFLOW_RENDER = 0;
private const int EROLE_MULTIMEDIA = 1; private const int EROLE_MULTIMEDIA = 1;

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace TweetDuck.Core.Notification.Sound{ namespace TweetLib.Audio.Utils{
sealed class PlaybackErrorEventArgs : EventArgs{ public sealed class PlaybackErrorEventArgs : EventArgs{
public string Message { get; } public string Message { get; }
public bool Ignore { get; set; } public bool Ignore { get; set; }

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.1.0")]

View File

@@ -35,7 +35,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
</PostBuildEvent> editbin /largeaddressaware /TSAWARE "$(TargetPath)"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -14,7 +14,6 @@ namespace UnitTests.Core.Utils{
foreach(var pos in Positions){ foreach(var pos in Positions){
Assert.AreEqual(string.Empty, new InjectedHTML(pos, "b", "b").Inject(string.Empty)); Assert.AreEqual(string.Empty, new InjectedHTML(pos, "b", "b").Inject(string.Empty));
Assert.AreEqual("aaaa", new InjectedHTML(pos, "b", "b").Inject("aaaa")); Assert.AreEqual("aaaa", new InjectedHTML(pos, "b", "b").Inject("aaaa"));
} }
} }