var rootElm = this.LoadResponseDocument("TimelineTweet_SimpleTweet.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1613784711020826626", post.StatusId.Id);
Assert.Equal(40480664L, post.UserId);
var rootElm = this.LoadResponseDocument("TimelineTweet_TweetWithMedia.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1614587968567783424", post.StatusId.Id);
Assert.Equal(40480664L, post.UserId);
var rootElm = this.LoadResponseDocument("TimelineTweet_RetweetedTweet.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1617128268548964354", post.StatusId.Id);
Assert.Equal(40480664L, post.RetweetedByUserId);
var rootElm = this.LoadResponseDocument("TimelineTweet_TweetWithVisibility.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1602775353088524288", post.StatusId.Id);
Assert.Equal(357750891L, post.UserId);
var rootElm = this.LoadResponseDocument("TimelineTweet_SelfThread.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1511751702684499968", post.StatusId.Id);
Assert.Equal(40480664L, post.UserId);
var rootElm = this.LoadResponseDocument("TimelineTweet_QuotedTweet.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1588614645866147840", post.StatusId.Id);
var quotedPostId = Assert.Single(post.QuoteStatusIds);
var rootElm = this.LoadResponseDocument("TimelineTweet_QuotedTweet_Tombstone.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1614653321310253057", post.StatusId.Id);
var quotedPostId = Assert.Single(post.QuoteStatusIds);
var rootElm = this.LoadResponseDocument("TimelineTweet_PromotedTweet.json");
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
- var postFactory = new TwitterPostFactory(this.CreateTabInfo());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>());
+ var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
+ var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
Assert.Equal("1674737917363888129", post.StatusId.Id);
Assert.Equal(2941313791L, post.UserId);
[Fact]
public void CreateFromStatus_Test()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new TwitterStatusId(status.IdStr), post.StatusId);
Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
Assert.False(post.IsProtect);
Assert.False(post.IsOwl);
Assert.False(post.IsMe);
+
+ Assert.False(post.IsRead);
}
[Fact]
public void CreateFromStatus_AuthorTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
var selfUserId = status.User.Id;
- var post = factory.CreateFromStatus(status, selfUserId, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId, followerIds: EmptyIdSet, firstLoad: false);
Assert.True(post.IsMe);
}
[Fact]
public void CreateFromStatus_FollowerTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
var followerIds = new HashSet<long> { status.User.Id };
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds);
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds, firstLoad: false);
Assert.False(post.IsOwl);
}
[Fact]
public void CreateFromStatus_NotFollowerTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
var followerIds = new HashSet<long> { 30000L };
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds);
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds, firstLoad: false);
Assert.True(post.IsOwl);
}
[Fact]
public void CreateFromStatus_RetweetTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var originalStatus = this.CreateStatus();
var retweetStatus = this.CreateStatus();
retweetStatus.RetweetedStatus = originalStatus;
retweetStatus.Source = """<a href="https://mobile.twitter.com" rel="nofollow">Twitter Web App</a>""";
- var post = factory.CreateFromStatus(retweetStatus, selfUserId: 20000L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(retweetStatus, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new TwitterStatusId(retweetStatus.IdStr), post.StatusId);
Assert.Equal(retweetStatus.User.Id, post.RetweetedByUserId);
Assert.Equal("https://www.opentween.org/", post.SourceUri?.OriginalString);
}
+ [Fact]
+ public void CreateFromStatus_FirstLoadNotUnreadTest()
+ {
+ var settingCommon = new SettingCommon
+ {
+ Read = true, // 起動時の読み込み分を既読扱いにする
+ };
+
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
+ var status = this.CreateStatus();
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: true);
+
+ Assert.True(post.IsRead); // 既読
+ }
+
+ [Fact]
+ public void CreateFromStatus_FirstLoadUnreadTest()
+ {
+ var settingCommon = new SettingCommon
+ {
+ Read = false, // 起動時の読み込み分を既読扱いにしない
+ };
+
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
+ var status = this.CreateStatus();
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: true);
+
+ Assert.False(post.IsRead); // 未読
+ }
+
+ [Fact]
+ public void CreateFromStatus_SelfPostUnreadTest()
+ {
+ var settingCommon = new SettingCommon
+ {
+ ReadOwnPost = false, // 自分自身の発言を既読扱いにしない
+ };
+
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
+ var status = this.CreateStatus();
+ status.User.Id = 20000L;
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+
+ Assert.False(post.IsRead); // 未読
+ }
+
+ [Fact]
+ public void CreateFromStatus_SelfPostNotUnreadTest()
+ {
+ var settingCommon = new SettingCommon
+ {
+ ReadOwnPost = true, // 自分自身の発言を既読扱いにする
+ };
+
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
+ var status = this.CreateStatus();
+ status.User.Id = 20000L;
+ var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+
+ Assert.True(post.IsRead); // 既読
+ }
+
private TwitterMessageEvent CreateDirectMessage(string senderId, string recipientId)
{
var messageId = this.random.Next(10000);
[Fact]
public void CreateFromDirectMessageEvent_Test()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var selfUser = this.CreateUser();
var otherUser = this.CreateUser();
[otherUser.IdStr] = otherUser,
};
var apps = this.CreateApps();
- var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id);
+ var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id, firstLoad: false);
Assert.Equal(new TwitterDirectMessageId(eventItem.Id), post.StatusId);
Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
Assert.False(post.IsProtect);
Assert.True(post.IsOwl);
Assert.False(post.IsMe);
+
+ Assert.False(post.IsRead);
}
[Fact]
public void CreateFromDirectMessageEvent_SenderTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var selfUser = this.CreateUser();
var otherUser = this.CreateUser();
[otherUser.IdStr] = otherUser,
};
var apps = this.CreateApps();
- var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id);
+ var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id, firstLoad: false);
Assert.Equal(otherUser.Id, post.UserId);
Assert.False(post.IsOwl);
[Fact]
public void GetReceivedHashtags_Test()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "hoge #OpenTween";
status.Entities.Hashtags = new[]
},
};
- _ = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet);
+ _ = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new[] { "#OpenTween" }, factory.GetReceivedHashtags());
Assert.Empty(factory.GetReceivedHashtags());
[Fact]
public void CreateFromStatus_MediaAltTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "https://t.co/hoge";
},
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = string.Format(Properties.Resources.ImageAltText, "代替テキスト");
Assert.Equal(accessibleText, post.AccessibleText);
[Fact]
public void CreateFromStatus_MediaNoAltTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "https://t.co/hoge";
},
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal("pic.twitter.com/hoge", post.AccessibleText);
Assert.Equal("""<a href="https://t.co/hoge" title="https://twitter.com/hoge/status/1234567890/photo/1">pic.twitter.com/hoge</a>""", post.Text);
[Fact]
public void CreateFromStatus_QuotedUrlTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "https://t.co/hoge";
FullText = "test",
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
Assert.Equal(accessibleText, post.AccessibleText);
[Fact]
public void CreateFromStatus_QuotedUrlWithPermelinkTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "hoge";
Expanded = "https://twitter.com/hoge/status/1234567890",
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = "hoge " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
Assert.Equal(accessibleText, post.AccessibleText);
[Fact]
public void CreateFromStatus_QuotedUrlNoReferenceTest()
{
- var factory = new TwitterPostFactory(this.CreateTabinfo());
+ var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
status.FullText = "https://t.co/hoge";
};
status.QuotedStatus = null;
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet);
+ var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = "twitter.com/hoge/status/1…";
Assert.Equal(accessibleText, post.AccessibleText);
using System.Net;
using System.Text.RegularExpressions;
using OpenTween.Api.DataModel;
+using OpenTween.Setting;
namespace OpenTween.Models
{
private static readonly Uri SourceUriBase = new("https://twitter.com/");
private readonly TabInformations tabinfo;
+ private readonly SettingCommon settingCommon;
private readonly HashSet<string> receivedHashTags = new();
- public TwitterPostFactory(TabInformations tabinfo)
- => this.tabinfo = tabinfo;
+ public TwitterPostFactory(TabInformations tabinfo, SettingCommon settingCommon)
+ {
+ this.tabinfo = tabinfo;
+ this.settingCommon = settingCommon;
+ }
public string[] GetReceivedHashtags()
{
TwitterStatus status,
long selfUserId,
ISet<long> followerIds,
+ bool firstLoad,
bool favTweet = false
)
{
// retweeterUser から生成
RetweetedBy = retweeterUser != null ? string.Intern(retweeterUser.ScreenName) : null,
RetweetedByUserId = retweeterUser?.Id,
+
+ IsRead = this.DetermineUnreadState(isMe, firstLoad),
};
}
TwitterMessageEvent eventItem,
IReadOnlyDictionary<string, TwitterUser> users,
IReadOnlyDictionary<string, TwitterMessageEventList.App> apps,
- long selfUserId
+ long selfUserId,
+ bool firstLoad
)
{
var timestamp = long.Parse(eventItem.CreatedTimestamp);
IsProtect = displayUser.Protected,
IsMe = senderIsMe,
IsOwl = !senderIsMe,
+
+ IsRead = this.DetermineUnreadState(senderIsMe, firstLoad),
};
}
+ private bool DetermineUnreadState(bool isMe, bool firstLoad)
+ {
+ if (isMe && this.settingCommon.ReadOwnPost)
+ return true;
+
+ if (firstLoad && this.settingCommon.Read)
+ return true;
+
+ return false;
+ }
+
private string ReplaceTextFromApi(string text, TwitterEntities? entities, TwitterQuotedStatusPermalink? quotedStatusLink)
{
if (entities?.Urls != null)
var credential = accountSettings.GetTwitterCredential();
this.twLegacy.Initialize(credential, accountSettings.Username, accountSettings.UserId);
this.twLegacy.RestrictFavCheck = settingCommon.RestrictFavCheck;
- this.twLegacy.ReadOwnPost = settingCommon.ReadOwnPost;
}
public void Dispose()
public Twitter(TwitterApi api)
{
- this.postFactory = new(TabInformations.GetInstance());
+ this.postFactory = new(TabInformations.GetInstance(), SettingManager.Instance.Common);
this.urlExpander = new(ShortUrl.Instance);
this.Api = api;
this.previousStatusId = status.IdStr;
// 投稿したものを返す
- var post = this.CreatePostsFromStatusData(status);
- this.SetInitialUnreadState(post, firstLoad: false);
+ var post = this.CreatePostsFromStatusData(status, firstLoad: false);
return post;
}
throw new WebApiException("Invalid Json!");
// Retweetしたものを返す
- var retweetPost = this.CreatePostsFromStatusData(status);
- this.SetInitialUnreadState(retweetPost, firstLoad: false);
+ var retweetPost = this.CreatePostsFromStatusData(status, firstLoad: false);
return retweetPost;
}
public bool RestrictFavCheck { get; set; }
- public bool ReadOwnPost { get; set; }
-
public int FollowersCount { get; private set; }
public int FriendsCount { get; private set; }
.ConfigureAwait(false);
}
- var item = this.CreatePostsFromStatusData(status);
- this.SetInitialUnreadState(item, firstLoad);
+ var item = this.CreatePostsFromStatusData(status, firstLoad);
return item;
}
- private PostClass CreatePostsFromStatusData(TwitterStatus status)
- => this.CreatePostsFromStatusData(status, favTweet: false);
+ private PostClass CreatePostsFromStatusData(TwitterStatus status, bool firstLoad)
+ => this.CreatePostsFromStatusData(status, firstLoad, favTweet: false);
- private PostClass CreatePostsFromStatusData(TwitterStatus status, bool favTweet)
+ private PostClass CreatePostsFromStatusData(TwitterStatus status, bool firstLoad, bool favTweet)
{
- var post = this.postFactory.CreateFromStatus(status, this.UserId, this.followerId, favTweet);
+ var post = this.postFactory.CreateFromStatus(status, this.UserId, this.followerId, firstLoad, favTweet);
_ = this.urlExpander.Expand(post);
return post;
private void CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabModel? tab, bool firstLoad)
{
- var posts = items.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
+ var posts = items.Select(x => this.CreatePostsFromStatusData(x, firstLoad)).ToArray();
TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);
if (gType != MyCommon.WORKERTYPE.UserTimeline &&
post.RetweetedByUserId != null && this.noRTId.Contains(post.RetweetedByUserId.Value)) continue;
- this.SetInitialUnreadState(post, firstLoad);
-
if (tab != null && tab is InternalStorageTabModel)
tab.AddPostQueue(post);
else
private void CreatePostsFromSearchJson(TwitterStatus[] statuses, PublicSearchTabModel tab, bool firstLoad)
{
- var posts = statuses.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
+ var posts = statuses.Select(x => this.CreatePostsFromStatusData(x, firstLoad)).ToArray();
TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);
continue;
}
- this.SetInitialUnreadState(post, firstLoad);
-
tab.AddPostQueue(post);
}
}
continue;
}
- var post = this.CreatePostsFromStatusData(status, true);
-
- this.SetInitialUnreadState(post, firstLoad);
+ var post = this.CreatePostsFromStatusData(status, firstLoad, favTweet: true);
TabInformations.GetInstance().AddPost(post);
}
}
- private void SetInitialUnreadState(PostClass post, bool firstLoad)
- {
- bool isRead;
- if (post.IsMe && this.ReadOwnPost)
- isRead = true;
- else if (firstLoad && SettingManager.Instance.Common.Read)
- isRead = true;
- else
- isRead = false;
-
- post.IsRead = isRead;
- }
-
public async Task GetListStatus(ListTimelineTabModel tab, bool more, bool firstLoad)
{
var count = GetApiResultCount(MyCommon.WORKERTYPE.List, more, firstLoad);
statuses = response.Statuses;
}
- return statuses.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
+ return statuses.Select(x => this.CreatePostsFromStatusData(x, firstLoad: false)).ToArray();
}
public async Task GetSearch(PublicSearchTabModel tab, bool more, bool firstLoad)
foreach (var eventItem in events)
{
- var post = this.postFactory.CreateFromDirectMessageEvent(eventItem, users, apps, this.UserId);
- this.SetInitialUnreadState(post, firstLoad);
+ var post = this.postFactory.CreateFromDirectMessageEvent(eventItem, users, apps, this.UserId, firstLoad);
_ = this.urlExpander.Expand(post);