OSDN Git Service

AccountCollectionクラスを追加
authorKimura Youichi <kim.upsilon@bucyou.net>
Tue, 16 Jan 2024 16:31:00 +0000 (01:31 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Tue, 16 Jan 2024 19:39:41 +0000 (04:39 +0900)
OpenTween.Tests/SocialProtocol/AccountCollectionTest.cs [new file with mode: 0644]
OpenTween.Tests/TweenMainTest.cs
OpenTween/ApplicationContainer.cs
OpenTween/ApplicationEvents.cs
OpenTween/SocialProtocol/AccountCollection.cs [new file with mode: 0644]
OpenTween/Tween.cs

diff --git a/OpenTween.Tests/SocialProtocol/AccountCollectionTest.cs b/OpenTween.Tests/SocialProtocol/AccountCollectionTest.cs
new file mode 100644 (file)
index 0000000..a0b9528
--- /dev/null
@@ -0,0 +1,142 @@
+// 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);
+        }
+    }
+}
index 10dc33d..5736707 100644 (file)
@@ -27,12 +27,11 @@ using System.Reflection;
 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;
@@ -50,8 +49,7 @@ namespace OpenTween
         {
             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));
@@ -61,7 +59,7 @@ namespace OpenTween
                 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);
index 3a32e95..7629161 100644 (file)
@@ -22,9 +22,9 @@
 #nullable enable
 
 using System;
-using OpenTween.Api;
 using OpenTween.Models;
 using OpenTween.Setting;
+using OpenTween.SocialProtocol;
 using OpenTween.Thumbnail;
 using OpenTween.Thumbnail.Services;
 
@@ -41,11 +41,8 @@ namespace OpenTween
         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;
@@ -63,8 +60,7 @@ namespace OpenTween
             => 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;
@@ -77,8 +73,7 @@ namespace OpenTween
             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);
@@ -89,12 +84,9 @@ namespace OpenTween
         private CultureService CreateCultureService()
             => new(this.Settings.Common);
 
-        private TwitterApi CreateTwitterApi()
+        private AccountCollection CreateAccountCollection()
             => new();
 
-        private Twitter CreateTwitter()
-            => new(this.TwitterApi);
-
         private ImageCache CreateImageCache()
             => new();
 
@@ -108,7 +100,7 @@ namespace OpenTween
             => 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()
         {
@@ -118,8 +110,7 @@ namespace OpenTween
             this.IsDisposed = true;
             this.mainFormLazy.Dispose();
             this.imgAzyobuziNetLazy.Dispose();
-            this.twitterLazy.Dispose();
-            this.twitterApiLazy.Dispose();
+            this.accountCollectionLazy.Dispose();
             this.iconAssetsManagerLazy.Dispose();
             this.imageCacheLazy.Dispose();
         }
index 04d5a69..57789b9 100644 (file)
@@ -31,6 +31,7 @@ using System;
 using System.Windows.Forms;
 using OpenTween.Connection;
 using OpenTween.Setting;
+using OpenTween.SocialProtocol;
 
 namespace OpenTween
 {
@@ -94,7 +95,7 @@ namespace OpenTween
                     return 1; // 設定が完了しなかったため終了
             }
 
-            SetupTwitter(container.Twitter, settings);
+            SetupAccounts(container.AccountCollection, settings);
 
             Application.Run(container.MainForm);
 
@@ -139,22 +140,15 @@ namespace OpenTween
             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)
             {
diff --git a/OpenTween/SocialProtocol/AccountCollection.cs b/OpenTween/SocialProtocol/AccountCollection.cs
new file mode 100644 (file)
index 0000000..03b15ad
--- /dev/null
@@ -0,0 +1,91 @@
+// 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();
+        }
+    }
+}
index 90832f7..f5dfca7 100644 (file)
@@ -59,6 +59,7 @@ using OpenTween.MediaUploadServices;
 using OpenTween.Models;
 using OpenTween.OpenTweenCustomControl;
 using OpenTween.Setting;
+using OpenTween.SocialProtocol;
 using OpenTween.Thumbnail;
 
 namespace OpenTween
@@ -108,8 +109,12 @@ 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);
@@ -241,7 +246,7 @@ namespace OpenTween
         public TweenMain(
             SettingManager settingManager,
             TabInformations tabInfo,
-            Twitter twitter,
+            AccountCollection accounts,
             ImageCache imageCache,
             IconAssetsManager iconAssets,
             ThumbnailGenerator thumbGenerator
@@ -249,7 +254,7 @@ namespace OpenTween
         {
             this.settings = settingManager;
             this.statuses = tabInfo;
-            this.tw = twitter;
+            this.accounts = accounts;
             this.iconCache = imageCache;
             this.iconAssets = iconAssets;
             this.thumbGenerator = thumbGenerator;
@@ -2492,18 +2497,7 @@ namespace OpenTween
                 {
                     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