--- /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 System.Threading.Tasks;
+using Moq;
+using OpenTween.Connection;
+using Xunit;
+
+namespace OpenTween.Api.Misskey
+{
+ public class NoteShowRequestTest
+ {
+ [Fact]
+ public async Task Send_Test()
+ {
+ var response = TestUtils.CreateApiResponse(new MisskeyNote());
+
+ var mock = new Mock<IApiConnection>();
+ mock.Setup(x =>
+ x.SendAsync(It.IsAny<IHttpRequest>())
+ )
+ .Callback<IHttpRequest>(x =>
+ {
+ var request = Assert.IsType<PostJsonRequest>(x);
+ Assert.Equal(new("notes/show", UriKind.Relative), request.RequestUri);
+ Assert.Equal(
+ """{"noteId":"abc123"}""",
+ request.JsonString
+ );
+ })
+ .ReturnsAsync(response);
+
+ var request = new NoteShowRequest
+ {
+ NoteId = new("abc123"),
+ };
+ await request.Send(mock.Object);
+
+ mock.VerifyAll();
+ }
+ }
+}
Assert.Equal(expectedParsed, parsed);
Assert.Equal(expectedResult, result);
}
+
+ public static readonly TheoryData<string, DateTimeUtc> ParseISOFixtures = new()
+ {
+ { "2024-01-02T12:34:56.789Z", new(2024, 1, 2, 12, 34, 56, 789) },
+ { "2024-01-02T12:34:56.789+09:00", new(2024, 1, 2, 3, 34, 56, 789) },
+ { "2024-01-02", new(2024, 1, 2, 0, 0, 0, 0) },
+ };
+
+ [Theory]
+ [MemberData(nameof(ParseISOFixtures))]
+ public void ParseISO_Test(string input, DateTimeUtc expected)
+ => Assert.Equal(expected, DateTimeUtc.ParseISO(input));
}
}
--- /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 OpenTween.Api.Misskey;
+using Xunit;
+
+namespace OpenTween.SocialProtocol.Misskey
+{
+ public class MisskeyPostFactoryTest
+ {
+ [Fact]
+ public void CreateFromNote_LocalNoteTest()
+ {
+ var note = new MisskeyNote
+ {
+ Id = "abcdef",
+ CreatedAt = "2024-01-01T01:02:03.456Z",
+ Text = "foo",
+ User = new()
+ {
+ Id = "ghijkl",
+ Username = "bar",
+ },
+ Visibility = "public",
+ };
+ var factory = new MisskeyPostFactory(new());
+ var accountState = new MisskeyAccountState(new("https://example.com/"), new("aaaa"), "hoge");
+
+ var post = factory.CreateFromNote(note, accountState, firstLoad: false);
+ Assert.Equal(new MisskeyNoteId("abcdef"), post.StatusId);
+ Assert.Equal(new(2024, 1, 1, 1, 2, 3, 456), post.CreatedAtForSorting);
+ Assert.Equal(new(2024, 1, 1, 1, 2, 3, 456), post.CreatedAt);
+ Assert.Equal(new("https://example.com/notes/abcdef"), post.PostUri);
+ Assert.Equal("foo", post.Text);
+ Assert.Equal("foo", post.TextFromApi);
+ Assert.Equal("foo", post.AccessibleText);
+ Assert.Empty(post.QuoteStatusIds);
+ Assert.False(post.IsFav);
+ Assert.False(post.IsReply);
+ Assert.Null(post.InReplyToStatusId);
+ Assert.Null(post.InReplyToUser);
+ Assert.Null(post.InReplyToUserId);
+ Assert.False(post.IsProtect);
+ Assert.Equal(new MisskeyUserId("ghijkl"), post.UserId);
+ Assert.Equal("bar", post.ScreenName);
+ Assert.Equal("bar", post.Nickname);
+ Assert.Equal("", post.ImageUrl);
+ Assert.Null(post.RetweetedId);
+ Assert.Null(post.RetweetedBy);
+ Assert.Null(post.RetweetedByUserId);
+ Assert.False(post.IsRead);
+ }
+
+ [Fact]
+ public void CreateFromNote_RemoteNoteTest()
+ {
+ var note = new MisskeyNote
+ {
+ Id = "abcdef",
+ CreatedAt = "2024-01-01T01:02:03.456Z",
+ Text = "foo",
+ User = new()
+ {
+ Id = "ghijkl",
+ Username = "bar",
+ Host = "bbb.example.com",
+ },
+ Visibility = "public",
+ };
+ var factory = new MisskeyPostFactory(new());
+ var accountState = new MisskeyAccountState(new("https://aaa.example.com/"), new("aaaa"), "hoge");
+
+ var post = factory.CreateFromNote(note, accountState, firstLoad: false);
+ Assert.Equal(new("https://aaa.example.com/notes/abcdef"), post.PostUri);
+ Assert.Equal("bar@bbb.example.com", post.ScreenName);
+ }
+
+ [Fact]
+ public void CreateFromNote_ReplyTest()
+ {
+ var repliedNote = new MisskeyNote
+ {
+ Id = "aaaaa",
+ CreatedAt = "2023-12-31T00:00:00.000Z",
+ Text = "hoge",
+ UserId = "abcdef",
+ User = new()
+ {
+ Id = "abcdef",
+ Username = "foo",
+ },
+ Visibility = "public",
+ };
+ var note = new MisskeyNote
+ {
+ Id = "bbbbb",
+ CreatedAt = "2024-01-01T01:02:03.456Z",
+ Text = "@foo tetete",
+ User = new()
+ {
+ Id = "ghijkl",
+ Username = "bar",
+ },
+ Reply = repliedNote,
+ Visibility = "public",
+ };
+ var factory = new MisskeyPostFactory(new());
+ var accountState = new MisskeyAccountState(new("https://example.com/"), new("aaaa"), "hoge");
+
+ var post = factory.CreateFromNote(note, accountState, firstLoad: false);
+ Assert.Equal(new MisskeyNoteId("bbbbb"), post.StatusId);
+ Assert.Equal("@foo tetete", post.Text);
+ Assert.Equal(new MisskeyUserId("ghijkl"), post.UserId);
+ Assert.Equal("bar", post.ScreenName);
+ Assert.Equal(new MisskeyNoteId("aaaaa"), post.InReplyToStatusId);
+ Assert.Equal("foo", post.InReplyToUser);
+ Assert.Equal(new MisskeyUserId("abcdef"), post.InReplyToUserId);
+ }
+
+ [Fact]
+ public void CreateFromNote_RenoteTest()
+ {
+ var renotedNote = new MisskeyNote
+ {
+ Id = "aaaaa",
+ CreatedAt = "2023-12-31T00:00:00.000Z",
+ Text = "hoge",
+ User = new()
+ {
+ Id = "abcdef",
+ Username = "foo",
+ },
+ Visibility = "public",
+ };
+ var note = new MisskeyNote
+ {
+ Id = "bbbbb",
+ CreatedAt = "2024-01-01T01:02:03.456Z",
+ Text = null,
+ User = new()
+ {
+ Id = "ghijkl",
+ Username = "bar",
+ },
+ Renote = renotedNote,
+ Visibility = "public",
+ };
+ var factory = new MisskeyPostFactory(new());
+ var accountState = new MisskeyAccountState(new("https://example.com/"), new("aaaa"), "hoge");
+
+ var post = factory.CreateFromNote(note, accountState, firstLoad: false);
+ Assert.Equal(new MisskeyNoteId("bbbbb"), post.StatusId);
+ Assert.Equal(new(2024, 1, 1, 1, 2, 3, 456), post.CreatedAtForSorting);
+ Assert.Equal(new(2023, 12, 31, 0, 0, 0, 0), post.CreatedAt);
+ Assert.Equal(new("https://example.com/notes/aaaaa"), post.PostUri);
+ Assert.Equal("hoge", post.Text);
+ Assert.Equal(new MisskeyUserId("abcdef"), post.UserId);
+ Assert.Equal("foo", post.ScreenName);
+ Assert.Equal(new MisskeyNoteId("aaaaa"), post.RetweetedId);
+ Assert.Equal("bar", post.RetweetedBy);
+ Assert.Equal(new MisskeyUserId("ghijkl"), post.RetweetedByUserId);
+ }
+
+ [Fact]
+ public void CreateFromNote_QuotedNoteTest()
+ {
+ var quotedNote = new MisskeyNote
+ {
+ Id = "aaaaa",
+ CreatedAt = "2023-12-31T00:00:00.000Z",
+ Text = "hoge",
+ User = new()
+ {
+ Id = "abcdef",
+ Username = "foo",
+ },
+ Visibility = "public",
+ };
+ var note = new MisskeyNote
+ {
+ Id = "bbbbb",
+ CreatedAt = "2024-01-01T01:02:03.456Z",
+ Text = "tetete", // Text が空でなく Renote がある場合は引用とみなす
+ User = new()
+ {
+ Id = "ghijkl",
+ Username = "bar",
+ },
+ Renote = quotedNote,
+ Visibility = "public",
+ };
+ var factory = new MisskeyPostFactory(new());
+ var accountState = new MisskeyAccountState(new("https://example.com/"), new("aaaa"), "hoge");
+
+ var post = factory.CreateFromNote(note, accountState, firstLoad: false);
+ Assert.Equal(new MisskeyNoteId("bbbbb"), post.StatusId);
+ Assert.Equal(new(2024, 1, 1, 1, 2, 3, 456), post.CreatedAtForSorting);
+ Assert.Equal(new(2024, 1, 1, 1, 2, 3, 456), post.CreatedAt);
+ Assert.Equal(new("https://example.com/notes/bbbbb"), post.PostUri);
+ Assert.Equal("tetete", post.Text);
+ Assert.Equal(new MisskeyUserId("ghijkl"), post.UserId);
+ Assert.Equal("bar", post.ScreenName);
+ Assert.Null(post.RetweetedId);
+ Assert.Null(post.RetweetedBy);
+ Assert.Null(post.RetweetedByUserId);
+ Assert.Equal(new[] { new MisskeyNoteId("aaaaa") }, post.QuoteStatusIds);
+ }
+ }
+}
--- /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.Runtime.Serialization;
+
+namespace OpenTween.Api.Misskey
+{
+ [DataContract]
+ public class MisskeyNote
+ {
+ [DataMember(Name = "id")]
+ public string Id { get; set; } = "";
+
+ [DataMember(Name = "createdAt")]
+ public string CreatedAt { get; set; } = "";
+
+ [DataMember(Name = "text")]
+ public string? Text { get; set; }
+
+ [DataMember(Name = "userId")]
+ public string UserId { get; set; } = "";
+
+ [DataMember(Name = "user")]
+ public MisskeyUserLite User { get; set; } = new();
+
+ [DataMember(Name = "replyId", IsRequired = false)]
+ public string? ReplyId { get; set; }
+
+ [DataMember(Name = "reply", IsRequired = false)]
+ public MisskeyNote? Reply { get; set; }
+
+ [DataMember(Name = "renoteId", IsRequired = false)]
+ public string? RenoteId { get; set; }
+
+ [DataMember(Name = "renote", IsRequired = false)]
+ public MisskeyNote? Renote { get; set; }
+
+ [DataMember(Name = "visibility")]
+ public string Visibility { get; set; } = "";
+
+ [DataMember(Name = "mentions", IsRequired = false)]
+ public string[] Mentions { get; set; } = Array.Empty<string>();
+
+ [DataMember(Name = "myReaction", IsRequired = false)]
+ public string? MyReaction { get; set; }
+ }
+}
--- /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.Runtime.Serialization;
+
+namespace OpenTween.Api.Misskey
+{
+ [DataContract]
+ public class MisskeyUserLite
+ {
+ [DataMember(Name = "id")]
+ public string Id { get; set; } = "";
+
+ [DataMember(Name = "name")]
+ public string? Name { get; set; }
+
+ [DataMember(Name = "username")]
+ public string Username { get; set; } = "";
+
+ [DataMember(Name = "host")]
+ public string? Host { get; set; }
+
+ [DataMember(Name = "avatarUrl")]
+ public string? AvatarUrl { get; set; }
+ }
+}
--- /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.Runtime.Serialization;
+using System.Threading.Tasks;
+using OpenTween.Connection;
+using OpenTween.SocialProtocol.Misskey;
+
+namespace OpenTween.Api.Misskey
+{
+ public class NoteShowRequest
+ {
+ public required MisskeyNoteId NoteId { get; set; }
+
+ public async Task<MisskeyNote> Send(IApiConnection apiConnection)
+ {
+ var request = new PostJsonRequest
+ {
+ RequestUri = new("notes/show", UriKind.Relative),
+ JsonString = this.CreateRequestJson(),
+ };
+
+ using var response = await apiConnection.SendAsync(request)
+ .ConfigureAwait(false);
+
+ var note = await response.ReadAsJson<MisskeyNote>()
+ .ConfigureAwait(false);
+
+ return note;
+ }
+
+ [DataContract]
+ private record RequestBody(
+ [property: DataMember(Name = "noteId")]
+ string NoteId
+ );
+
+ private string CreateRequestJson()
+ {
+ var body = new RequestBody(
+ NoteId: this.NoteId.Id
+ );
+
+ return JsonUtils.SerializeJsonByDataContract(body);
+ }
+ }
+}
result = MinValue;
return false;
}
+
+ public static DateTimeUtc ParseISO(string input)
+ => new(DateTimeOffset.Parse(input, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
}
}
}
public bool CanUsePostId(PostId postId)
- => throw new NotImplementedException();
+ => postId is MisskeyNoteId;
public void Dispose()
{
public class MisskeyClient : ISocialProtocolClient
{
private readonly MisskeyAccount account;
+ private readonly MisskeyPostFactory postFactory = new();
public MisskeyClient(MisskeyAccount account)
=> this.account = account;
return new();
}
- public Task<PostClass> GetPostById(PostId postId, bool firstLoad)
- => throw this.CreateException();
+ public async Task<PostClass> GetPostById(PostId postId, bool firstLoad)
+ {
+ var request = new NoteShowRequest
+ {
+ NoteId = this.AssertMisskeyNoteId(postId),
+ };
+
+ var note = await request.Send(this.account.Connection)
+ .ConfigureAwait(false);
+
+ var post = this.CreatePostFromNote(note, firstLoad);
+
+ return post;
+ }
public Task<TimelineResponse> GetHomeTimeline(int count, IQueryCursor? cursor, bool firstLoad)
=> throw this.CreateException();
private WebApiException CreateException()
=> new("Not implemented");
+
+ private MisskeyNoteId AssertMisskeyNoteId(PostId postId)
+ {
+ return postId is MisskeyNoteId noteId
+ ? noteId
+ : throw new WebApiException($"Not supported type: {postId.GetType()}");
+ }
+
+ private PostClass CreatePostFromNote(MisskeyNote note, bool firstLoad)
+ => this.postFactory.CreateFromNote(note, this.account.AccountState, firstLoad);
}
}
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2023 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 OpenTween.Models;
+
+namespace OpenTween.SocialProtocol.Misskey
+{
+ public class MisskeyNoteId : PostId
+ {
+ public override string IdType => "misskey_note";
+
+ public override string Id { get; }
+
+ public MisskeyNoteId(string id)
+ => this.Id = id ?? throw new ArgumentNullException(nameof(id));
+ }
+}
--- /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.Linq;
+using OpenTween.Api.Misskey;
+using OpenTween.Models;
+using OpenTween.Setting;
+
+namespace OpenTween.SocialProtocol.Misskey
+{
+ public class MisskeyPostFactory
+ {
+ private readonly SettingCommon settingCommon;
+
+ public MisskeyPostFactory()
+ : this(SettingManager.Instance.Common)
+ {
+ }
+
+ public MisskeyPostFactory(SettingCommon settingCommon)
+ => this.settingCommon = settingCommon;
+
+ public PostClass CreateFromNote(MisskeyNote note, MisskeyAccountState accountState, bool firstLoad)
+ {
+ var noteUser = note.User;
+ var noteUserId = new MisskeyUserId(noteUser.Id);
+
+ var renotedNote = note.Renote;
+ var renoterUser = renotedNote != null ? noteUser : null;
+
+ // リツイートであるか否かに関わらず常にオリジナルのツイート及びユーザーを指す
+ var originalNote = renotedNote ?? note;
+ var originalNoteId = new MisskeyNoteId(originalNote.Id);
+ var originalNoteUser = originalNote.User;
+ var originalNoteUserId = new MisskeyUserId(originalNoteUser.Id);
+ var originalNoteUserAcct = originalNoteUser.Host is { } host ? $"{originalNoteUser.Username}@{host}" : originalNoteUser.Username;
+
+ var replyToNote = originalNote.Reply;
+
+ var isMe = noteUserId == accountState.UserId;
+ var reactionSent = note.MyReaction != null;
+
+ var originalText = originalNote.Text ?? "";
+ var urlEntities = TweetExtractor.ExtractUrlEntities(originalText);
+ var textHtml = TweetFormatter.AutoLinkHtml(originalText, urlEntities);
+
+ return new()
+ {
+ // note から生成
+ StatusId = new MisskeyNoteId(note.Id),
+ CreatedAtForSorting = DateTimeUtc.ParseISO(note.CreatedAt),
+ IsMe = isMe,
+
+ // originalNote から生成
+ PostUri = CreateLocalPostUri(accountState.ServerUri, originalNoteId),
+ CreatedAt = DateTimeUtc.ParseISO(originalNote.CreatedAt),
+ Text = textHtml,
+ TextFromApi = originalText,
+ AccessibleText = originalText,
+ IsFav = reactionSent,
+ IsReply = renotedNote == null && originalNote.Mentions?.Any(x => x == accountState.UserId.Id) == true,
+ InReplyToStatusId = replyToNote?.Id is { } replyToIdStr ? new MisskeyNoteId(replyToIdStr) : null,
+ InReplyToUser = replyToNote?.User.Username,
+ InReplyToUserId = replyToNote?.UserId is { } replyToUserIdStr ? new MisskeyUserId(replyToUserIdStr) : null,
+ IsProtect = originalNote.Visibility is not "public" or "home",
+
+ // originalNoteUser から生成
+ UserId = originalNoteUserId,
+ ScreenName = originalNoteUserAcct,
+ Nickname = originalNoteUser.Name ?? originalNoteUser.Username,
+ ImageUrl = originalNoteUser.AvatarUrl ?? "",
+
+ // renotedNote から生成
+ RetweetedId = renotedNote?.Id is { } renotedId ? new MisskeyNoteId(renotedId) : null,
+
+ // renoterUser から生成
+ RetweetedBy = renoterUser?.Username,
+ RetweetedByUserId = renoterUser?.Id is { } renoterId ? new MisskeyUserId(renoterId) : null,
+
+ IsRead = this.DetermineUnreadState(isMe, firstLoad),
+ };
+ }
+
+ public static Uri CreateLocalPostUri(Uri serverUri, MisskeyNoteId noteId)
+ => new(serverUri, $"/notes/{noteId.Id}");
+
+ private bool DetermineUnreadState(bool isMe, bool firstLoad)
+ {
+ if (isMe && this.settingCommon.ReadOwnPost)
+ return true;
+
+ if (firstLoad && this.settingCommon.Read)
+ return true;
+
+ return false;
+ }
+ }
+}