mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
1a31e69ec9 | |||
e065983c95 | |||
30a169171a | |||
8d1900362e | |||
e154189de1 | |||
b0f147de24 | |||
979b3548db | |||
05d6c578b3 | |||
a117559063 | |||
f87c649b09 | |||
6504dc9184 | |||
25a8ddffd4 | |||
fa0f9b89cf | |||
4d00a67891 | |||
bd2c43e1f4 | |||
c7279eaa34 | |||
fd523e552c | |||
cb877b8b23 | |||
ed1bee8b89 | |||
a8e1492056 | |||
5587216c01 | |||
86569261ad | |||
4a9049c7aa | |||
75d60a8182 | |||
14d4dc2ed9 | |||
fd0e1740a5 | |||
70ca890bef | |||
b9318dfd8e | |||
995642a719 | |||
d14de4ac9e | |||
b7f325a241 | |||
27c55718c2 | |||
421ff0654b | |||
ed947458f9 | |||
9cdb20ba84 | |||
d8774b735f | |||
adcb42695f | |||
dd77b5bcbb | |||
d2445be155 | |||
10254c8af7 | |||
d7e830badf | |||
b445a3a9b8 | |||
97f42ead66 | |||
03730fafb9 | |||
0be9465dca | |||
d7f1df4995 | |||
3cb0f90706 | |||
a3e9b15a8a | |||
00bfa68a57 | |||
c311e24f08 | |||
1cdd4e4455 | |||
8078c0081a | |||
a867e1fc40 | |||
61da36ac1c | |||
720ca2a018 | |||
b39c593552 | |||
c808952a45 | |||
b468d7a766 | |||
28578f60be | |||
92a39e2527 |
@@ -6,6 +6,8 @@ namespace TweetDuck.Configuration{
|
|||||||
// public args
|
// public args
|
||||||
public const string ArgDataFolder = "-datafolder";
|
public const string ArgDataFolder = "-datafolder";
|
||||||
public const string ArgLogging = "-log";
|
public const string ArgLogging = "-log";
|
||||||
|
public const string ArgIgnoreGDPR = "-nogdpr";
|
||||||
|
public const string ArgNotificationScrollWA = "-nscrollwa";
|
||||||
|
|
||||||
// internal args
|
// internal args
|
||||||
public const string ArgRestart = "-restart";
|
public const string ArgRestart = "-restart";
|
||||||
|
@@ -64,6 +64,10 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetRightClickedLink(string type, string url){
|
||||||
|
ContextInfo.SetLink(type, url);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||||
ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||||
}
|
}
|
||||||
@@ -99,10 +103,6 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
// Global
|
// Global
|
||||||
|
|
||||||
public void SetLastRightClickInfo(string type, string url){
|
|
||||||
ContextInfo.SetLink(type, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||||
notification.InvokeAsyncSafe(() => {
|
notification.InvokeAsyncSafe(() => {
|
||||||
form.OnTweetNotification();
|
form.OnTweetNotification();
|
||||||
|
6
Core/FormBrowser.Designer.cs
generated
6
Core/FormBrowser.Designer.cs
generated
@@ -39,17 +39,17 @@
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
||||||
this.ClientSize = new System.Drawing.Size(400, 386);
|
this.ClientSize = new System.Drawing.Size(1008, 730);
|
||||||
this.Icon = Properties.Resources.icon;
|
this.Icon = Properties.Resources.icon;
|
||||||
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||||
this.MinimumSize = new System.Drawing.Size(416, 424);
|
this.MinimumSize = new System.Drawing.Size(348, 424);
|
||||||
this.Name = "FormBrowser";
|
this.Name = "FormBrowser";
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
|
||||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
|
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
|
||||||
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
|
|
||||||
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
|
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
|
||||||
|
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
|
||||||
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
|
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
@@ -393,7 +393,7 @@ namespace TweetDuck.Core{
|
|||||||
FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
|
FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
|
||||||
|
|
||||||
form.FormClosed += (sender, args) => {
|
form.FormClosed += (sender, args) => {
|
||||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck && !UpdateHandler.TemporarilyForceUpdateChecking){
|
||||||
Config.DismissedUpdate = null;
|
Config.DismissedUpdate = null;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
||||||
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
||||||
private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508;
|
private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508;
|
||||||
|
private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509;
|
||||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
protected ContextInfo.LinkInfo LastLink { get; private set; }
|
protected ContextInfo.LinkInfo LastLink { get; private set; }
|
||||||
@@ -57,6 +58,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
||||||
model.AddItem(MenuSearchInBrowser, "Search in browser");
|
model.AddItem(MenuSearchInBrowser, "Search in browser");
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
|
model.AddItem(MenuReadApplyROT13, "Apply ROT13");
|
||||||
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasTweetImage = LastLink.IsImage;
|
bool hasTweetImage = LastLink.IsImage;
|
||||||
@@ -126,7 +129,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuViewImage:
|
case MenuViewImage: {
|
||||||
void ViewImage(string path){
|
void ViewImage(string path){
|
||||||
string ext = Path.GetExtension(path);
|
string ext = Path.GetExtension(path);
|
||||||
|
|
||||||
@@ -141,11 +144,12 @@ namespace TweetDuck.Core.Handling{
|
|||||||
string url = LastLink.GetMediaSource(parameters);
|
string url = LastLink.GetMediaSource(parameters);
|
||||||
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
|
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
|
||||||
|
|
||||||
|
control.InvokeAsyncSafe(() => {
|
||||||
if (File.Exists(file)){
|
if (File.Exists(file)){
|
||||||
ViewImage(file);
|
ViewImage(file);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger);
|
analytics.AnalyticsFile.ViewedImages.Trigger();
|
||||||
|
|
||||||
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
|
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
|
||||||
ViewImage(file);
|
ViewImage(file);
|
||||||
@@ -153,25 +157,47 @@ namespace TweetDuck.Core.Handling{
|
|||||||
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MenuSaveMedia:
|
case MenuSaveMedia: {
|
||||||
if (LastLink.IsVideo){
|
bool isVideo = LastLink.IsVideo;
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
|
string url = LastLink.GetMediaSource(parameters);
|
||||||
TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault());
|
string username = LastChirp.Authors.LastOrDefault();
|
||||||
|
|
||||||
|
control.InvokeAsyncSafe(() => {
|
||||||
|
if (isVideo){
|
||||||
|
TwitterUtils.DownloadVideo(url, username);
|
||||||
|
analytics.AnalyticsFile.DownloadedVideos.Trigger();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
TwitterUtils.DownloadImage(url, username, ImageQuality);
|
||||||
TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality);
|
analytics.AnalyticsFile.DownloadedImages.Trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
case MenuSaveTweetImages: {
|
||||||
|
string[] urls = LastChirp.Images;
|
||||||
|
string username = LastChirp.Authors.LastOrDefault();
|
||||||
|
|
||||||
|
control.InvokeAsyncSafe(() => {
|
||||||
|
TwitterUtils.DownloadImages(urls, username, ImageQuality);
|
||||||
|
analytics.AnalyticsFile.DownloadedImages.Trigger();
|
||||||
|
});
|
||||||
|
|
||||||
case MenuSaveTweetImages:
|
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
|
||||||
TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MenuReadApplyROT13:
|
||||||
|
string selection = parameters.SelectionText;
|
||||||
|
control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK));
|
||||||
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.UsedROT13.Trigger);
|
||||||
|
return true;
|
||||||
|
|
||||||
case MenuSearchInBrowser:
|
case MenuSearchInBrowser:
|
||||||
string query = parameters.SelectionText;
|
string query = parameters.SelectionText;
|
||||||
|
@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
||||||
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
||||||
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
||||||
private const CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615;
|
private const CefMenuCommand MenuWriteApplyROT13 = (CefMenuCommand)26615;
|
||||||
private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616;
|
private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616;
|
||||||
|
|
||||||
private const string TitleReloadBrowser = "Reload browser";
|
private const string TitleReloadBrowser = "Reload browser";
|
||||||
@@ -44,7 +44,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
if (isSelecting){
|
if (isSelecting){
|
||||||
if (isEditing){
|
if (isEditing){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
|
model.AddItem(MenuWriteApplyROT13, "Apply ROT13");
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
@@ -141,7 +141,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
SetClipboardText(form, LastChirp.QuoteUrl);
|
SetClipboardText(form, LastChirp.QuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuInputApplyROT13:
|
case MenuWriteApplyROT13:
|
||||||
form.InvokeAsyncSafe(form.ApplyROT13);
|
form.InvokeAsyncSafe(form.ApplyROT13);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@@ -4,11 +4,19 @@ using CefSharp.Enums;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
sealed class DragHandlerBrowser : IDragHandler{
|
sealed class DragHandlerBrowser : IDragHandler{
|
||||||
|
private readonly RequestHandlerBrowser requestHandler;
|
||||||
|
|
||||||
|
public DragHandlerBrowser(RequestHandlerBrowser requestHandler){
|
||||||
|
this.requestHandler = requestHandler;
|
||||||
|
}
|
||||||
|
|
||||||
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
|
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
|
||||||
void TriggerDragStart(string type, string data = null){
|
void TriggerDragStart(string type, string data = null){
|
||||||
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
|
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestHandler.BlockNextUserNavUrl = dragData.LinkUrl; // empty if not a link
|
||||||
|
|
||||||
if (dragData.IsLink){
|
if (dragData.IsLink){
|
||||||
TriggerDragStart("link", dragData.LinkUrl);
|
TriggerDragStart("link", dragData.LinkUrl);
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,12 @@ using TweetDuck.Core.Handling.General;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class RequestHandlerBase : DefaultRequestHandler{
|
class RequestHandlerBase : DefaultRequestHandler{
|
||||||
|
private readonly bool autoReload;
|
||||||
|
|
||||||
|
public RequestHandlerBase(bool autoReload){
|
||||||
|
this.autoReload = autoReload;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
|
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
|
||||||
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||||
}
|
}
|
||||||
@@ -18,5 +24,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||||
|
if (autoReload){
|
||||||
|
browser.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
using CefSharp;
|
using System.Text.RegularExpressions;
|
||||||
|
using CefSharp;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
private static readonly Regex TmpResourceRedirect = new Regex(@"dist\/(.*?)\.(.*?)\.js$", RegexOptions.Compiled);
|
||||||
browser.Reload();
|
|
||||||
}
|
public string BlockNextUserNavUrl { get; set; }
|
||||||
|
|
||||||
|
public RequestHandlerBrowser() : base(true){}
|
||||||
|
|
||||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
|
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
|
||||||
@@ -16,11 +19,45 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){
|
||||||
|
if (userGesture && request.TransitionType == TransitionType.LinkClicked){
|
||||||
|
bool block = request.Url == BlockNextUserNavUrl;
|
||||||
|
BlockNextUserNavUrl = string.Empty;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
if (request.ResourceType == ResourceType.Image && request.Url.Contains("backgrounds/spinner_blue")){
|
if (request.ResourceType == ResourceType.Image && request.Url.Contains("backgrounds/spinner_blue")){
|
||||||
request.Url = TwitterUtils.LoadingSpinner.Url;
|
request.Url = TwitterUtils.LoadingSpinner.Url;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (request.ResourceType == ResourceType.Script){ // TODO delaying the apocalypse
|
||||||
|
Match match = TmpResourceRedirect.Match(request.Url);
|
||||||
|
|
||||||
|
if (match.Success){
|
||||||
|
string scriptType = match.Groups[1].Value;
|
||||||
|
string scriptHash = match.Groups[2].Value;
|
||||||
|
|
||||||
|
const string HashBundle = "1bd75b5854";
|
||||||
|
const string HashVendor = "942c0a20e8";
|
||||||
|
|
||||||
|
if (scriptType == "bundle" && scriptHash != HashBundle){
|
||||||
|
request.Url = TmpResourceRedirect.Replace(request.Url, "dist/$1."+HashBundle+".js");
|
||||||
|
System.Diagnostics.Debug.WriteLine("rewriting "+scriptType+" to "+request.Url);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (scriptType == "vendor" && scriptHash != HashVendor){
|
||||||
|
request.Url = TmpResourceRedirect.Replace(request.Url, "dist/$1."+HashVendor+".js");
|
||||||
|
System.Diagnostics.Debug.WriteLine("rewriting "+scriptType+" to "+request.Url);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine("accepting "+scriptType+" as "+request.Url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||||
}
|
}
|
||||||
|
@@ -124,7 +124,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBase()
|
RequestHandler = new RequestHandlerBase(false)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
@@ -65,7 +66,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Program.UserConfig.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel));
|
return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel));
|
||||||
|
|
||||||
case TweetNotification.Size.Custom:
|
case TweetNotification.Size.Custom:
|
||||||
return Program.UserConfig.CustomNotificationSize.Height;
|
return Program.UserConfig.CustomNotificationSize.Height;
|
||||||
@@ -136,7 +137,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
int eventType = wParam.ToInt32();
|
int eventType = wParam.ToInt32();
|
||||||
|
|
||||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
||||||
|
if (Arguments.HasFlag(Arguments.ArgNotificationScrollWA)){
|
||||||
|
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01);
|
||||||
|
browser.ExecuteScriptAsync("window.scrollBy", 0, -Math.Round(delta/0.72));
|
||||||
|
}
|
||||||
|
else{
|
||||||
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
|
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
|
||||||
|
}
|
||||||
|
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
||||||
@@ -255,7 +263,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
string html = base.GetTweetHTML(tweet);
|
string html = base.GetTweetHTML(tweet);
|
||||||
|
|
||||||
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||||
html = injection.Inject(html);
|
html = injection.InjectInto(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
|
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
}
|
}
|
||||||
|
|
||||||
using(IFrame frame = args.Browser.MainFrame){
|
using(IFrame frame = args.Browser.MainFrame){
|
||||||
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()), "screenshot");
|
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
string html = tweet.GenerateHtml("td-screenshot");
|
string html = tweet.GenerateHtml("td-screenshot");
|
||||||
|
|
||||||
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||||
html = injection.Inject(html);
|
html = injection.InjectInto(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
@@ -69,6 +69,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!WindowsUtils.IsAeroEnabled){
|
||||||
|
MoveToVisibleLocation(); // TODO make this look nicer I guess
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr context = NativeMethods.GetDC(this.Handle);
|
IntPtr context = NativeMethods.GetDC(this.Handle);
|
||||||
|
|
||||||
if (context == IntPtr.Zero){
|
if (context == IntPtr.Zero){
|
||||||
|
@@ -27,6 +27,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
#if GEN_SCREENSHOT_FRAMES
|
#if GEN_SCREENSHOT_FRAMES
|
||||||
private readonly Timer debugger;
|
private readonly Timer debugger;
|
||||||
private int frameCounter;
|
private int frameCounter;
|
||||||
|
|
||||||
|
public const int WaitFrames = 60;
|
||||||
|
#else
|
||||||
|
public const int WaitFrames = 5;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private FormNotificationScreenshotable screenshot;
|
private FormNotificationScreenshotable screenshot;
|
||||||
|
@@ -7,7 +7,7 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
sealed class TweetNotification{
|
sealed class TweetNotification{
|
||||||
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
|
private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
|
||||||
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty;
|
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty;
|
||||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
||||||
|
|
||||||
|
@@ -69,7 +69,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
MenuHandler = new ContextMenuGuide(owner),
|
MenuHandler = new ContextMenuGuide(owner),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
RequestHandler = new RequestHandlerBase(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
@@ -4,6 +4,7 @@ using System.Text;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
@@ -39,14 +40,16 @@ namespace TweetDuck.Core{
|
|||||||
private string prevSoundNotificationPath = null;
|
private string prevSoundNotificationPath = null;
|
||||||
|
|
||||||
public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){
|
public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){
|
||||||
|
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
||||||
DialogHandler = new FileDialogHandler(),
|
DialogHandler = new FileDialogHandler(),
|
||||||
DragHandler = new DragHandlerBrowser(),
|
DragHandler = new DragHandlerBrowser(requestHandler),
|
||||||
MenuHandler = new ContextMenuBrowser(owner),
|
MenuHandler = new ContextMenuBrowser(owner),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
RequestHandler = requestHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
@@ -154,6 +157,10 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
|
if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){
|
||||||
|
ScriptLoader.ExecuteScript(frame, @"TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
|
||||||
|
}
|
||||||
|
|
||||||
if (Program.UserConfig.FirstRun){
|
if (Program.UserConfig.FirstRun){
|
||||||
ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
|
ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
|
||||||
}
|
}
|
||||||
|
@@ -71,6 +71,9 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||||
|
|
||||||
|
[DllImport("dwmapi.dll")]
|
||||||
|
public static extern int DwmIsCompositionEnabled(out bool enabled);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||||
|
@@ -19,11 +19,23 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ConvertRot13(string str){
|
||||||
|
return Regex.Replace(str, @"[a-zA-Z]", match => {
|
||||||
|
int code = match.Value[0];
|
||||||
|
int start = code <= 90 ? 65 : 97;
|
||||||
|
return ((char)(start+(code-start+13)%26)).ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static int CountOccurrences(string source, string substring){
|
public static int CountOccurrences(string source, string substring){
|
||||||
int count = 0, index = 0;
|
if (substring.Length == 0){
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(substring), "Searched substring must not be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0, index = 0, length = substring.Length;
|
||||||
|
|
||||||
while((index = source.IndexOf(substring, index)) != -1){
|
while((index = source.IndexOf(substring, index)) != -1){
|
||||||
index += substring.Length;
|
index += length;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,10 +15,12 @@ namespace TweetDuck.Core.Utils{
|
|||||||
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
|
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
|
||||||
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
||||||
|
|
||||||
|
private static readonly bool IsWindows8OrNewer;
|
||||||
|
private static bool HasMicrosoftBeenBroughtTo2008Yet;
|
||||||
|
|
||||||
public static int CurrentProcessID { get; }
|
public static int CurrentProcessID { get; }
|
||||||
public static bool ShouldAvoidToolWindow { get; }
|
public static bool ShouldAvoidToolWindow { get; }
|
||||||
|
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
|
||||||
private static bool HasMicrosoftBeenBroughtTo2008Yet;
|
|
||||||
|
|
||||||
static WindowsUtils(){
|
static WindowsUtils(){
|
||||||
using(Process me = Process.GetCurrentProcess()){
|
using(Process me = Process.GetCurrentProcess()){
|
||||||
@@ -26,7 +28,9 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version ver = Environment.OSVersion.Version;
|
Version ver = Environment.OSVersion.Version;
|
||||||
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
||||||
|
|
||||||
|
ShouldAvoidToolWindow = IsWindows8OrNewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnsureTLS12(){
|
public static void EnsureTLS12(){
|
||||||
|
@@ -16,7 +16,7 @@ namespace TweetDuck.Data{
|
|||||||
this.html = html;
|
this.html = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Inject(string targetHTML){
|
public string InjectInto(string targetHTML){
|
||||||
int index = targetHTML.IndexOf(search, StringComparison.Ordinal);
|
int index = targetHTML.IndexOf(search, StringComparison.Ordinal);
|
||||||
|
|
||||||
if (index == -1){
|
if (index == -1){
|
||||||
|
@@ -84,14 +84,20 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
string folderPathName = new DirectoryInfo(rootFolder).FullName;
|
string folderPathName = new DirectoryInfo(rootFolder).FullName;
|
||||||
DirectoryInfo currentInfo = new DirectoryInfo(fullPath);
|
DirectoryInfo currentInfo = new DirectoryInfo(fullPath); // initially points to the file, which is convenient for the Attributes check below
|
||||||
|
DirectoryInfo parentInfo = currentInfo.Parent;
|
||||||
|
|
||||||
while(currentInfo.Parent != null){
|
while(parentInfo != null){
|
||||||
if (currentInfo.Parent.FullName == folderPathName){
|
if (currentInfo.Exists && currentInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)){
|
||||||
|
return string.Empty; // no reason why a plugin should have files/folders with symlinks, junctions, or any other crap
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentInfo.FullName == folderPathName){
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentInfo = currentInfo.Parent;
|
currentInfo = parentInfo;
|
||||||
|
parentInfo = currentInfo.Parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch{
|
catch{
|
||||||
|
@@ -19,7 +19,7 @@ namespace TweetDuck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.14";
|
public const string VersionTag = "1.14.4.1";
|
||||||
|
|
||||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
||||||
|
@@ -42,5 +42,6 @@ using TweetDuck;
|
|||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TweetTest.Unit")]
|
||||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
||||||
#endif
|
#endif
|
||||||
|
45
README.md
45
README.md
@@ -9,53 +9,48 @@
|
|||||||
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
||||||
* **.NET desktop development**
|
* **.NET desktop development**
|
||||||
* .NET Framework 4 – 4.6 development tools
|
* .NET Framework 4 – 4.6 development tools
|
||||||
|
* F# desktop language support
|
||||||
* **Desktop development with C++**
|
* **Desktop development with C++**
|
||||||
* VC++ 2017 v141 toolset
|
* VC++ 2017 latest v141 tools
|
||||||
|
|
||||||
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running this command in the **Package Manager Console**:
|
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Version 65.0.0-pre01
|
PM> Install-Package CefSharp.WinForms -Version 66.0.0-CI2629 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution.
|
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
|
||||||
|
|
||||||
To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure your **Configuration** is set to `Active (Debug)` (or just `Debug`), and insert this into the **Command line arguments** field:
|
While debugging, opening the main menu and clicking **Reload browser** automatically rebuilds all resources in the `Resources/Scripts` and `Resources/Plugins` folders. This allows editing HTML/CSS/JS files and applying the changes without restarting the program, but it will cause a short delay between browser reloads.
|
||||||
```
|
|
||||||
-datafolder TweetDuckDebug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build
|
### Release
|
||||||
|
|
||||||
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
|
Open **Batch Build**, tick all `Release` configurations with `x86` platform, and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
|
||||||
|
|
||||||
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
||||||
|
|
||||||
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
|
The `Release` configuration omits debug symbols and other unnecessary files. You can modify this behavior by opening `TweetDuck.csproj`, and editing the `<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">` section.
|
||||||
|
|
||||||
|
If you decide to publicly release a custom version, please make it clear that it is not an official release of TweetDuck. There are many references to the official website and this repository, especially in the update system, so search for `chylex.com` and `github.com` in all files and replace them appropriately.
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
There are a few quirks in the build process that may catch you off guard:
|
#### Error: The command (...) exited with code 1
|
||||||
|
- This indicates a failed post-build event, open the **Output** tab for logs
|
||||||
- **Plugin files are not updated automatically**
|
- Determine if there was an IO error from the `rmdir` commands, or whether the error was in the **PostBuild.ps1** script (`Encountered an error while running PostBuild.ps1 on line <xyz>`)
|
||||||
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
|
|
||||||
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
|
|
||||||
- **Error: The command (...) exited with code 1**
|
|
||||||
- If the post-build event fails, open the **Output** tab and look for the cause
|
|
||||||
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
|
|
||||||
- Some files are checked for invalid characters:
|
- Some files are checked for invalid characters:
|
||||||
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
||||||
|
|
||||||
### Installers
|
### Installers
|
||||||
|
|
||||||
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
||||||
|
|
||||||
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
||||||
|
|
||||||
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio!
|
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that this will only package the files, you still need to run the [release build](#release) in Visual Studio!
|
||||||
|
|
||||||
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
||||||
* **TweetDuck.exe**
|
* **TweetDuck.exe**
|
||||||
@@ -67,8 +62,10 @@ After the window closes, three installers will be generated inside the `bld/Outp
|
|||||||
* This is a portable installer that does not need administrator privileges
|
* This is a portable installer that does not need administrator privileges
|
||||||
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
|
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
|
||||||
|
|
||||||
Note: There is a small chance you will see a resource error when running `RUN BUILD.bat`. If that happens, close the console window (which will terminate all Inno Setup processes and leave corrupted installer files in the output folder), and run it again.
|
#### Notes
|
||||||
|
|
||||||
### Code Notes
|
> When opening **Batch Build**, you will also see `x64` and `AnyCPU` configurations. These are visible due to what I consider a Visual Studio bug, and will not work without significant changes to the project. Manually running the `Resources/PostCefUpdate.ps1` PowerShell script modifies the downloaded CefSharp packages, and removes the invalid configurations.
|
||||||
|
|
||||||
There are many references to the official TweetDuck website and this repository in the code and installers, so if you plan to release your own version, make sure to search for `tweetduck.chylex.com` and `github.com` in the whole repository and replace them appropriately.
|
> There is a small chance running `RUN BUILD.bat` immediately shows a resource error. If that happens, close the console window (which terminates all Inno Setup processes and leaves corrupted installers in the output folder), and run it again.
|
||||||
|
|
||||||
|
> Running `RUN BUILD.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`.
|
||||||
|
BIN
Resources/Design/icon_pushpin.afdesign
Normal file
BIN
Resources/Design/icon_pushpin.afdesign
Normal file
Binary file not shown.
@@ -82,12 +82,10 @@ enabled(){
|
|||||||
</a>`;
|
</a>`;
|
||||||
|
|
||||||
// add column buttons and keyboard shortcut info to UI
|
// add column buttons and keyboard shortcut info to UI
|
||||||
window.TDPF_injectMustache("column/column_header.mustache", "prepend", "</header>", `
|
window.TDPF_injectMustache("column/column_header.mustache", "prepend", "<a data-testid=\"optionsToggle\"", `
|
||||||
{{^isTemporary}}
|
<a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle">
|
||||||
<a class="column-header-link td-clear-column-shortcut" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">
|
|
||||||
<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></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}}`);
|
|
||||||
|
|
||||||
window.TDPF_injectMustache("keyboard_shortcut_list.mustache", "prepend", "</dl> <dl", `
|
window.TDPF_injectMustache("keyboard_shortcut_list.mustache", "prepend", "</dl> <dl", `
|
||||||
<dd class="keyboard-shortcut-definition" style="white-space:nowrap">
|
<dd class="keyboard-shortcut-definition" style="white-space:nowrap">
|
||||||
@@ -101,13 +99,12 @@ enabled(){
|
|||||||
// load custom style
|
// load custom style
|
||||||
var css = window.TDPF_createCustomStyle(this);
|
var css = window.TDPF_createCustomStyle(this);
|
||||||
css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
||||||
|
css.insert(".column-header-links { min-width: 51px !important; }");
|
||||||
|
css.insert("[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
|
||||||
css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
||||||
css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
||||||
css.insert(".column-title { margin-right: 60px !important; }");
|
css.insert("[data-action='td-clearcolumns-dosingle'] { padding: 3px 0 !important; }");
|
||||||
css.insert(".mark-all-read-link { right: 59px !important; }");
|
css.insert("[data-action='clear'].btn-options-tray { display: none !important; }");
|
||||||
css.insert(".open-compose-dm-link { right: 90px !important; }");
|
|
||||||
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
|
|
||||||
css.insert("[data-td-icon='icon-message'] .column-title { margin-right: 115px !important; }");
|
|
||||||
css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
|
css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
|
||||||
css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
|
css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
|
||||||
}
|
}
|
||||||
|
@@ -396,8 +396,10 @@ enabled(){
|
|||||||
switch(currentTheme){
|
switch(currentTheme){
|
||||||
case "black":
|
case "black":
|
||||||
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
|
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
|
||||||
|
this.css.insert(".column-header-temp { background-color: transparent !important }");
|
||||||
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
|
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
|
||||||
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
|
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
|
||||||
|
this.css.insert(".column-message.is-actionable span:hover > .icon-small-valigned { filter: saturate(20) }");
|
||||||
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
|
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
|
||||||
notificationScrollbarColor = "666";
|
notificationScrollbarColor = "666";
|
||||||
break;
|
break;
|
||||||
@@ -539,8 +541,8 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
|
|||||||
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
|
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
|
||||||
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
|
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
|
||||||
|
|
||||||
.column-header .column-type-icon { bottom: 26px !important }
|
.js-column-header .column-type-icon { margin-top: -1px !important }
|
||||||
.is-options-open .column-type-icon { bottom: 25px !important }
|
.inline-reply .pull-left .Button--link { margin-top: 3px !important }
|
||||||
|
|
||||||
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
||||||
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
||||||
@@ -561,7 +563,7 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
|
|||||||
if (this.config.columnWidth[0] === '/'){
|
if (this.config.columnWidth[0] === '/'){
|
||||||
let cols = this.config.columnWidth.slice(1);
|
let cols = this.config.columnWidth.slice(1);
|
||||||
|
|
||||||
this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important }");
|
this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important; min-width: 160px }");
|
||||||
this.css.insert(".is-condensed .column { width: calc((100vw - 55px) / "+cols+" - 6px) !important }");
|
this.css.insert(".is-condensed .column { width: calc((100vw - 55px) / "+cols+" - 6px) !important }");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@@ -44,6 +44,8 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
<option disabled></option>
|
<option disabled></option>
|
||||||
<optgroup label="Dynamic width">
|
<optgroup label="Dynamic width">
|
||||||
|
<option value="/1">1 column on screen</option>
|
||||||
|
<option value="/2">2 columns on screen</option>
|
||||||
<option value="/3">3 columns on screen</option>
|
<option value="/3">3 columns on screen</option>
|
||||||
<option value="/4">4 columns on screen</option>
|
<option value="/4">4 columns on screen</option>
|
||||||
<option value="/5">5 columns on screen</option>
|
<option value="/5">5 columns on screen</option>
|
||||||
|
@@ -23,25 +23,34 @@ enabled(){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var section = data.element.closest("section.js-column");
|
let section = data.element.closest("section.js-column");
|
||||||
|
let column = TD.controller.columnManager.get(section.attr("data-column"));
|
||||||
|
|
||||||
var column = TD.controller.columnManager.get(section.attr("data-column"));
|
let feeds = column.getFeeds();
|
||||||
var header = $(".column-title", section);
|
let accountText = "";
|
||||||
var title = header.children(".column-head-title");
|
|
||||||
|
|
||||||
var columnTitle, columnAccount;
|
if (feeds.length === 1){
|
||||||
|
let metadata = feeds[0].getMetadata();
|
||||||
|
let id = metadata.ownerId || metadata.id;
|
||||||
|
|
||||||
if (title.length){
|
if (id){
|
||||||
columnTitle = title.text();
|
accountText = TD.cache.names.getScreenName(id);
|
||||||
columnAccount = header.children(".attribution").text();
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
columnTitle = header.children(".column-title-edit-box").val();
|
let account = TD.storage.accountController.get(feeds[0].getAccountKey());
|
||||||
columnAccount = "";
|
|
||||||
|
if (account){
|
||||||
|
accountText = "@"+account.getUsername();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let header = $(".column-header-title", section);
|
||||||
|
let title = header.children(".column-heading");
|
||||||
|
let titleText = title.length ? title.text() : header.children(".column-title-edit-box").val();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
query = configuration.customSelector(columnTitle, columnAccount, column, section.hasClass("column-temp"));
|
query = configuration.customSelector(titleText, accountText, column, section.hasClass("column-temp"));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
|
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
|
||||||
return;
|
return;
|
||||||
|
@@ -51,7 +51,7 @@ enabled(){
|
|||||||
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
|
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-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 { width: 100%; height: 100%; min-width: 500px; background-color: #fff; display: flex; }");
|
||||||
this.css.insert("#templates-modal > div { display: flex; flex-direction: column; }");
|
this.css.insert("#templates-modal > div { display: flex; flex-direction: column; }");
|
||||||
|
|
||||||
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
|
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
|
||||||
@@ -68,7 +68,7 @@ enabled(){
|
|||||||
this.css.insert("#template-list li .icon:hover { opacity: 1; }");
|
this.css.insert("#template-list li .icon:hover { opacity: 1; }");
|
||||||
this.css.insert("#template-list li .template-actions { float: right; }");
|
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 { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 225px; 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 { 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 { margin: 24px 0 9px; }");
|
||||||
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
|
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
|
||||||
@@ -261,7 +261,7 @@ enabled(){
|
|||||||
$(".manage-templates-btn").addClass("active");
|
$(".manage-templates-btn").addClass("active");
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
<div id="templates-modal-wrap">
|
<div id="templates-modal-wrap" class="scroll-v scroll-styled-v">
|
||||||
<div id="templates-modal">
|
<div id="templates-modal">
|
||||||
<div id="template-list">
|
<div id="template-list">
|
||||||
<ul></ul>
|
<ul></ul>
|
||||||
|
@@ -9,14 +9,13 @@ $ErrorActionPreference = "Stop"
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
$sw = [Diagnostics.Stopwatch]::StartNew()
|
$sw = [Diagnostics.Stopwatch]::StartNew()
|
||||||
Write-Host "--------------------------"
|
|
||||||
|
|
||||||
if ($version.Equals("")){
|
if ($version.Equals("")){
|
||||||
$version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion
|
$version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Host "--------------------------"
|
||||||
Write-Host "TweetDuck version" $version
|
Write-Host "TweetDuck version" $version
|
||||||
|
|
||||||
Write-Host "--------------------------"
|
Write-Host "--------------------------"
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
@@ -44,42 +43,48 @@ try{
|
|||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
function Check-Carriage-Return{
|
function Remove-Empty-Lines{
|
||||||
Param(
|
Param([Parameter(Mandatory = $True, Position = 1)] $lines)
|
||||||
[Parameter(Mandatory = $True, Position = 1)] $fname
|
|
||||||
)
|
|
||||||
|
|
||||||
$file = @(Get-ChildItem -Path $targetDir -Include $fname -Recurse)[0]
|
ForEach($line in $lines){
|
||||||
|
if ($line -ne ''){
|
||||||
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){
|
$line
|
||||||
Throw "$fname cannot contain carriage return"
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Verified" $file.FullName.Substring($targetDir.Length)
|
function Check-Carriage-Return{
|
||||||
|
Param([Parameter(Mandatory = $True, Position = 1)] $file)
|
||||||
|
|
||||||
|
if (!(Test-Path $file)){
|
||||||
|
Throw "$file does not exist"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Get-Content -Path $file -Raw).Contains("`r")){
|
||||||
|
Throw "$file must not have any carriage return characters"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Verified" $file.Substring($targetDir.Length)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Rewrite-File{
|
function Rewrite-File{
|
||||||
[CmdletBinding()]
|
Param([Parameter(Mandatory = $True, Position = 1)] $file,
|
||||||
Param(
|
[Parameter(Mandatory = $True, Position = 2)] $lines)
|
||||||
[Parameter(Mandatory = $True, Position = 1)] $file,
|
|
||||||
[Parameter(Mandatory = $True, Position = 2)] $lines
|
|
||||||
)
|
|
||||||
|
|
||||||
|
$lines = Remove-Empty-Lines($lines)
|
||||||
$relativePath = $file.FullName.Substring($targetDir.Length)
|
$relativePath = $file.FullName.Substring($targetDir.Length)
|
||||||
|
|
||||||
if ($relativePath.StartsWith("scripts\")){
|
if ($relativePath.StartsWith("scripts\")){
|
||||||
$lines = (,("#" + $version) + $lines)
|
$lines = (,("#" + $version) + $lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
$lines = $lines | Where { $_ -ne '' }
|
|
||||||
|
|
||||||
[IO.File]::WriteAllLines($file.FullName, $lines)
|
[IO.File]::WriteAllLines($file.FullName, $lines)
|
||||||
Write-Host "Processed" $relativePath
|
Write-Host "Processed" $relativePath
|
||||||
}
|
}
|
||||||
|
|
||||||
# Post processing
|
# Post processing
|
||||||
|
|
||||||
Check-Carriage-Return("emoji-ordering.txt")
|
Check-Carriage-Return(Join-Path $targetDir "plugins\official\emoji-keyboard\emoji-ordering.txt")
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
|
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
$lines = [IO.File]::ReadLines($file.FullName)
|
||||||
@@ -92,9 +97,9 @@ try{
|
|||||||
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
|
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
$lines = [IO.File]::ReadLines($file.FullName)
|
||||||
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
||||||
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
|
$lines = $lines -Replace '^(\S.*) {$', '$1{'
|
||||||
$lines = $lines -Replace '^(\S.*?) {$', '$1{'
|
$lines = $lines -Replace '^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$', '$1:$2$3;'
|
||||||
$lines = @(($lines | Where { $_ -ne '' }) -Join ' ')
|
$lines = @((Remove-Empty-Lines($lines)) -Join ' ')
|
||||||
Rewrite-File $file $lines
|
Rewrite-File $file $lines
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +115,13 @@ try{
|
|||||||
Rewrite-File $file $lines
|
Rewrite-File $file $lines
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "------------------"
|
# Finished
|
||||||
|
|
||||||
$sw.Stop()
|
$sw.Stop()
|
||||||
|
Write-Host "------------------"
|
||||||
Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms"
|
Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms"
|
||||||
Write-Host "------------------"
|
Write-Host "------------------"
|
||||||
|
|
||||||
}catch{
|
}catch{
|
||||||
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
||||||
Write-Host $_
|
Write-Host $_
|
||||||
|
60
Resources/PostCefUpdate.ps1
Normal file
60
Resources/PostCefUpdate.ps1
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
try{
|
||||||
|
$mainProj = "..\TweetDuck.csproj"
|
||||||
|
$browserProj = "..\subprocess\TweetDuck.Browser.csproj"
|
||||||
|
|
||||||
|
$cefMatch = Select-String -Path $mainProj '<Import Project="packages\\cef\.redist\.x86\.(.*?)\\'
|
||||||
|
$cefVersion = $cefMatch.Matches[0].Groups[1].Value
|
||||||
|
|
||||||
|
$sharpMatch = Select-String -Path $mainProj '<Import Project="packages\\CefSharp\.Common\.(.*?)\\'
|
||||||
|
$sharpVersion = $sharpMatch.Matches[0].Groups[1].Value
|
||||||
|
|
||||||
|
$propsFiles = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.props",
|
||||||
|
"..\packages\CefSharp.WinForms.${sharpVersion}\build\CefSharp.WinForms.props"
|
||||||
|
|
||||||
|
# Greetings
|
||||||
|
|
||||||
|
$title = "CEF ${cefVersion}, CefSharp ${sharpVersion}"
|
||||||
|
|
||||||
|
Write-Host ("-" * $title.Length)
|
||||||
|
Write-Host $title
|
||||||
|
Write-Host ("-" * $title.Length)
|
||||||
|
|
||||||
|
# Perform update
|
||||||
|
|
||||||
|
Write-Host "Copying dev tools to repository..."
|
||||||
|
|
||||||
|
Copy-Item "..\packages\cef.redist.x86.${cefVersion}\CEF\devtools_resources.pak" -Destination "..\bld\Resources\" -Force
|
||||||
|
|
||||||
|
Write-Host "Updating browser subprocess reference..."
|
||||||
|
|
||||||
|
$contents = [IO.File]::ReadAllText($browserProj)
|
||||||
|
$contents = $contents -Replace '(?<=<HintPath>\.\.\\packages\\CefSharp\.Common\.)(.*?)(?=\\)', $sharpVersion
|
||||||
|
$contents = $contents -Replace '(?<=<Reference Include="CefSharp\.BrowserSubprocess\.Core, Version=)(\d+)', $sharpVersion.Split(".")[0]
|
||||||
|
|
||||||
|
[IO.File]::WriteAllText($browserProj, $contents)
|
||||||
|
|
||||||
|
Write-Host "Removing x64 and AnyCPU from CefSharp props..."
|
||||||
|
|
||||||
|
foreach($file in $propsFiles){
|
||||||
|
$contents = [IO.File]::ReadAllText($file)
|
||||||
|
$contents = $contents -Replace '(?<=<When Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
|
||||||
|
|
||||||
|
[IO.File]::WriteAllText($file, $contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finished
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Finished. Exiting in 6 seconds..."
|
||||||
|
Start-Sleep -Seconds 6
|
||||||
|
|
||||||
|
}catch{
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
||||||
|
Write-Host $_
|
||||||
|
|
||||||
|
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||||
|
Exit 1
|
||||||
|
}
|
@@ -145,6 +145,19 @@
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let isSensitive = (tweet) => {
|
||||||
|
let main = tweet.getMainTweet && tweet.getMainTweet();
|
||||||
|
return true if main && main.possiblySensitive; // TODO these don't show media badges when hiding sensitive media
|
||||||
|
|
||||||
|
let related = tweet.getRelatedTweet && tweet.getRelatedTweet();
|
||||||
|
return true if related && related.possiblySensitive;
|
||||||
|
|
||||||
|
let quoted = tweet.quotedTweet;
|
||||||
|
return true if quoted && quoted.possiblySensitive;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
let fixMedia = (html, media) => {
|
let fixMedia = (html, media) => {
|
||||||
return html.find("a[data-media-entity-id='"+media.mediaId+"'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("'+media.small()+'")');
|
return html.find("a[data-media-entity-id='"+media.mediaId+"'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("'+media.small()+'")');
|
||||||
};
|
};
|
||||||
@@ -160,7 +173,7 @@
|
|||||||
startRecentTweetTimer();
|
startRecentTweetTimer();
|
||||||
|
|
||||||
if (column.model.getHasNotification()){
|
if (column.model.getHasNotification()){
|
||||||
let sensitive = (tweet.getRelatedTweet() && tweet.getRelatedTweet().possiblySensitive || (tweet.quotedTweet && tweet.quotedTweet.possiblySensitive));
|
let sensitive = isSensitive(tweet);
|
||||||
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
||||||
|
|
||||||
let html = $(tweet.render({
|
let html = $(tweet.render({
|
||||||
@@ -172,7 +185,8 @@
|
|||||||
isMediaPreviewLarge: false,
|
isMediaPreviewLarge: false,
|
||||||
isMediaPreviewCompact: false,
|
isMediaPreviewCompact: false,
|
||||||
isMediaPreviewInQuoted: previews,
|
isMediaPreviewInQuoted: previews,
|
||||||
thumbSizeClass: "media-size-medium"
|
thumbSizeClass: "media-size-medium",
|
||||||
|
mediaPreviewSize: "medium"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
@@ -197,6 +211,10 @@
|
|||||||
html.find(".js-media").remove();
|
html.find(".js-media").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.find("a[data-full-url]").each(function(){ // bypass t.co on all links
|
||||||
|
this.href = this.getAttribute("data-full-url");
|
||||||
|
});
|
||||||
|
|
||||||
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
|
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
|
||||||
this.outerHTML = this.innerHTML;
|
this.outerHTML = this.innerHTML;
|
||||||
});
|
});
|
||||||
@@ -380,6 +398,8 @@
|
|||||||
let menu = $(".js-dropdown-content").children("ul").first();
|
let menu = $(".js-dropdown-content").children("ul").first();
|
||||||
return if menu.length === 0;
|
return if menu.length === 0;
|
||||||
|
|
||||||
|
menu.find(".update-available-item").parent().remove(); // TODO temp
|
||||||
|
|
||||||
let button = $('<li class="is-selectable" data-tweetduck><a href="#" data-action>TweetDuck</a></li>');
|
let button = $('<li class="is-selectable" data-tweetduck><a href="#" data-action>TweetDuck</a></li>');
|
||||||
button.insertBefore(menu.children(".drp-h-divider").last());
|
button.insertBefore(menu.children(".drp-h-divider").last());
|
||||||
|
|
||||||
@@ -508,6 +528,7 @@
|
|||||||
obj.profileImageURL = realAvatar;
|
obj.profileImageURL = realAvatar;
|
||||||
obj.url = "https://tweetduck.chylex.com";
|
obj.url = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
|
if (obj.entities && obj.entities.url){
|
||||||
obj.entities.url.urls = [{
|
obj.entities.url.urls = [{
|
||||||
url: obj.url,
|
url: obj.url,
|
||||||
expanded_url: obj.url,
|
expanded_url: obj.url,
|
||||||
@@ -515,6 +536,7 @@
|
|||||||
indices: [ 0, 23 ]
|
indices: [ 0, 23 ]
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
|
else if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
|
||||||
obj.url = e.url.urls[0].expanded_url;
|
obj.url = e.url.urls[0].expanded_url;
|
||||||
}
|
}
|
||||||
@@ -558,17 +580,17 @@
|
|||||||
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
||||||
|
|
||||||
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
||||||
$TD.setLastRightClickInfo("video", media.chooseVideoVariant().url);
|
$TD.setRightClickedLink("video", media.chooseVideoVariant().url);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$TD.setLastRightClickInfo("image", media.large());
|
$TD.setRightClickedLink("image", media.large());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (me.classList.contains("js-gif-play")){
|
else if (me.classList.contains("js-gif-play")){
|
||||||
$TD.setLastRightClickInfo("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
$TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$TD.setLastRightClickInfo("link", me.getAttribute("data-full-url"));
|
$TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -707,6 +729,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let gif = html.find(".js-media-gif-container");
|
||||||
|
|
||||||
|
if (gif.length){
|
||||||
|
gif.css("background-image", 'url("'+chirp.getMedia()[0].small()+'")');
|
||||||
|
}
|
||||||
|
|
||||||
let type = chirp.getChirpType();
|
let type = chirp.getChirpType();
|
||||||
|
|
||||||
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
|
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
|
||||||
@@ -1368,11 +1396,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift.
|
// Block: Fix broken horizontal scrolling of column container when holding Shift. TODO Fix broken smooth scrolling.
|
||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){
|
if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){
|
||||||
TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(e){
|
TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(column){
|
||||||
$(".js-column[data-column='"+e.model.getKey()+"']").off("mousewheel onmousewheel");
|
let ele = $(".js-column[data-column='"+column.model.getKey()+"']");
|
||||||
|
return if !ele.length;
|
||||||
|
|
||||||
|
ele.off("onmousewheel").on("mousewheel", ".scroll-v", function(e){
|
||||||
|
if (e.shiftKey){
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.TDGF_prioritizeNewestEvent(ele[0], "mousewheel");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1485,10 +1522,17 @@
|
|||||||
//
|
//
|
||||||
// Block: Disable default TweetDeck update notification.
|
// Block: Disable default TweetDeck update notification.
|
||||||
//
|
//
|
||||||
onAppReady.push(function(){
|
$(document).on("uiSuggestRefreshToggle", function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*onAppReady.push(function(){
|
||||||
let events = $._data(document, "events");
|
let events = $._data(document, "events");
|
||||||
delete events["uiSuggestRefreshToggle"];
|
delete events["uiSuggestRefreshToggle"];
|
||||||
});
|
|
||||||
|
//$(".js-app-settings .icon-settings").removeClass("color-twitter-yellow"); // TODO temp
|
||||||
|
});*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Disable TweetDeck metrics.
|
// Block: Disable TweetDeck metrics.
|
||||||
|
@@ -3,12 +3,13 @@
|
|||||||
let css = $(`
|
let css = $(`
|
||||||
<style>
|
<style>
|
||||||
#td-introduction-modal {
|
#td-introduction-modal {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#td-introduction-modal .mdl {
|
#td-introduction-modal .mdl {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-width: 830px;
|
min-width: 515px;
|
||||||
|
max-width: 835px;
|
||||||
height: 328px;
|
height: 328px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
</style>`).appendTo(document.head);
|
</style>`).appendTo(document.head);
|
||||||
|
|
||||||
let ele = $(`
|
let ele = $(`
|
||||||
<div id="td-introduction-modal" class="ovl">
|
<div id="td-introduction-modal" class="ovl scroll-v scroll-styled-v">
|
||||||
<div class="mdl is-inverted-dark">
|
<div class="mdl is-inverted-dark">
|
||||||
<header class="mdl-header">
|
<header class="mdl-header">
|
||||||
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
|
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
|
||||||
|
@@ -14,14 +14,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hook into links to bypass default open function and t.co, and handle skipping notification when opening links.
|
// Block: Hook into links to bypass default open function, and handle skipping notification when opening links.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
const onLinkClick = function(e){
|
const onLinkClick = function(e){
|
||||||
if (e.button === 0 || e.button === 1){
|
if (e.button === 0 || e.button === 1){
|
||||||
let ele = e.currentTarget;
|
let ele = e.currentTarget;
|
||||||
|
|
||||||
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
|
$TD.openBrowser(ele.href);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if ($TDX.skipOnLinkClick){
|
if ($TDX.skipOnLinkClick){
|
||||||
@@ -38,13 +38,6 @@
|
|||||||
addEventListener(links, "auxclick", onLinkClick);
|
addEventListener(links, "auxclick", onLinkClick);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
//
|
|
||||||
// Block: Allow bypassing of t.co in context menus.
|
|
||||||
//
|
|
||||||
addEventListener(links, "contextmenu", function(e){
|
|
||||||
$TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Expand shortened links on hover or display tooltip.
|
// Block: Expand shortened links on hover or display tooltip.
|
||||||
//
|
//
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
|
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
|
||||||
|
|
||||||
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => {
|
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => {
|
||||||
let framesLeft = 5; // basic render is done in 1 frame, large media take longer
|
let framesLeft = {frames}; // basic render is done in 1 frame, large media take longer
|
||||||
|
|
||||||
let onNextFrame = function(){
|
let onNextFrame = function(){
|
||||||
if (--framesLeft < 0){
|
if (--framesLeft < 0){
|
||||||
|
@@ -150,6 +150,7 @@ button {
|
|||||||
|
|
||||||
.activity-header .icon-user-filled {
|
.activity-header .icon-user-filled {
|
||||||
vertical-align: sub !important;
|
vertical-align: sub !important;
|
||||||
|
margin-right: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
|
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
|
||||||
@@ -220,6 +221,50 @@ a[data-full-url] {
|
|||||||
bottom: 0 !important;
|
bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********************************************************/
|
||||||
|
/* Prevent column icons from being hidden by column title */
|
||||||
|
/**********************************************************/
|
||||||
|
|
||||||
|
.column-header-title {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-heading {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-header-links {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-td-icon="icon-message"] .column-header-links {
|
||||||
|
min-width: 86px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
/* Fix modal dialogs breaking when window size is too small */
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
.ovl:before, .overlay:before {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ovl, .overlay {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ovl[style="display: block;"] {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck .overlay {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************/
|
/*******************************************/
|
||||||
/* Fix general visual issues or annoyances */
|
/* Fix general visual issues or annoyances */
|
||||||
/*******************************************/
|
/*******************************************/
|
||||||
@@ -234,6 +279,37 @@ a[data-full-url] {
|
|||||||
vertical-align: -10% !important;
|
vertical-align: -10% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-title {
|
||||||
|
/* fix alignment of everything in column headers */
|
||||||
|
padding-top: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-title .attribution {
|
||||||
|
/* fix alignment of usernames in column headers */
|
||||||
|
margin: 0 0 0 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-navigator .tooltip {
|
||||||
|
/* fix tooltips in navigation */
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-header-links .icon {
|
||||||
|
/* fix tooltips in column headers */
|
||||||
|
height: calc(1em + 8px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-header-temp:not(.js-column-header) {
|
||||||
|
/* fix missing column header padding in Edit List dialog */
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-column-options .btn-options-tray {
|
||||||
|
/* fix underline on buttons in column options */
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
#tduck .nav-user-info .hide-condensed {
|
#tduck .nav-user-info .hide-condensed {
|
||||||
/* move login account info */
|
/* move login account info */
|
||||||
margin: 0px !important;
|
margin: 0px !important;
|
||||||
@@ -241,7 +317,7 @@ a[data-full-url] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-td-font='smallest'] .sprite-verified-mini {
|
html[data-td-font='smallest'] .sprite-verified-mini {
|
||||||
/* fix cut off badge when zoomed in */
|
/* fix cut off badge in timelines */
|
||||||
width: 13px !important;
|
width: 13px !important;
|
||||||
height: 13px !important;
|
height: 13px !important;
|
||||||
background-position: -223px -99px !important;
|
background-position: -223px -99px !important;
|
||||||
@@ -261,11 +337,21 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
background-position: -223px -97px !important;
|
background-position: -223px -97px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
||||||
|
/* fix cut off badge in follow chirps */
|
||||||
|
margin-top: -7px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.keyboard-shortcut-list {
|
.keyboard-shortcut-list {
|
||||||
/* fix keyboard navigation alignment */
|
/* fix keyboard navigation alignment */
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-message.is-actionable span:hover > .icon-small-valigned {
|
||||||
|
/* add a visual response when hovering individual filter icons; black theme uses a value of 20 */
|
||||||
|
filter: saturate(10);
|
||||||
|
}
|
||||||
|
|
||||||
.tweet-detail-wrapper .js-media-gif-container {
|
.tweet-detail-wrapper .js-media-gif-container {
|
||||||
/* GIFs in detail view don't trigger the pointer cursor */
|
/* GIFs in detail view don't trigger the pointer cursor */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -290,9 +376,9 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
|
|
||||||
.column-nav-link .attribution {
|
.js-column-nav-list .attribution {
|
||||||
/* fix cut off account names */
|
/* fix cut off account names */
|
||||||
position: absolute !important;
|
line-height: 1.1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tduck .js-docked-compose .js-drawer-close {
|
#tduck .js-docked-compose .js-drawer-close {
|
||||||
@@ -311,25 +397,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
vertical-align: -15% !important;
|
vertical-align: -15% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************/
|
|
||||||
/* Fix tooltips in navigation and column header */
|
|
||||||
/************************************************/
|
|
||||||
|
|
||||||
.app-navigator .tooltip {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.js-column-header .column-header-link {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.js-column-header .column-header-link .icon {
|
|
||||||
width: calc(1em + 8px);
|
|
||||||
height: 100%;
|
|
||||||
padding: 9px 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************/
|
/*******************************************/
|
||||||
/* Fix one pixel space below column header */
|
/* Fix one pixel space below column header */
|
||||||
/*******************************************/
|
/*******************************************/
|
||||||
@@ -342,10 +409,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
border-bottom: none !important;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-options-open .column-type-icon {
|
|
||||||
bottom: 27px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************/
|
/********************************************/
|
||||||
/* Fix cut off usernames in Messages column */
|
/* Fix cut off usernames in Messages column */
|
||||||
/********************************************/
|
/********************************************/
|
||||||
|
@@ -61,13 +61,25 @@ a[data-full-url] {
|
|||||||
/* Fix general visual issues or annoyances */
|
/* Fix general visual issues or annoyances */
|
||||||
/*******************************************/
|
/*******************************************/
|
||||||
|
|
||||||
|
html[data-td-font='smallest'] .sprite-verified-mini {
|
||||||
|
/* fix cut off badge in timelines */
|
||||||
|
width: 13px !important;
|
||||||
|
height: 13px !important;
|
||||||
|
background-position: -223px -99px !important;
|
||||||
|
}
|
||||||
|
|
||||||
html[data-td-font='smallest'] .badge-verified:before {
|
html[data-td-font='smallest'] .badge-verified:before {
|
||||||
/* fix cut off badge icon */
|
/* fix cut off badge in notifications */
|
||||||
width: 13px !important;
|
width: 13px !important;
|
||||||
height: 13px !important;
|
height: 13px !important;
|
||||||
background-position: -223px -98px !important;
|
background-position: -223px -98px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
||||||
|
/* fix cut off badge in follow chirps */
|
||||||
|
margin-top: -7px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.account-inline .username {
|
.account-inline .username {
|
||||||
vertical-align: 10% !important;
|
vertical-align: 10% !important;
|
||||||
}
|
}
|
||||||
@@ -90,6 +102,7 @@ html[data-td-font='smallest'] .badge-verified:before {
|
|||||||
|
|
||||||
.activity-header .icon-user-filled {
|
.activity-header .icon-user-filled {
|
||||||
vertical-align: sub !important;
|
vertical-align: sub !important;
|
||||||
|
margin-right: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-notification-padded .item-img {
|
.td-notification-padded .item-img {
|
||||||
|
@@ -164,7 +164,7 @@
|
|||||||
<div class='tdu-buttons'>
|
<div class='tdu-buttons'>
|
||||||
<button class='tdu-btn-download'>Update now</button>
|
<button class='tdu-btn-download'>Update now</button>
|
||||||
<button class='tdu-btn-later'>Remind me later</button>
|
<button class='tdu-btn-later'>Remind me later</button>
|
||||||
<button class='tdu-btn-ignore'>Ignore this update</button>
|
<!-- TODO <button class='tdu-btn-ignore'>Ignore this update</button>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).appendTo(document.body).css("display", existed ? "block" : "none");
|
`).appendTo(document.body).css("display", existed ? "block" : "none");
|
||||||
|
117
TweetDuck.csproj
117
TweetDuck.csproj
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.props')" />
|
<Import Project="packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props')" />
|
||||||
<Import Project="packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.props')" />
|
<Import Project="packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props')" />
|
||||||
<Import Project="packages\cef.redist.x86.3.3325.1758\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3325.1758\build\cef.redist.x86.props')" />
|
<Import Project="packages\cef.redist.x86.3.3359.1772\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3359.1772\build\cef.redist.x86.props')" />
|
||||||
<Import Project="packages\cef.redist.x64.3.3325.1758\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3325.1758\build\cef.redist.x64.props')" />
|
<Import Project="packages\cef.redist.x64.3.3359.1772\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3359.1772\build\cef.redist.x64.props')" />
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@@ -15,25 +15,11 @@
|
|||||||
<AssemblyName>TweetDuck</AssemblyName>
|
<AssemblyName>TweetDuck</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NuGetPackageImportStamp>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
</NuGetPackageImportStamp>
|
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
||||||
<TargetFrameworkProfile>
|
</PropertyGroup>
|
||||||
</TargetFrameworkProfile>
|
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||||
<PublishUrl>publish\</PublishUrl>
|
<StartArguments>-datafolder TweetDuckDebug</StartArguments>
|
||||||
<Install>true</Install>
|
|
||||||
<InstallFrom>Disk</InstallFrom>
|
|
||||||
<UpdateEnabled>false</UpdateEnabled>
|
|
||||||
<UpdateMode>Foreground</UpdateMode>
|
|
||||||
<UpdateInterval>7</UpdateInterval>
|
|
||||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
|
||||||
<UpdatePeriodically>false</UpdatePeriodically>
|
|
||||||
<UpdateRequired>false</UpdateRequired>
|
|
||||||
<MapFileExtensions>true</MapFileExtensions>
|
|
||||||
<ApplicationRevision>0</ApplicationRevision>
|
|
||||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
|
||||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
|
||||||
<UseApplicationTrust>false</UseApplicationTrust>
|
|
||||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -44,23 +30,15 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
|
||||||
<LangVersion>7</LangVersion>
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<DefineConstants>
|
|
||||||
</DefineConstants>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
<LangVersion>7</LangVersion>
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -327,11 +305,6 @@
|
|||||||
<Compile Include="Updates\Events\UpdateEventArgs.cs" />
|
<Compile Include="Updates\Events\UpdateEventArgs.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
|
||||||
<Visible>False</Visible>
|
|
||||||
<ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
|
|
||||||
<Install>true</Install>
|
|
||||||
</BootstrapperPackage>
|
|
||||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||||
<Visible>False</Visible>
|
<Visible>False</Visible>
|
||||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||||
@@ -342,11 +315,6 @@
|
|||||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||||
<Install>false</Install>
|
<Install>false</Install>
|
||||||
</BootstrapperPackage>
|
</BootstrapperPackage>
|
||||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
|
|
||||||
<Visible>False</Visible>
|
|
||||||
<ProductName>Windows Installer 4.5</ProductName>
|
|
||||||
<Install>true</Install>
|
|
||||||
</BootstrapperPackage>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Core\FormBrowser.resx">
|
<EmbeddedResource Include="Core\FormBrowser.resx">
|
||||||
@@ -365,30 +333,45 @@
|
|||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Include="Resources\avatar.png" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="Resources\icon-small.ico" />
|
<None Include="Resources\icon-small.ico" />
|
||||||
<None Include="Resources\icon-tray-new.ico" />
|
<None Include="Resources\icon-tray-new.ico" />
|
||||||
<None Include="Resources\icon-tray.ico" />
|
<None Include="Resources\icon-tray.ico" />
|
||||||
<None Include="Resources\PostBuild.ps1" />
|
|
||||||
<None Include="Resources\spinner.apng" />
|
<None Include="Resources\spinner.apng" />
|
||||||
</ItemGroup>
|
<None Include="Resources\PostBuild.ps1" />
|
||||||
<ItemGroup>
|
<None Include="Resources\Plugins\.debug\.meta" />
|
||||||
<Folder Include="Resources\Plugins\" />
|
<None Include="Resources\Plugins\.debug\browser.js" />
|
||||||
</ItemGroup>
|
<None Include="Resources\Plugins\clear-columns\.meta" />
|
||||||
<ItemGroup>
|
<None Include="Resources\Plugins\clear-columns\browser.js" />
|
||||||
<Content Include="Resources\avatar.png" />
|
<None Include="Resources\Plugins\edit-design\.meta" />
|
||||||
<Content Include="Resources\Scripts\code.js" />
|
<None Include="Resources\Plugins\edit-design\browser.js" />
|
||||||
<Content Include="Resources\Scripts\introduction.js" />
|
<None Include="Resources\Plugins\edit-design\modal.html" />
|
||||||
<Content Include="Resources\Scripts\notification.js" />
|
<None Include="Resources\Plugins\edit-design\theme.black.css" />
|
||||||
<Content Include="Resources\Scripts\pages\error.html" />
|
<None Include="Resources\Plugins\emoji-keyboard\.meta" />
|
||||||
<Content Include="Resources\Scripts\pages\example.html" />
|
<None Include="Resources\Plugins\emoji-keyboard\browser.js" />
|
||||||
<Content Include="Resources\Scripts\plugins.browser.js" />
|
<None Include="Resources\Plugins\emoji-keyboard\emoji-instructions.txt" />
|
||||||
<Content Include="Resources\Scripts\plugins.js" />
|
<None Include="Resources\Plugins\emoji-keyboard\emoji-ordering.txt" />
|
||||||
<Content Include="Resources\Scripts\plugins.notification.js" />
|
<None Include="Resources\Plugins\reply-account\.meta" />
|
||||||
<Content Include="Resources\Scripts\styles\browser.css" />
|
<None Include="Resources\Plugins\reply-account\browser.js" />
|
||||||
<Content Include="Resources\Scripts\styles\notification.css" />
|
<None Include="Resources\Plugins\reply-account\configuration.default.js" />
|
||||||
<Content Include="Resources\Scripts\twitter.js" />
|
<None Include="Resources\Plugins\templates\.meta" />
|
||||||
<Content Include="Resources\Scripts\update.js" />
|
<None Include="Resources\Plugins\templates\browser.js" />
|
||||||
|
<None Include="Resources\Plugins\timeline-polls\.meta" />
|
||||||
|
<None Include="Resources\Plugins\timeline-polls\browser.js" />
|
||||||
|
<None Include="Resources\Scripts\code.js" />
|
||||||
|
<None Include="Resources\Scripts\introduction.js" />
|
||||||
|
<None Include="Resources\Scripts\notification.js" />
|
||||||
|
<None Include="Resources\Scripts\pages\error.html" />
|
||||||
|
<None Include="Resources\Scripts\pages\example.html" />
|
||||||
|
<None Include="Resources\Scripts\plugins.browser.js" />
|
||||||
|
<None Include="Resources\Scripts\plugins.js" />
|
||||||
|
<None Include="Resources\Scripts\plugins.notification.js" />
|
||||||
|
<None Include="Resources\Scripts\screenshot.js" />
|
||||||
|
<None Include="Resources\Scripts\styles\browser.css" />
|
||||||
|
<None Include="Resources\Scripts\styles\notification.css" />
|
||||||
|
<None Include="Resources\Scripts\twitter.js" />
|
||||||
|
<None Include="Resources\Scripts\update.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
|
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
|
||||||
@@ -428,13 +411,11 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('packages\cef.redist.x64.3.3325.1758\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3325.1758\build\cef.redist.x64.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x64.3.3359.1772\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3359.1772\build\cef.redist.x64.props'))" />
|
||||||
<Error Condition="!Exists('packages\cef.redist.x86.3.3325.1758\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3325.1758\build\cef.redist.x86.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x86.3.3359.1772\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3359.1772\build\cef.redist.x86.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.targets'))" />
|
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.65.0.0-pre01\build\CefSharp.Common.targets')" />
|
<Import Project="packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.65.0.0-pre01\build\CefSharp.WinForms.targets')" />
|
|
||||||
</Project>
|
</Project>
|
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\Tw
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TweetTest.Unit", "lib\TweetTest.Unit\TweetTest.Unit.fsproj", "{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
@@ -28,7 +30,8 @@ Global
|
|||||||
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.Build.0 = Release|x86
|
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.Build.0 = Release|x86
|
||||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.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 = Debug|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.Build.0 = Debug|x86
|
||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
|
||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
|
||||||
@@ -37,6 +40,10 @@ Global
|
|||||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
|
||||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
|
||||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86
|
||||||
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.ActiveCfg = Debug|x86
|
||||||
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.Build.0 = Debug|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@@ -10,6 +10,8 @@ using Timer = System.Windows.Forms.Timer;
|
|||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateHandler : IDisposable{
|
sealed class UpdateHandler : IDisposable{
|
||||||
|
public const bool TemporarilyForceUpdateChecking = true;
|
||||||
|
|
||||||
public const int CheckCodeUpdatesDisabled = -1;
|
public const int CheckCodeUpdatesDisabled = -1;
|
||||||
public const int CheckCodeNotOnTweetDeck = -2;
|
public const int CheckCodeNotOnTweetDeck = -2;
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ namespace TweetDuck.Updates{
|
|||||||
|
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
if (Program.UserConfig.EnableUpdateCheck){
|
if (Program.UserConfig.EnableUpdateCheck || TemporarilyForceUpdateChecking){
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
if (Program.UserConfig.EnableUpdateCheck || force){
|
if (Program.UserConfig.EnableUpdateCheck || TemporarilyForceUpdateChecking || force){
|
||||||
if (!browser.IsTweetDeckWebsite){
|
if (!browser.IsTweetDeckWebsite){
|
||||||
return CheckCodeNotOnTweetDeck;
|
return CheckCodeNotOnTweetDeck;
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@@ -30,6 +30,7 @@ Uninstallable=TDIsUninstallable
|
|||||||
UninstallDisplayName={#MyAppName}
|
UninstallDisplayName={#MyAppName}
|
||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
Compression=lzma2/ultra
|
Compression=lzma2/ultra
|
||||||
|
LZMADictionarySize=15360
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
InternalCompressLevel=normal
|
InternalCompressLevel=normal
|
||||||
MinVersion=0,6.1
|
MinVersion=0,6.1
|
||||||
|
@@ -30,6 +30,7 @@ Uninstallable=no
|
|||||||
UsePreviousAppDir=no
|
UsePreviousAppDir=no
|
||||||
PrivilegesRequired=lowest
|
PrivilegesRequired=lowest
|
||||||
Compression=lzma2/ultra
|
Compression=lzma2/ultra
|
||||||
|
LZMADictionarySize=15360
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
InternalCompressLevel=normal
|
InternalCompressLevel=normal
|
||||||
MinVersion=0,6.1
|
MinVersion=0,6.1
|
||||||
|
@@ -32,6 +32,7 @@ UninstallDisplayName={#MyAppName}
|
|||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
PrivilegesRequired=lowest
|
PrivilegesRequired=lowest
|
||||||
Compression=lzma/normal
|
Compression=lzma/normal
|
||||||
|
LZMADictionarySize=512
|
||||||
SolidCompression=True
|
SolidCompression=True
|
||||||
InternalCompressLevel=normal
|
InternalCompressLevel=normal
|
||||||
MinVersion=0,6.1
|
MinVersion=0,6.1
|
||||||
|
79
lib/TweetTest.Unit/Core/TestBrowserUtils.fs
Normal file
79
lib/TweetTest.Unit/Core/TestBrowserUtils.fs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
namespace Unit.Core.BrowserUtils
|
||||||
|
|
||||||
|
open Xunit
|
||||||
|
open TweetDuck.Core.Utils
|
||||||
|
|
||||||
|
|
||||||
|
module CheckUrl =
|
||||||
|
type Result = BrowserUtils.UrlCheckResult
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts HTTP protocol`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("http://example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts HTTPS protocol`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("https://example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts FTP protocol`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("ftp://example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts MAILTO protocol`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("mailto://someone@example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts URL with port, path, query, and hash`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("http://www.example.co.uk:80/path?key=abc&array[]=5#hash"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts IPv4 address`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("http://127.0.0.1"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts IPv6 address`` () =
|
||||||
|
Assert.Equal(Result.Fine, BrowserUtils.CheckUrl("http://[2001:db8:0:0:0:ff00:42:8329]"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``recognizes t.co as tracking URL`` () =
|
||||||
|
Assert.Equal(Result.Tracking, BrowserUtils.CheckUrl("http://t.co/12345"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rejects empty URL`` () =
|
||||||
|
Assert.Equal(Result.Invalid, BrowserUtils.CheckUrl(""))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rejects missing protocol`` () =
|
||||||
|
Assert.Equal(Result.Invalid, BrowserUtils.CheckUrl("www.example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rejects banned protocol`` () =
|
||||||
|
Assert.Equal(Result.Invalid, BrowserUtils.CheckUrl("file://example.com"))
|
||||||
|
|
||||||
|
|
||||||
|
module GetFileNameFromUrl =
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``simple file URL returns file name`` () =
|
||||||
|
Assert.Equal("index.html", BrowserUtils.GetFileNameFromUrl("http://example.com/index.html"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``file URL with query returns file name`` () =
|
||||||
|
Assert.Equal("index.html", BrowserUtils.GetFileNameFromUrl("http://example.com/index.html?version=2"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``file URL w/o extension returns file name`` () =
|
||||||
|
Assert.Equal("index", BrowserUtils.GetFileNameFromUrl("http://example.com/index"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``file URL with trailing dot returns file name with dot`` () =
|
||||||
|
Assert.Equal("index.", BrowserUtils.GetFileNameFromUrl("http://example.com/index."))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``root URL returns null`` () =
|
||||||
|
Assert.Null(BrowserUtils.GetFileNameFromUrl("http://example.com"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``path URL returns null`` () =
|
||||||
|
Assert.Null(BrowserUtils.GetFileNameFromUrl("http://example.com/path/"))
|
118
lib/TweetTest.Unit/Core/TestStringUtils.fs
Normal file
118
lib/TweetTest.Unit/Core/TestStringUtils.fs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
namespace Unit.Core.StringUtils
|
||||||
|
|
||||||
|
open Xunit
|
||||||
|
open TweetDuck.Core.Utils
|
||||||
|
|
||||||
|
|
||||||
|
module ExtractBefore =
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``empty input string returns empty string`` () =
|
||||||
|
Assert.Equal("", StringUtils.ExtractBefore("", ' '))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``input string w/o searched char returns input string`` () =
|
||||||
|
Assert.Equal("abc", StringUtils.ExtractBefore("abc", ' '))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``finding searched char returns everything before it`` () =
|
||||||
|
Assert.Equal("abc", StringUtils.ExtractBefore("abc def ghi", ' '))
|
||||||
|
|
||||||
|
[<Theory>]
|
||||||
|
[<InlineData(0, "abc")>]
|
||||||
|
[<InlineData(3, "abc")>]
|
||||||
|
[<InlineData(4, "abc def")>]
|
||||||
|
[<InlineData(7, "abc def")>]
|
||||||
|
[<InlineData(8, "abc def ghi")>]
|
||||||
|
let ``start index works`` (startIndex: int, expectedValue: string) =
|
||||||
|
Assert.Equal(expectedValue, StringUtils.ExtractBefore("abc def ghi", ' ', startIndex))
|
||||||
|
|
||||||
|
|
||||||
|
module ParseInts =
|
||||||
|
open System
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``empty input string returns empty collection`` () =
|
||||||
|
Assert.Empty(StringUtils.ParseInts("", ','))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``single integer is parsed correctly`` () =
|
||||||
|
Assert.Equal([ 1 ], StringUtils.ParseInts("1", ','))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``multiple integers are parsed correctly`` () =
|
||||||
|
Assert.Equal([ -3 .. 3 ], StringUtils.ParseInts("-3,-2,-1,0,1,2,3", ','))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``excessive delimiters are discarded`` () =
|
||||||
|
Assert.Equal([ 1 .. 4 ], StringUtils.ParseInts(",,1,,,2,3,,4,,,", ','))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``invalid format throws exception`` () =
|
||||||
|
Assert.Throws<FormatException>(fun () -> StringUtils.ParseInts("1,2,a", ',') |> ignore)
|
||||||
|
|
||||||
|
|
||||||
|
module ConvertPascalCaseToScreamingSnakeCase =
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts one word`` () =
|
||||||
|
Assert.Equal("HELP", StringUtils.ConvertPascalCaseToScreamingSnakeCase("Help"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts two words`` () =
|
||||||
|
Assert.Equal("HELP_ME", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMe"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts many words`` () =
|
||||||
|
Assert.Equal("HELP_ME_PLEASE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMePlease"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts one uppercase abbreviation`` () =
|
||||||
|
Assert.Equal("HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HTMLCode"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts many uppercase abbreviations`` () =
|
||||||
|
Assert.Equal("I_LIKE_HTML_AND_CSS", StringUtils.ConvertPascalCaseToScreamingSnakeCase("ILikeHTMLAndCSS"))
|
||||||
|
|
||||||
|
|
||||||
|
module ConvertRot13 =
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``ignores digits and special characters`` () =
|
||||||
|
Assert.Equal("<123'456.789>", StringUtils.ConvertRot13("<123'456.789>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts lowercase letters correctly`` () =
|
||||||
|
Assert.Equal("nopqrstuvwxyzabcdefghijklm", StringUtils.ConvertRot13("abcdefghijklmnopqrstuvwxyz"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts uppercase letters correctly`` () =
|
||||||
|
Assert.Equal("NOPQRSTUVWXYZABCDEFGHIJKLM", StringUtils.ConvertRot13("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``converts mixed character types correctly`` () =
|
||||||
|
Assert.Equal("Uryyb, jbeyq! :)", StringUtils.ConvertRot13("Hello, world! :)"))
|
||||||
|
|
||||||
|
|
||||||
|
module CountOccurrences =
|
||||||
|
open System
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``empty input string returns zero`` () =
|
||||||
|
Assert.Equal(0, StringUtils.CountOccurrences("", "a"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``empty searched string throws`` () =
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(fun () -> StringUtils.CountOccurrences("hello", "") |> ignore)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``counts single letter characters correctly`` () =
|
||||||
|
Assert.Equal(3, StringUtils.CountOccurrences("hello world", "l"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``counts longer substrings correctly`` () =
|
||||||
|
Assert.Equal(2, StringUtils.CountOccurrences("hello and welcome in hell", "hell"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``does not count overlapping substrings`` () =
|
||||||
|
Assert.Equal(2, StringUtils.CountOccurrences("aaaaa", "aa"))
|
112
lib/TweetTest.Unit/Core/TestTwitterUtils.fs
Normal file
112
lib/TweetTest.Unit/Core/TestTwitterUtils.fs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
namespace Unit.Core.TwitterUtils
|
||||||
|
|
||||||
|
open Xunit
|
||||||
|
open TweetDuck.Core.Utils
|
||||||
|
|
||||||
|
|
||||||
|
[<Collection("RegexAccount")>]
|
||||||
|
module RegexAccount_IsMatch =
|
||||||
|
let isMatch = TwitterUtils.RegexAccount.IsMatch
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts HTTP protocol`` () =
|
||||||
|
Assert.True(isMatch("http://twitter.com/chylexmc"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts HTTPS protocol`` () =
|
||||||
|
Assert.True(isMatch("https://twitter.com/chylexmc"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accepts trailing slash`` () =
|
||||||
|
Assert.True(isMatch("https://twitter.com/chylexmc/"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rejects URL with query`` () =
|
||||||
|
Assert.False(isMatch("https://twitter.com/chylexmc?query"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rejects URL with extra path`` () =
|
||||||
|
Assert.False(isMatch("https://twitter.com/chylexmc/status/123"))
|
||||||
|
|
||||||
|
[<Theory>]
|
||||||
|
[<InlineData("signup")>]
|
||||||
|
[<InlineData("tos")>]
|
||||||
|
[<InlineData("privacy")>]
|
||||||
|
[<InlineData("search")>]
|
||||||
|
[<InlineData("search?query")>]
|
||||||
|
[<InlineData("search-home")>]
|
||||||
|
[<InlineData("search-advanced")>]
|
||||||
|
let ``rejects reserved page names`` (name: string) =
|
||||||
|
Assert.False(isMatch("https://twitter.com/"+name))
|
||||||
|
|
||||||
|
[<Theory>]
|
||||||
|
[<InlineData("tosser")>]
|
||||||
|
[<InlineData("searching")>]
|
||||||
|
let ``accepts accounts starting with reserved page names`` (name: string) =
|
||||||
|
Assert.True(isMatch("https://twitter.com/"+name))
|
||||||
|
|
||||||
|
|
||||||
|
[<Collection("RegexAccount")>]
|
||||||
|
module RegexAccount_Match =
|
||||||
|
let extract str = TwitterUtils.RegexAccount.Match(str).Groups.[1].Value
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``extracts account name from simple URL`` () =
|
||||||
|
Assert.Equal("_abc_DEF_123", extract("https://twitter.com/_abc_DEF_123"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``extracts account name from URL with trailing slash`` () =
|
||||||
|
Assert.Equal("_abc_DEF_123", extract("https://twitter.com/_abc_DEF_123/"))
|
||||||
|
|
||||||
|
|
||||||
|
module GetMediaLink_Default =
|
||||||
|
let getMediaLinkDefault url = TwitterUtils.GetMediaLink(url, TwitterUtils.ImageQuality.Default)
|
||||||
|
let domain = "https://pbs.twimg.com"
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``does not modify URL w/o extension`` () =
|
||||||
|
Assert.Equal(domain+"/media/123", getMediaLinkDefault(domain+"/media/123"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``does not modify URL w/o quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/media/123.jpg", getMediaLinkDefault(domain+"/media/123.jpg"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``does not modify URL with quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/media/123.jpg:small", getMediaLinkDefault(domain+"/media/123.jpg:small"))
|
||||||
|
|
||||||
|
|
||||||
|
module GetMediaLink_Orig =
|
||||||
|
let getMediaLinkOrig url = TwitterUtils.GetMediaLink(url, TwitterUtils.ImageQuality.Orig)
|
||||||
|
let domain = "https://pbs.twimg.com"
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``appends :orig to valid URL w/o quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/media/123.jpg:orig", getMediaLinkOrig(domain+"/media/123.jpg"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rewrites :orig into valid URL with quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/media/123.jpg:orig", getMediaLinkOrig(domain+"/media/123.jpg:small"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``does not modify unknown URL w/o quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/profile_images/123.jpg", getMediaLinkOrig(domain+"/profile_images/123.jpg"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``rewrites :orig into unknown URL with quality suffix`` () =
|
||||||
|
Assert.Equal(domain+"/profile_images/123.jpg:orig", getMediaLinkOrig(domain+"/profile_images/123.jpg:small"))
|
||||||
|
|
||||||
|
|
||||||
|
module GetImageFileName =
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``extracts file name from URL w/o quality suffix`` () =
|
||||||
|
Assert.Equal("test.jpg", TwitterUtils.GetImageFileName("http://example.com/test.jpg"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``extracts file name from URL with quality suffix`` () =
|
||||||
|
Assert.Equal("test.jpg", TwitterUtils.GetImageFileName("http://example.com/test.jpg:orig"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``extracts file name from URL with a port`` () =
|
||||||
|
Assert.Equal("test.jpg", TwitterUtils.GetImageFileName("http://example.com:80/test.jpg"))
|
49
lib/TweetTest.Unit/Data/TestInjectedHTML.fs
Normal file
49
lib/TweetTest.Unit/Data/TestInjectedHTML.fs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
namespace Unit.Data.InjectedHTML
|
||||||
|
|
||||||
|
open Xunit
|
||||||
|
open TweetDuck.Data
|
||||||
|
|
||||||
|
|
||||||
|
module Inject =
|
||||||
|
let before = InjectedHTML.Position.Before
|
||||||
|
let after = InjectedHTML.Position.After
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injecting string before searched string works`` () =
|
||||||
|
Assert.Equal("<p>source[left]<br>code</p>", InjectedHTML(before, "<br>", "[left]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injecting string after searched string works`` () =
|
||||||
|
Assert.Equal("<p>source<br>[right]code</p>", InjectedHTML(after, "<br>", "[right]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injecting string at the beginning works`` () =
|
||||||
|
Assert.Equal("[start]<p>source<br>code</p>", InjectedHTML(before, "<p>", "[start]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injecting string at the end works`` () =
|
||||||
|
Assert.Equal("<p>source<br>code</p>[end]", InjectedHTML(after, "</p>", "[end]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injection only triggers for first occurrence of searched string`` () =
|
||||||
|
Assert.Equal("<p>source[left]<br>code</p><br>", InjectedHTML(before, "<br>", "[left]").InjectInto("<p>source<br>code</p><br>"))
|
||||||
|
Assert.Equal("<p>source<br>[right]code</p><br>", InjectedHTML(after, "<br>", "[right]").InjectInto("<p>source<br>code</p><br>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``empty searched string injects at the beginning`` () =
|
||||||
|
Assert.Equal("[start]<p>source<br>code</p>", InjectedHTML(before, "", "[start]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
Assert.Equal("[start]<p>source<br>code</p>", InjectedHTML(after, "", "[start]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``injecting empty string does not modify source`` () =
|
||||||
|
Assert.Equal("<p>source<br>code</p>", InjectedHTML(before, "<br>", "").InjectInto("<p>source<br>code</p>"))
|
||||||
|
Assert.Equal("<p>source<br>code</p>", InjectedHTML(after, "<br>", "").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``failed match does not modify source`` () =
|
||||||
|
Assert.Equal("<p>source<br>code</p>", InjectedHTML(before, "<wrong>", "[left]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
Assert.Equal("<p>source<br>code</p>", InjectedHTML(after, "<wrong>", "[right]").InjectInto("<p>source<br>code</p>"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``invalid position does not modify source`` () =
|
||||||
|
Assert.Equal("<p>source<br>code</p>", InjectedHTML(enum<_>(1000), "<br>", "[somewhere]").InjectInto("<p>source<br>code</p>"))
|
57
lib/TweetTest.Unit/Data/TestResult.fs
Normal file
57
lib/TweetTest.Unit/Data/TestResult.fs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
namespace Unit.Data.Result
|
||||||
|
|
||||||
|
open Xunit
|
||||||
|
open TweetDuck.Data
|
||||||
|
open System
|
||||||
|
|
||||||
|
|
||||||
|
module Result_WithValue =
|
||||||
|
let result = Result<int>(10)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``HasValue returns true`` () =
|
||||||
|
Assert.True(result.HasValue)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accessing Value returns the provided value`` () =
|
||||||
|
Assert.Equal(10, result.Value)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accessing Exception throws`` () =
|
||||||
|
Assert.Throws<InvalidOperationException>(fun () -> result.Exception |> ignore)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``Handle calls the correct callback`` () =
|
||||||
|
let passTest = fun _ -> ()
|
||||||
|
let failTest = fun _ -> Assert.True(false)
|
||||||
|
result.Handle(Action<_>(passTest), Action<_>(failTest))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``Select returns another valid Result`` () =
|
||||||
|
Assert.Equal(20, result.Select(fun x -> x * 2).Value)
|
||||||
|
|
||||||
|
|
||||||
|
module Result_WithException =
|
||||||
|
let result = Result<int>(IndexOutOfRangeException("bad"))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``HasValue returns false`` () =
|
||||||
|
Assert.False(result.HasValue)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accessing Value throws`` () =
|
||||||
|
Assert.Throws<InvalidOperationException>(fun () -> result.Value |> ignore)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``accessing Exception returns the provided exception`` () =
|
||||||
|
Assert.IsType<IndexOutOfRangeException>(result.Exception)
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``Handle calls the correct callback`` () =
|
||||||
|
let passTest = fun _ -> ()
|
||||||
|
let failTest = fun _ -> Assert.True(false)
|
||||||
|
result.Handle(Action<_>(failTest), Action<_>(passTest))
|
||||||
|
|
||||||
|
[<Fact>]
|
||||||
|
let ``Select returns a Result with the same Exception`` () =
|
||||||
|
Assert.Same(result.Exception, result.Select(fun x -> x * 2).Exception)
|
109
lib/TweetTest.Unit/TweetTest.Unit.fsproj
Normal file
109
lib/TweetTest.Unit/TweetTest.Unit.fsproj
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.props" Condition="Exists('..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.props')" />
|
||||||
|
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.props" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>eee1071a-28fa-48b1-82a1-9cbdc5c3f2c3</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>TweetTest.Unit</RootNamespace>
|
||||||
|
<AssemblyName>TweetTest.Unit</AssemblyName>
|
||||||
|
<UseStandardResourceNames>True</UseStandardResourceNames>
|
||||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
|
<TargetFSharpCoreVersion>4.4.3.0</TargetFSharpCoreVersion>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Name>TweetTest.Unit</Name>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<Tailcalls>false</Tailcalls>
|
||||||
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<WarningLevel>3</WarningLevel>
|
||||||
|
<DocumentationFile>
|
||||||
|
</DocumentationFile>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Choose>
|
||||||
|
<When Condition="'$(VisualStudioVersion)' == '11.0'">
|
||||||
|
<PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets') ">
|
||||||
|
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
</When>
|
||||||
|
<Otherwise>
|
||||||
|
<PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets') ">
|
||||||
|
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Otherwise>
|
||||||
|
</Choose>
|
||||||
|
<Import Project="$(FSharpTargetsPath)" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props'))" />
|
||||||
|
</Target>
|
||||||
|
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.targets" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.targets" Condition="Exists('..\..\packages\Microsoft.NET.Test.Sdk.15.7.2\build\net45\Microsoft.Net.Test.Sdk.targets')" />
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="packages.config" />
|
||||||
|
<Compile Include="Core\TestBrowserUtils.fs" />
|
||||||
|
<Compile Include="Core\TestStringUtils.fs" />
|
||||||
|
<Compile Include="Core\TestTwitterUtils.fs" />
|
||||||
|
<Compile Include="Data\TestInjectedHTML.fs" />
|
||||||
|
<Compile Include="Data\TestResult.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.CodeCoverage.Shim">
|
||||||
|
<HintPath>..\..\packages\Microsoft.CodeCoverage.1.0.3\lib\netstandard1.0\Microsoft.VisualStudio.CodeCoverage.Shim.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="mscorlib" />
|
||||||
|
<Reference Include="FSharp.Core">
|
||||||
|
<Name>FSharp.Core</Name>
|
||||||
|
<AssemblyName>FSharp.Core.dll</AssemblyName>
|
||||||
|
<HintPath>$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="xunit.abstractions">
|
||||||
|
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert">
|
||||||
|
<HintPath>..\..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core">
|
||||||
|
<HintPath>..\..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop">
|
||||||
|
<HintPath>..\..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<ProjectReference Include="..\..\TweetDuck.csproj">
|
||||||
|
<Name>TweetDuck</Name>
|
||||||
|
<Project>{2389a7cd-e0d3-4706-8294-092929a33a2d}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
13
lib/TweetTest.Unit/packages.config
Normal file
13
lib/TweetTest.Unit/packages.config
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.CodeCoverage" version="1.0.3" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.NET.Test.Sdk" version="15.7.2" targetFramework="net452" />
|
||||||
|
<package id="xunit" version="2.3.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.analyzers" version="0.9.0" targetFramework="net452" />
|
||||||
|
<package id="xunit.assert" version="2.3.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.core" version="2.3.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.3.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.3.1" targetFramework="net452" />
|
||||||
|
<package id="xunit.runner.visualstudio" version="2.3.1" targetFramework="net452" developmentDependency="true" />
|
||||||
|
</packages>
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="cef.redist.x64" version="3.3325.1758" targetFramework="net452" />
|
<package id="cef.redist.x64" version="3.3359.1772" targetFramework="net452" />
|
||||||
<package id="cef.redist.x86" version="3.3325.1758" targetFramework="net452" />
|
<package id="cef.redist.x86" version="3.3359.1772" targetFramework="net452" />
|
||||||
<package id="CefSharp.Common" version="65.0.0-pre01" targetFramework="net452" />
|
<package id="CefSharp.Common" version="66.0.0-CI2629" targetFramework="net452" />
|
||||||
<package id="CefSharp.WinForms" version="65.0.0-pre01" targetFramework="net452" />
|
<package id="CefSharp.WinForms" version="66.0.0-CI2629" targetFramework="net452" />
|
||||||
</packages>
|
</packages>
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<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')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
<StartupObject />
|
<StartupObject />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=65.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=66.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\CefSharp.Common.65.0.0-pre01\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
<HintPath>..\packages\CefSharp.Common.66.0.0-CI2629\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace UnitTests.Core{
|
|
||||||
[TestClass]
|
|
||||||
public class TestBrowserUtils{
|
|
||||||
[TestMethod]
|
|
||||||
public void TestIsValidUrl(){
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com")); // base
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://www.google.com")); // www.
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.co.uk")); // co.uk
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("https://google.com")); // https
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("ftp://google.com")); // ftp
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("mailto:someone@google.com")); // mailto
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/")); // trailing slash
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?")); // trailing question mark
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x")); // parameters
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/#hash")); // parameters + hash
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
|
||||||
|
|
||||||
foreach(string tld in new string[]{ "accountants", "blackfriday", "cancerresearch", "coffee", "cool", "foo", "travelersinsurance" }){
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://test."+tld)); // long and unusual TLDs
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("http://t.co/abc")); // tracking
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("https://t.co/abc")); // tracking
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer")); // file
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer.exe")); // file
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("://explorer.exe")); // file-sorta
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("file://explorer.exe")); // file-proper
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("")); // empty
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("lol")); // random
|
|
||||||
|
|
||||||
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("gopher://nobody.cares")); // lmao rekt
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestGetFileNameFromUrl(){
|
|
||||||
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html"));
|
|
||||||
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?"));
|
|
||||||
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?param1=abc¶m2=false"));
|
|
||||||
|
|
||||||
Assert.AreEqual("index", BrowserUtils.GetFileNameFromUrl("http://test.com/index"));
|
|
||||||
Assert.AreEqual("index.", BrowserUtils.GetFileNameFromUrl("http://test.com/index."));
|
|
||||||
|
|
||||||
Assert.IsNull(BrowserUtils.GetFileNameFromUrl("http://test.com/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,36 +0,0 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace UnitTests.Core{
|
|
||||||
[TestClass]
|
|
||||||
public class TestStringUtils{
|
|
||||||
[TestMethod]
|
|
||||||
public void TestExtractBefore(){
|
|
||||||
Assert.AreEqual("missing", StringUtils.ExtractBefore("missing", '_'));
|
|
||||||
Assert.AreEqual("", StringUtils.ExtractBefore("_empty", '_'));
|
|
||||||
Assert.AreEqual("some", StringUtils.ExtractBefore("some_text", '_'));
|
|
||||||
Assert.AreEqual("first", StringUtils.ExtractBefore("first_separator_only", '_'));
|
|
||||||
Assert.AreEqual("start_index", StringUtils.ExtractBefore("start_index_test", '_', 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestParseInts(){
|
|
||||||
CollectionAssert.AreEqual(new int[0], StringUtils.ParseInts("", ','));
|
|
||||||
CollectionAssert.AreEqual(new int[]{ 1 }, StringUtils.ParseInts("1", ','));
|
|
||||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts("1,2,3", ','));
|
|
||||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts("1,2,3,", ','));
|
|
||||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts(",1,2,,3,", ','));
|
|
||||||
CollectionAssert.AreEqual(new int[]{ -50, 50 }, StringUtils.ParseInts("-50,50", ','));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestConvertPascalCaseToScreamingSnakeCase(){
|
|
||||||
Assert.AreEqual("HELP", StringUtils.ConvertPascalCaseToScreamingSnakeCase("Help"));
|
|
||||||
Assert.AreEqual("HELP_ME", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMe"));
|
|
||||||
Assert.AreEqual("HELP_ME_PLEASE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMePlease"));
|
|
||||||
|
|
||||||
Assert.AreEqual("HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HTMLCode"));
|
|
||||||
Assert.AreEqual("CHECK_OUT_MY_HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("CheckOutMyHTMLCode"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,57 +0,0 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace UnitTests.Core{
|
|
||||||
[TestClass]
|
|
||||||
public class TestTwitterUtils{
|
|
||||||
[TestMethod]
|
|
||||||
public void TestAccountRegex(){
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc"));
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc"));
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/"));
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc/"));
|
|
||||||
|
|
||||||
Assert.AreEqual("chylexmc", TwitterUtils.RegexAccount.Match("http://twitter.com/chylexmc").Groups[1].Value);
|
|
||||||
Assert.AreEqual("123", TwitterUtils.RegexAccount.Match("http://twitter.com/123").Groups[1].Value);
|
|
||||||
Assert.AreEqual("_", TwitterUtils.RegexAccount.Match("http://twitter.com/_").Groups[1].Value);
|
|
||||||
|
|
||||||
Assert.AreEqual("Abc_123", TwitterUtils.RegexAccount.Match("http://twitter.com/Abc_123").Groups[1].Value);
|
|
||||||
Assert.AreEqual("Abc_123", TwitterUtils.RegexAccount.Match("https://twitter.com/Abc_123/").Groups[1].Value);
|
|
||||||
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/status"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://nottwitter.com/chylexmc"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc?"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("www.twitter.com/chylexmc"));
|
|
||||||
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/signup"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/tos"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/privacy"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search?query"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search-home"));
|
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search-advanced"));
|
|
||||||
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/tosser"));
|
|
||||||
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/searching"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestImageQualityLink(){
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Default));
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Orig));
|
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Default));
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Orig));
|
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Default));
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Orig));
|
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Default));
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Orig));
|
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Default));
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Orig));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,64 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using TweetDuck.Data;
|
|
||||||
|
|
||||||
namespace UnitTests.Data{
|
|
||||||
[TestClass]
|
|
||||||
public class TestInjectedHTML{
|
|
||||||
private static IEnumerable<InjectedHTML.Position> Positions => Enum.GetValues(typeof(InjectedHTML.Position)).Cast<InjectedHTML.Position>();
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestFailedMatches(){
|
|
||||||
foreach(var pos in Positions){
|
|
||||||
Assert.AreEqual(string.Empty, new InjectedHTML(pos, "b", "b").Inject(string.Empty));
|
|
||||||
Assert.AreEqual("aaaa", new InjectedHTML(pos, "b", "b").Inject("aaaa"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestEmptySearch(){
|
|
||||||
foreach(var pos in Positions){
|
|
||||||
Assert.AreEqual("b", new InjectedHTML(pos, string.Empty, "b").Inject(string.Empty));
|
|
||||||
Assert.AreEqual("baaaa", new InjectedHTML(pos, string.Empty, "b").Inject("aaaa"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestEmptyHTML(){
|
|
||||||
foreach(var pos in Positions){
|
|
||||||
Assert.AreEqual(string.Empty, new InjectedHTML(pos, string.Empty, string.Empty).Inject(string.Empty));
|
|
||||||
Assert.AreEqual("aaaa", new InjectedHTML(pos, string.Empty, string.Empty).Inject("aaaa"));
|
|
||||||
Assert.AreEqual("aaaa", new InjectedHTML(pos, "a", string.Empty).Inject("aaaa"));
|
|
||||||
Assert.AreEqual("aaaa", new InjectedHTML(pos, "b", string.Empty).Inject("aaaa"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestInvalidPosition(){
|
|
||||||
Assert.AreEqual("aaaa", new InjectedHTML((InjectedHTML.Position)(Positions.Count()+1), "a", "b").Inject("aaaa"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestPositions(){
|
|
||||||
Assert.AreEqual("aaabcxaaa", new InjectedHTML(InjectedHTML.Position.Before, "x", "bc").Inject("aaaxaaa"));
|
|
||||||
Assert.AreEqual("aaaxbcaaa", new InjectedHTML(InjectedHTML.Position.After, "x", "bc").Inject("aaaxaaa"));
|
|
||||||
|
|
||||||
Assert.AreEqual("bcxaaa", new InjectedHTML(InjectedHTML.Position.Before, "x", "bc").Inject("xaaa"));
|
|
||||||
Assert.AreEqual("xbcaaa", new InjectedHTML(InjectedHTML.Position.After, "x", "bc").Inject("xaaa"));
|
|
||||||
|
|
||||||
Assert.AreEqual("aaabcx", new InjectedHTML(InjectedHTML.Position.Before, "x", "bc").Inject("aaax"));
|
|
||||||
Assert.AreEqual("aaaxbc", new InjectedHTML(InjectedHTML.Position.After, "x", "bc").Inject("aaax"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TestFirstOccurrence(){
|
|
||||||
Assert.AreEqual("bcaaaa", new InjectedHTML(InjectedHTML.Position.Before, "a", "bc").Inject("aaaa"));
|
|
||||||
Assert.AreEqual("abcaaa", new InjectedHTML(InjectedHTML.Position.After, "a", "bc").Inject("aaaa"));
|
|
||||||
|
|
||||||
Assert.AreEqual("bcaaaa", new InjectedHTML(InjectedHTML.Position.Before, "aa", "bc").Inject("aaaa"));
|
|
||||||
Assert.AreEqual("aabcaa", new InjectedHTML(InjectedHTML.Position.After, "aa", "bc").Inject("aaaa"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -24,11 +24,6 @@
|
|||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
<LangVersion>7</LangVersion>
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core">
|
<Reference Include="System.Core">
|
||||||
@@ -49,13 +44,9 @@
|
|||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Configuration\TestUserConfig.cs" />
|
<Compile Include="Configuration\TestUserConfig.cs" />
|
||||||
<Compile Include="Core\TestStringUtils.cs" />
|
|
||||||
<Compile Include="Core\TestTwitterUtils.cs" />
|
|
||||||
<Compile Include="Data\TestCombinedFileStream.cs" />
|
<Compile Include="Data\TestCombinedFileStream.cs" />
|
||||||
<Compile Include="Core\TestBrowserUtils.cs" />
|
|
||||||
<Compile Include="Data\TestCommandLineArgs.cs" />
|
<Compile Include="Data\TestCommandLineArgs.cs" />
|
||||||
<Compile Include="Data\TestFileSerializer.cs" />
|
<Compile Include="Data\TestFileSerializer.cs" />
|
||||||
<Compile Include="Data\TestInjectedHTML.cs" />
|
|
||||||
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="UnitTestIO.cs" />
|
<Compile Include="UnitTestIO.cs" />
|
||||||
|
Reference in New Issue
Block a user