--- /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.
+
+using System;
+using Xunit;
+
+namespace OpenTween.SocialProtocol
+{
+ public class AccountCollectionTest
+ {
+ private readonly Random random = new();
+
+ private UserAccount CreateAccountSetting(string key)
+ {
+ return new()
+ {
+ UniqueKey = new(key),
+ TwitterAuthType = APIAuthType.OAuth1,
+ Token = "aaaaa",
+ TokenSecret = "bbbbb",
+ UserId = this.random.Next(),
+ Username = "tetete",
+ };
+ }
+
+ [Fact]
+ public void LoadFromSettings_Test()
+ {
+ using var accounts = new AccountCollection();
+
+ var settingCommon = new SettingCommon
+ {
+ UserAccounts = new()
+ {
+ this.CreateAccountSetting("00000000-0000-4000-8000-000000000000"),
+ },
+ SelectedAccountKey = new("00000000-0000-4000-8000-000000000000"),
+ };
+ accounts.LoadFromSettings(settingCommon);
+
+ Assert.Single(accounts.Items);
+ Assert.Equal(settingCommon.UserAccounts[0].UserId, accounts.Primary.UserId);
+ }
+
+ [Fact]
+ public void LoadFromSettings_RemoveTest()
+ {
+ using var accounts = new AccountCollection();
+
+ var settingCommon1 = new SettingCommon
+ {
+ UserAccounts = new()
+ {
+ this.CreateAccountSetting("00000000-0000-4000-8000-000000000000"),
+ },
+ SelectedAccountKey = new("00000000-0000-4000-8000-000000000000"),
+ };
+ accounts.LoadFromSettings(settingCommon1);
+
+ var accountItem1 = Assert.Single(accounts.Items);
+ Assert.Equal(settingCommon1.UserAccounts[0].UserId, accounts.Primary.UserId);
+
+ var settingCommon2 = new SettingCommon
+ {
+ UserAccounts = new()
+ {
+ // 00000000-0000-4000-8000-000000000000 は削除
+ },
+ SelectedAccountKey = null,
+ };
+ accounts.LoadFromSettings(settingCommon2);
+
+ // 欠けている ID は削除される
+ Assert.Empty(accounts.Items);
+ Assert.Equal(APIAuthType.None, accounts.Primary.Api.AuthType);
+ Assert.True(accountItem1.IsDisposed);
+ }
+
+ [Fact]
+ public void LoadFromSettings_ReconfigureTest()
+ {
+ using var accounts = new AccountCollection();
+
+ var settingCommon1 = new SettingCommon
+ {
+ UserAccounts = new()
+ {
+ this.CreateAccountSetting("00000000-0000-4000-8000-000000000000"),
+ },
+ SelectedAccountKey = new("00000000-0000-4000-8000-000000000000"),
+ };
+ accounts.LoadFromSettings(settingCommon1);
+
+ var accountItem1 = Assert.Single(accounts.Items);
+ Assert.Equal(settingCommon1.UserAccounts[0].UserId, accounts.Primary.UserId);
+
+ var settingCommon2 = new SettingCommon
+ {
+ UserAccounts = new()
+ {
+ // 同一の ID だが再認証により UserId が変わっている
+ this.CreateAccountSetting("00000000-0000-4000-8000-000000000000"),
+ },
+ SelectedAccountKey = new("00000000-0000-4000-8000-000000000000"),
+ };
+ accounts.LoadFromSettings(settingCommon2);
+
+ var accountItem2 = Assert.Single(accounts.Items);
+ Assert.Equal(settingCommon2.UserAccounts[0].UserId, accounts.Primary.UserId);
+
+ // 同一の ID は同じインスタンスを使用
+ Assert.Same(accountItem1, accountItem2);
+ Assert.NotEqual(
+ settingCommon1.UserAccounts[0].UserId,
+ accountItem2.UserId
+ );
+ Assert.Equal(
+ settingCommon2.UserAccounts[0].UserId,
+ accountItem2.UserId
+ );
+ Assert.False(accountItem2.IsDisposed);
+ }
+ }
+}
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
-using OpenTween.Api;
using OpenTween.Api.DataModel;
-using OpenTween.Connection;
using OpenTween.Models;
using OpenTween.OpenTweenCustomControl;
using OpenTween.Setting;
+using OpenTween.SocialProtocol;
using OpenTween.Thumbnail;
using Xunit;
using Xunit.Extensions;
{
var settings = new SettingManager("");
var tabinfo = new TabInformations();
- using var twitterApi = new TwitterApi();
- using var twitter = new Twitter(twitterApi);
+ using var accounts = new AccountCollection();
using var imageCache = new ImageCache();
using var iconAssets = new IconAssetsManager();
var thumbnailGenerator = new ThumbnailGenerator(new(autoupdate: false));
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.SetField);
field.SetValue(null, tabinfo);
- using var tweenMain = new TweenMain(settings, tabinfo, twitter, imageCache, iconAssets, thumbnailGenerator);
+ using var tweenMain = new TweenMain(settings, tabinfo, accounts, imageCache, iconAssets, thumbnailGenerator);
var context = new TestContext(settings, tabinfo);
func(tweenMain, context);
#nullable enable
using System;
-using OpenTween.Api;
using OpenTween.Models;
using OpenTween.Setting;
+using OpenTween.SocialProtocol;
using OpenTween.Thumbnail;
using OpenTween.Thumbnail.Services;
public CultureService CultureService
=> this.cultureServiceLazy.Value;
- public TwitterApi TwitterApi
- => this.twitterApiLazy.Value;
-
- public Twitter Twitter
- => this.twitterLazy.Value;
+ public AccountCollection AccountCollection
+ => this.accountCollectionLazy.Value;
public ImageCache ImageCache
=> this.imageCacheLazy.Value;
=> this.mainFormLazy.Value;
private readonly Lazy<CultureService> cultureServiceLazy;
- private readonly DisposableLazy<TwitterApi> twitterApiLazy;
- private readonly DisposableLazy<Twitter> twitterLazy;
+ private readonly DisposableLazy<AccountCollection> accountCollectionLazy;
private readonly DisposableLazy<ImageCache> imageCacheLazy;
private readonly DisposableLazy<IconAssetsManager> iconAssetsManagerLazy;
private readonly DisposableLazy<ImgAzyobuziNet> imgAzyobuziNetLazy;
SettingManager.Instance = settings;
this.cultureServiceLazy = new(this.CreateCultureService);
- this.twitterApiLazy = new(this.CreateTwitterApi);
- this.twitterLazy = new(this.CreateTwitter);
+ this.accountCollectionLazy = new(this.CreateAccountCollection);
this.imageCacheLazy = new(this.CreateImageCache);
this.iconAssetsManagerLazy = new(this.CreateIconAssetsManager);
this.imgAzyobuziNetLazy = new(this.CreateImgAzyobuziNet);
private CultureService CreateCultureService()
=> new(this.Settings.Common);
- private TwitterApi CreateTwitterApi()
+ private AccountCollection CreateAccountCollection()
=> new();
- private Twitter CreateTwitter()
- => new(this.TwitterApi);
-
private ImageCache CreateImageCache()
=> new();
=> new(this.ImgAzyobuziNet);
private TweenMain CreateTweenMain()
- => new(this.Settings, this.TabInfo, this.Twitter, this.ImageCache, this.IconAssetsManager, this.ThumbnailGenerator);
+ => new(this.Settings, this.TabInfo, this.AccountCollection, this.ImageCache, this.IconAssetsManager, this.ThumbnailGenerator);
public void Dispose()
{
this.IsDisposed = true;
this.mainFormLazy.Dispose();
this.imgAzyobuziNetLazy.Dispose();
- this.twitterLazy.Dispose();
- this.twitterApiLazy.Dispose();
+ this.accountCollectionLazy.Dispose();
this.iconAssetsManagerLazy.Dispose();
this.imageCacheLazy.Dispose();
}
using System.Windows.Forms;
using OpenTween.Connection;
using OpenTween.Setting;
+using OpenTween.SocialProtocol;
namespace OpenTween
{
return 1; // 設定が完了しなかったため終了
}
- SetupTwitter(container.Twitter, settings);
+ SetupAccounts(container.AccountCollection, settings);
Application.Run(container.MainForm);
return true;
}
- private static void SetupTwitter(Twitter tw, SettingManager settings)
+ private static void SetupAccounts(AccountCollection accounts, SettingManager settings)
{
- var account = settings.Common.SelectedAccount;
- if (account != null)
- tw.Initialize(account.GetTwitterCredential(), account.Username, account.UserId);
- else
- tw.Initialize(new TwitterCredentialNone(), "", 0L);
-
- tw.RestrictFavCheck = settings.Common.RestrictFavCheck;
- tw.ReadOwnPost = settings.Common.ReadOwnPost;
+ accounts.LoadFromSettings(settings.Common);
// アクセストークンが有効であるか確認する
// ここが Twitter API への最初のアクセスになるようにすること
try
{
- tw.VerifyCredentials();
+ accounts.Primary.VerifyCredentials();
}
catch (WebApiException ex)
{
--- /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;
+
+namespace OpenTween.SocialProtocol
+{
+ public sealed class AccountCollection : IDisposable
+ {
+ private Dictionary<Guid, Twitter> accounts = new();
+ private Guid? primaryId;
+ private readonly Twitter emptyAccount = new(new());
+
+ public bool IsDisposed { get; private set; }
+
+ public Twitter Primary
+ => this.primaryId != null ? this.accounts[this.primaryId.Value] : this.emptyAccount;
+
+ public Twitter[] Items
+ => this.accounts.Values.ToArray();
+
+ public void LoadFromSettings(SettingCommon settingCommon)
+ {
+ var oldAccounts = this.accounts;
+ var newAccounts = new Dictionary<Guid, Twitter>();
+
+ foreach (var accountSettings in settingCommon.UserAccounts)
+ {
+ var accountKey = accountSettings.UniqueKey;
+ if (!oldAccounts.TryGetValue(accountKey, out var account))
+ account = new(new());
+
+ var credential = accountSettings.GetTwitterCredential();
+ account.Initialize(credential, accountSettings.Username, accountSettings.UserId);
+
+ account.RestrictFavCheck = settingCommon.RestrictFavCheck;
+ account.ReadOwnPost = settingCommon.ReadOwnPost;
+
+ newAccounts[accountKey] = account;
+ }
+
+ this.accounts = newAccounts;
+ this.primaryId = settingCommon.SelectedAccountKey;
+
+ var removedAccounts = oldAccounts
+ .Where(x => !newAccounts.ContainsKey(x.Key))
+ .Select(x => x.Value);
+
+ this.DisposeAccounts(removedAccounts);
+ }
+
+ public void Dispose()
+ {
+ if (this.IsDisposed)
+ return;
+
+ this.emptyAccount.Dispose();
+ this.DisposeAccounts(this.accounts.Values);
+
+ this.IsDisposed = true;
+ }
+
+ private void DisposeAccounts(IEnumerable<Twitter> accounts)
+ {
+ foreach (var account in accounts)
+ account.Dispose();
+ }
+ }
+}
using OpenTween.Models;
using OpenTween.OpenTweenCustomControl;
using OpenTween.Setting;
+using OpenTween.SocialProtocol;
using OpenTween.Thumbnail;
namespace OpenTween
// 設定ファイル
private readonly SettingManager settings;
- // twitter解析部
- private readonly Twitter tw;
+ // ユーザーアカウント
+ private readonly AccountCollection accounts;
+
+#pragma warning disable SA1300
+ private Twitter tw => this.accounts.Primary; // AccountCollection への移行用
+#pragma warning restore SA1300
// Growl呼び出し部
private readonly GrowlHelper gh = new(ApplicationSettings.ApplicationName);
public TweenMain(
SettingManager settingManager,
TabInformations tabInfo,
- Twitter twitter,
+ AccountCollection accounts,
ImageCache imageCache,
IconAssetsManager iconAssets,
ThumbnailGenerator thumbGenerator
{
this.settings = settingManager;
this.statuses = tabInfo;
- this.tw = twitter;
+ this.accounts = accounts;
this.iconCache = imageCache;
this.iconAssets = iconAssets;
this.thumbGenerator = thumbGenerator;
{
this.settings.ApplySettings();
- if (MyCommon.IsNullOrEmpty(this.settings.Common.Token))
- this.tw.ClearAuthInfo();
-
- var account = this.settings.Common.SelectedAccount;
- if (account != null)
- this.tw.Initialize(account.GetTwitterCredential(), account.Username, account.UserId);
- else
- this.tw.Initialize(new TwitterCredentialNone(), "", 0L);
-
- this.tw.RestrictFavCheck = this.settings.Common.RestrictFavCheck;
- this.tw.ReadOwnPost = this.settings.Common.ReadOwnPost;
-
+ this.accounts.LoadFromSettings(this.settings.Common);
this.ImageSelector.Model.InitializeServices(this.tw, this.tw.Configuration);
try