* NEW: Twemoji 15.1.0 に対応しました
- Unicode 15.1 で追加された絵文字が表示されるようになります
* CHG: 設定画面でのアカウント一覧の表示形式を変更
+ * CHG: 新規アカウント追加時のダイアログの構成を変更
* CHG: 新規タブの初回に読み込まれた発言を既読状態にする(起動時の初回の読み込みと同じ動作となる)
* CHG: ドメインに x.com が使われている引用ツイートの展開・投稿に対応
* FIX: 発言の削除中にタブを切り替えるとエラーが発生する不具合を修正 (thx @Tan90909090!)
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
-using OpenTween.Api;
using OpenTween.Connection;
using OpenTween.Models;
using OpenTween.Setting.Panel;
{
this.InitializeComponent();
- this.BasedPanel.AddAccountButton.Click += this.AddAccountButton_Click;
+ this.BasedPanel.ApplyNetworkSettings = this.ApplyNetworkSettings;
+ this.BasedPanel.OpenInBrowser = this.OpenInBrowser;
this.GetPeriodPanel.CheckPostAndGet.CheckedChanged += this.CheckPostAndGet_CheckedChanged;
this.ActionPanel.UReadMng.CheckedChanged += this.UReadMng_CheckedChanged;
}
}
- private async void AddAccountButton_Click(object sender, EventArgs e)
- {
- using (ControlTransaction.Disabled(this.BasedPanel.AddAccountButton))
- {
- try
- {
- this.ApplyNetworkSettings();
-
- var appToken = this.SelectAuthType();
- if (appToken == null)
- return;
-
- UserAccount newAccount;
- if (appToken.AuthType == APIAuthType.TwitterComCookie)
- {
- newAccount = new()
- {
- TwitterAuthType = appToken.AuthType,
- TwitterComCookie = appToken.TwitterComCookie,
- };
-
- using var twitterApi = new TwitterApi();
- using var apiConnection = new TwitterApiConnection(new TwitterCredentialCookie(appToken), new());
- twitterApi.Initialize(apiConnection);
- var twitterUser = await twitterApi.AccountVerifyCredentials();
- newAccount.UserId = twitterUser.IdStr;
- newAccount.Username = twitterUser.ScreenName;
- }
- else
- {
- var account = await this.PinAuth(appToken);
- if (account == null)
- return;
- newAccount = account;
- }
-
- this.BasedPanel.AddAccount(newAccount);
-
- MessageBox.Show(
- this,
- Properties.Resources.AuthorizeButton_Click1,
- "Authenticate",
- MessageBoxButtons.OK);
- }
- catch (TwitterApiException ex)
- {
- var message = Properties.Resources.AuthorizeButton_Click2 + Environment.NewLine +
- string.Join(Environment.NewLine, ex.LongMessages);
-
- MessageBox.Show(this, message, "Authenticate", MessageBoxButtons.OK);
- }
- }
- }
-
/// <summary>
/// 現在設定画面に入力されているネットワーク関係の設定を適用します
/// </summary>
TwitterApiConnection.RestApiHost = this.ConnectionPanel.TwitterAPIText.Text.Trim();
}
- private TwitterAppToken? SelectAuthType()
- {
- using var dialog = new AuthTypeSelectDialog();
-
- var ret = dialog.ShowDialog(this);
- if (ret != DialogResult.OK)
- return null;
-
- return dialog.Result;
- }
-
- private async Task<UserAccount?> PinAuth(TwitterAppToken appToken)
+ private async Task OpenInBrowser(IWin32Window? owner, Uri uri)
{
- var requestToken = await TwitterApiConnection.GetRequestTokenAsync(appToken);
-
- var pinPageUrl = TwitterApiConnection.GetAuthorizeUri(requestToken);
-
var browserPath = this.ActionPanel.BrowserPathText.Text;
- var pin = AuthDialog.DoAuth(this, pinPageUrl, browserPath);
- if (MyCommon.IsNullOrEmpty(pin))
- return null; // キャンセルされた場合
-
- var accessTokenResponse = await TwitterApiConnection.GetAccessTokenAsync(requestToken, pin);
-
- return new UserAccount
- {
- TwitterAuthType = appToken.AuthType,
- TwitterOAuth1ConsumerKey = appToken.OAuth1CustomConsumerKey?.Value ?? "",
- TwitterOAuth1ConsumerSecret = appToken.OAuth1CustomConsumerSecret?.Value ?? "",
- Username = accessTokenResponse["screen_name"],
- UserId = accessTokenResponse["user_id"],
- Token = accessTokenResponse["oauth_token"],
- TokenSecret = accessTokenResponse["oauth_token_secret"],
- };
+ await MyCommon.OpenInBrowserAsync(owner, browserPath, uri);
}
private void CheckPostAndGet_CheckedChanged(object sender, EventArgs e)
/// </summary>
private void InitializeComponent()
{
+ this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BasedPanel));
this.AccountListLabel = new System.Windows.Forms.Label();
this.AccountsListBox = new System.Windows.Forms.ListBox();
this.RemoveAccountButton = new System.Windows.Forms.Button();
this.MakePrimaryButton = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
+ this.contextMenuAddAccount = new System.Windows.Forms.ContextMenuStrip(this.components);
this.panel1.SuspendLayout();
this.SuspendLayout();
//
resources.ApplyResources(this.AddAccountButton, "AddAccountButton");
this.AddAccountButton.Name = "AddAccountButton";
this.AddAccountButton.UseVisualStyleBackColor = true;
+ this.AddAccountButton.Click += new System.EventHandler(this.AddAccountButton_Click);
//
// RemoveAccountButton
//
resources.ApplyResources(this.panel1, "panel1");
this.panel1.Name = "panel1";
//
+ // contextMenuAddAccount
+ //
+ this.contextMenuAddAccount.Name = "contextMenuAddAccount";
+ resources.ApplyResources(this.contextMenuAddAccount, "contextMenuAddAccount");
+ //
// BasedPanel
//
resources.ApplyResources(this, "$this");
internal System.Windows.Forms.Button RemoveAccountButton;
internal System.Windows.Forms.Button MakePrimaryButton;
private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.ContextMenuStrip contextMenuAddAccount;
}
}
using System.ComponentModel;
using System.Data;
using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using OpenTween.SocialProtocol;
namespace OpenTween.Setting.Panel
{
{
internal BindingList<AccountListBoxItem> AccountsList { get; } = new();
+ internal Action? ApplyNetworkSettings { get; set; }
+
+ internal Func<IWin32Window?, Uri, Task>? OpenInBrowser { get; set; }
+
+ private readonly AccountSetupDispatcher setupDispatcher = new();
+
internal record AccountListBoxItem(UserAccount AccountSettings, bool IsPrimary)
{
public string DisplayText
{
this.InitializeComponent();
this.InitializeBinding();
+ this.InitializeAddAccountDropdown();
}
private void InitializeBinding()
this.AccountsListBox.DisplayMember = nameof(AccountListBoxItem.DisplayText);
}
+ private void InitializeAddAccountDropdown()
+ {
+ foreach (var (id, caption) in this.setupDispatcher.GetCaptions())
+ {
+ var menuItem = new ToolStripMenuItem
+ {
+ Text = caption,
+ Tag = id,
+ };
+ menuItem.Click += this.AddAccountMenuItem_Click;
+
+ this.contextMenuAddAccount.Items.Add(menuItem);
+ this.components.Add(menuItem);
+ }
+ }
+
public void LoadConfig(SettingCommon settingCommon)
{
using (ControlTransaction.Update(this.AccountsListBox))
this.AccountsList[index] with { IsPrimary = true };
}
+ private void AddAccountButton_Click(object sender, EventArgs e)
+ {
+ this.contextMenuAddAccount.Show(
+ this.AddAccountButton,
+ new(x: 0, y: this.AddAccountButton.Height)
+ );
+ }
+
+ private void AddAccountMenuItem_Click(object sender, EventArgs e)
+ {
+ var setupId = (Guid)((ToolStripMenuItem)sender).Tag;
+
+ this.ApplyNetworkSettings?.Invoke();
+
+ var authorizedAccount = this.setupDispatcher.Dispatch(this, setupId, this.OpenInBrowser);
+ if (authorizedAccount == null)
+ return;
+
+ this.AddAccount(authorizedAccount);
+
+ MessageBox.Show(
+ this,
+ Properties.Resources.AuthorizeButton_Click1,
+ "Authenticate",
+ MessageBoxButtons.OK
+ );
+ }
+
private void RemoveAccountButton_Click(object sender, EventArgs e)
{
var selectedIndex = this.AccountsListBox.SelectedIndex;
<data name=">>AddAccountButton.Parent"><value>panel1</value></data>
<data name=">>AddAccountButton.Type"><value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></data>
<data name=">>AddAccountButton.ZOrder"><value>2</value></data>
+ <data name=">>contextMenuAddAccount.Name"><value>contextMenuAddAccount</value></data>
+ <data name=">>contextMenuAddAccount.Type"><value>System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></data>
<data name=">>MakePrimaryButton.Name"><value>MakePrimaryButton</value></data>
<data name=">>MakePrimaryButton.Parent"><value>panel1</value></data>
<data name=">>MakePrimaryButton.Type"><value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></data>
<data name=">>panel1.Name"><value>panel1</value></data>
<data name=">>panel1.Parent"><value>$this</value></data>
<data name=">>panel1.Type"><value>System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></data>
- <data name=">>panel1.ZOrder"><value>0</value></data>
+ <data name=">>panel1.ZOrder"><value>1</value></data>
<data name=">>RemoveAccountButton.Name"><value>RemoveAccountButton</value></data>
<data name=">>RemoveAccountButton.Parent"><value>panel1</value></data>
<data name=">>RemoveAccountButton.Type"><value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></data>
<data name="AddAccountButton.Size" type="System.Drawing.Size, System.Drawing"><value>100, 25</value></data>
<data name="AddAccountButton.TabIndex" type="System.Int32, mscorlib"><value>2</value></data>
<data name="AddAccountButton.Text"><value>追加</value></data>
+ <data name="contextMenuAddAccount.Size" type="System.Drawing.Size, System.Drawing"><value>181, 26</value></data>
+ <metadata name="contextMenuAddAccount.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"><value>17, 17</value></metadata>
<data name="MakePrimaryButton.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"><value>Top, Right</value></data>
<data name="MakePrimaryButton.AutoSize" type="System.Boolean, mscorlib"><value>True</value></data>
<data name="MakePrimaryButton.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms"><value>NoControl</value></data>
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2024 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using OpenTween.SocialProtocol.Twitter;
+
+namespace OpenTween.SocialProtocol
+{
+ public class AccountSetupDispatcher
+ {
+ public record AccountSetupItem(
+ Guid Id,
+ string Caption,
+ Func<IAccountFactory> CreateInstance
+ );
+
+ private readonly List<AccountSetupItem> setupList;
+
+ public AccountSetupDispatcher()
+ {
+ this.setupList = new()
+ {
+ new(Guid.NewGuid(), "Twitter (OAuth)", () => new TwitterOAuthSetupDialog()),
+ new(Guid.NewGuid(), "Twitter (Cookie)", () => new TwitterCookieSetupDialog()),
+ };
+ }
+
+ public (Guid Id, string Caption)[] GetCaptions()
+ => this.setupList.Select(x => (x.Id, x.Caption)).ToArray();
+
+ public UserAccount? Dispatch(IWin32Window? owner, Guid setupId, Func<IWin32Window?, Uri, Task>? openInBrowser)
+ {
+ var setupItem = this.setupList.First(x => x.Id == setupId);
+
+ using var setup = setupItem.CreateInstance();
+ setup.OpenInBrowser = openInBrowser;
+
+ return setup.ShowAccountSetupDialog(owner);
+ }
+ }
+}
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2024 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+#nullable enable
+
+using System;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace OpenTween.SocialProtocol
+{
+ public interface IAccountFactory : IDisposable
+ {
+ public Func<IWin32Window?, Uri, Task>? OpenInBrowser { get; set; }
+
+ public UserAccount? ShowAccountSetupDialog(IWin32Window? owner);
+ }
+}
#nullable enable
using System;
+using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTween.Api;
namespace OpenTween.SocialProtocol.Twitter
{
- public partial class TwitterCookieSetupDialog : OTBaseForm
+ public partial class TwitterCookieSetupDialog : OTBaseForm, IAccountFactory
{
public TwitterCookieSetup Model { get; } = new();
+ public Func<IWin32Window?, Uri, Task>? OpenInBrowser { get; set; }
+
public TwitterCookieSetupDialog()
{
this.InitializeComponent();
);
}
+ public UserAccount? ShowAccountSetupDialog(IWin32Window? owner)
+ {
+ var ret = this.ShowDialog(owner);
+ if (ret != DialogResult.OK)
+ return null;
+
+ return this.Model.AuthorizedAccount!;
+ }
+
private async void ButtonOK_Click(object sender, EventArgs e)
{
if (MyCommon.IsNullOrEmpty(this.Model.TwitterComCookie))
using System;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTween.Api;
namespace OpenTween.SocialProtocol.Twitter
{
- public partial class TwitterOAuthSetupDialog : OTBaseForm
+ public partial class TwitterOAuthSetupDialog : OTBaseForm, IAccountFactory
{
public TwitterOAuthSetup Model { get; } = new();
+ public Func<IWin32Window?, Uri, Task>? OpenInBrowser { get; set; }
+
public TwitterOAuthSetupDialog()
{
this.InitializeComponent();
);
}
+ public UserAccount? ShowAccountSetupDialog(IWin32Window? owner)
+ {
+ var ret = this.ShowDialog(owner);
+ if (ret != DialogResult.OK)
+ return null;
+
+ return this.Model.AuthorizedAccount!;
+ }
+
private async void ButtonGetAuthorizeUri_Click(object sender, EventArgs e)
{
using (ControlTransaction.Disabled(this))
if (e.Button == MouseButtons.Right)
return;
- await MyCommon.OpenInBrowserAsync(this, this.Model.AuthorizeUri);
+ if (this.OpenInBrowser == null)
+ throw new InvalidOperationException($"{nameof(this.OpenInBrowser)} is not set");
+
+ await this.OpenInBrowser(this, this.Model.AuthorizeUri);
}
private void MenuItemCopyLink_Click(object sender, EventArgs e)