aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.DS_Storebin6148 -> 0 bytes
-rw-r--r--.gitignore2
-rw-r--r--BackEnd/.DS_Storebin6148 -> 0 bytes
-rw-r--r--BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs8
-rw-r--r--BackEnd/Timeline.Tests/packages.lock.json12
-rw-r--r--BackEnd/Timeline/Controllers/BookmarkTimelineController.cs2
-rw-r--r--BackEnd/Timeline/Controllers/HighlightTimelineController.cs2
-rw-r--r--BackEnd/Timeline/Controllers/TimelineController.cs1
-rw-r--r--BackEnd/Timeline/Controllers/TimelinePostController.cs5
-rw-r--r--BackEnd/Timeline/Controllers/TokenController.cs1
-rw-r--r--BackEnd/Timeline/Controllers/UserAvatarController.cs2
-rw-r--r--BackEnd/Timeline/Controllers/UserController.cs5
-rw-r--r--BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs6
-rw-r--r--BackEnd/Timeline/Models/Http/HttpUserLinks.cs7
-rw-r--r--BackEnd/Timeline/Services/Mapper/TimelineMapper.cs15
-rw-r--r--BackEnd/Timeline/Services/Mapper/UserMapper.cs6
-rw-r--r--BackEnd/Timeline/Services/Timeline/MarkdownProcessor.cs24
-rw-r--r--BackEnd/Timeline/SignalRHub/ITimelineClient.cs5
-rw-r--r--BackEnd/Timeline/SignalRHub/TimelineHub.cs38
-rw-r--r--BackEnd/Timeline/Timeline.csproj2
-rw-r--r--BackEnd/Timeline/packages.lock.json6
-rw-r--r--FrontEnd/src/services/timeline.ts20
-rw-r--r--FrontEnd/src/utilities/useReverseScrollPositionRemember.ts77
-rw-r--r--FrontEnd/src/utilities/useScrollToTop.ts2
-rw-r--r--FrontEnd/src/views/timeline/Timeline.tsx7
-rw-r--r--FrontEnd/src/views/timeline/index.tsx3
26 files changed, 132 insertions, 126 deletions
diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index e970fa9e..00000000
--- a/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/.gitignore b/.gitignore
index 41ffa34d..4b60f7ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -229,3 +229,5 @@ _Pvt_Extensions
# FAKE - F# Make
.fake/
+
+.DS_Store
diff --git a/BackEnd/.DS_Store b/BackEnd/.DS_Store
deleted file mode 100644
index fd43f3f2..00000000
--- a/BackEnd/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs
index 72553248..5fab2bdb 100644
--- a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs
+++ b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs
@@ -33,8 +33,8 @@ namespace Timeline.Tests.IntegratedTests
body.Members.Should().NotBeNull().And.BeEmpty();
var links = body._links;
links.Should().NotBeNull();
- links.Self.Should().EndWith("timelines/@user1");
- links.Posts.Should().EndWith("timelines/@user1/posts");
+ links.Self.Should().NotBeNull();
+ links.Posts.Should().NotBeNull();
}
{
@@ -45,8 +45,8 @@ namespace Timeline.Tests.IntegratedTests
body.Members.Should().NotBeNull().And.BeEmpty();
var links = body._links;
links.Should().NotBeNull();
- links.Self.Should().EndWith("timelines/t1");
- links.Posts.Should().EndWith("timelines/t1/posts");
+ links.Self.Should().NotBeNull();
+ links.Posts.Should().NotBeNull();
}
}
diff --git a/BackEnd/Timeline.Tests/packages.lock.json b/BackEnd/Timeline.Tests/packages.lock.json
index f1679705..32cba1af 100644
--- a/BackEnd/Timeline.Tests/packages.lock.json
+++ b/BackEnd/Timeline.Tests/packages.lock.json
@@ -130,8 +130,8 @@
},
"Markdig": {
"type": "Transitive",
- "resolved": "0.28.1",
- "contentHash": "70CneXw2N/1t7v6OfZJqMKLPRB1YWTPddEIcHT/P6IL6X1zsXELIu/DHVt96kr83PIVLznMuXoFK6b9N9KTODg=="
+ "resolved": "0.30.2",
+ "contentHash": "rm5rfHGP0BZtcB31CmQeWLLPdpG6xA6xN92x2KcNrl+9MPJONq9u4dfG7VgnTOxdjDnuEkX6GCxMYS+LpoKGOQ=="
},
"Microsoft.AspNetCore.Connections.Abstractions": {
"type": "Transitive",
@@ -749,8 +749,8 @@
},
"SixLabors.ImageSharp": {
"type": "Transitive",
- "resolved": "2.1.0",
- "contentHash": "H8npUDq3VRagzRsJVxaNoUSMmD+kjAs7sR1Ip85eWbN5c0O1medMw/FiQ32dSNfWz/gVrc+xHs3es9SFlAiNBw==",
+ "resolved": "2.1.1",
+ "contentHash": "oNHMT8+yUR9dyuU2r7fwsXvvS4OnfCs9N5i2y2p9iBxp3nvEZbe/hZWrOGfD7MX2lxOAlWzlhzj0q2uFwP8avg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "5.0.0",
"System.Text.Encoding.CodePages": "5.0.0"
@@ -1755,13 +1755,13 @@
"dependencies": {
"AutoMapper": "11.0.1",
"AutoMapper.Extensions.Microsoft.DependencyInjection": "11.0.0",
- "Markdig": "0.28.1",
+ "Markdig": "0.30.2",
"Microsoft.AspNetCore.SpaServices.Extensions": "6.0.4",
"Microsoft.EntityFrameworkCore": "6.0.4",
"Microsoft.EntityFrameworkCore.Analyzers": "6.0.4",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.4",
"NSwag.AspNetCore": "13.15.10",
- "SixLabors.ImageSharp": "2.1.0",
+ "SixLabors.ImageSharp": "2.1.1",
"System.IdentityModel.Tokens.Jwt": "6.17.0"
}
}
diff --git a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs
index a1fa511c..ba19e67d 100644
--- a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs
+++ b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Timeline.Entities;
@@ -16,6 +17,7 @@ namespace Timeline.Controllers
/// </summary>
[ApiController]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Use v2 api.")]
public class BookmarkTimelineController : MyControllerBase
{
private readonly IBookmarkTimelineService _service;
diff --git a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs
index e30cf720..94fdd01e 100644
--- a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs
+++ b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Timeline.Auth;
@@ -17,6 +18,7 @@ namespace Timeline.Controllers
/// </summary>
[ApiController]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Use v2 bookmark instead.")]
public class HighlightTimelineController : MyControllerBase
{
private readonly IHighlightTimelineService _service;
diff --git a/BackEnd/Timeline/Controllers/TimelineController.cs b/BackEnd/Timeline/Controllers/TimelineController.cs
index 7aeec02f..4a3bdbe1 100644
--- a/BackEnd/Timeline/Controllers/TimelineController.cs
+++ b/BackEnd/Timeline/Controllers/TimelineController.cs
@@ -23,6 +23,7 @@ namespace Timeline.Controllers
[Route("timelines")]
[CatchMultipleTimelineException]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Ues v2 api.")]
public class TimelineController : MyControllerBase
{
private readonly IUserService _userService;
diff --git a/BackEnd/Timeline/Controllers/TimelinePostController.cs b/BackEnd/Timeline/Controllers/TimelinePostController.cs
index fee80adb..ee457a1b 100644
--- a/BackEnd/Timeline/Controllers/TimelinePostController.cs
+++ b/BackEnd/Timeline/Controllers/TimelinePostController.cs
@@ -26,7 +26,8 @@ namespace Timeline.Controllers
[ApiController]
[Route("timelines/{timeline}/posts")]
[CatchMultipleTimelineException]
- [ProducesErrorResponseType(typeof(CommonResponse))]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Use v2 api.")]
public class TimelinePostController : MyControllerBase
{
private readonly ILogger<TimelinePostController> _logger;
@@ -145,7 +146,7 @@ namespace Timeline.Controllers
[ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> DataGet([FromRoute][GeneralTimelineName] string timeline, [FromRoute] long post, [FromRoute(Name = "data_index")][Range(0, 100)] long dataIndex)
{
var timelineId = await _timelineService.GetTimelineIdByNameAsync(timeline);
diff --git a/BackEnd/Timeline/Controllers/TokenController.cs b/BackEnd/Timeline/Controllers/TokenController.cs
index 7fba0bc5..2078c0ec 100644
--- a/BackEnd/Timeline/Controllers/TokenController.cs
+++ b/BackEnd/Timeline/Controllers/TokenController.cs
@@ -17,6 +17,7 @@ namespace Timeline.Controllers
[Route("token")]
[ApiController]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Ues v2 api.")]
public class TokenController : MyControllerBase
{
private readonly IUserService _userService;
diff --git a/BackEnd/Timeline/Controllers/UserAvatarController.cs b/BackEnd/Timeline/Controllers/UserAvatarController.cs
index 072ab621..9e081757 100644
--- a/BackEnd/Timeline/Controllers/UserAvatarController.cs
+++ b/BackEnd/Timeline/Controllers/UserAvatarController.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using System;
using System.Threading.Tasks;
using Timeline.Filters;
using Timeline.Helpers.Cache;
@@ -17,6 +18,7 @@ namespace Timeline.Controllers
/// </summary>
[ApiController]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Ues v2 api.")]
public class UserAvatarController : MyControllerBase
{
private readonly IUserService _userService;
diff --git a/BackEnd/Timeline/Controllers/UserController.cs b/BackEnd/Timeline/Controllers/UserController.cs
index 95a99a03..5dbd7016 100644
--- a/BackEnd/Timeline/Controllers/UserController.cs
+++ b/BackEnd/Timeline/Controllers/UserController.cs
@@ -1,13 +1,13 @@
-using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Timeline.Auth;
using Timeline.Filters;
using Timeline.Models.Http;
using Timeline.Models.Validation;
-using Timeline.Services;
using Timeline.Services.Mapper;
using Timeline.Services.User;
@@ -18,6 +18,7 @@ namespace Timeline.Controllers
/// </summary>
[ApiController]
[ProducesErrorResponseType(typeof(CommonResponse))]
+ [Obsolete("Ues v2 api.")]
public class UserController : MyControllerBase
{
private readonly IUserService _userService;
diff --git a/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs b/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs
index 4d486041..8b7101e4 100644
--- a/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs
+++ b/BackEnd/Timeline/Controllers/V2/TimelinePostV2Controller.cs
@@ -107,7 +107,7 @@ namespace Timeline.Controllers.V2
var data = await _postService.GetPostDataV2Async(timelineId, post, dataIndex);
if (data.ContentType == MimeTypes.TextMarkdown)
{
- return new ByteData(_markdownProcessor.Process(data.Data, Url, timeline, post), data.ContentType);
+ return new ByteData(_markdownProcessor.Process(data.Data, Url, owner, timeline, post), data.ContentType);
}
return data;
}
@@ -157,8 +157,8 @@ namespace Timeline.Controllers.V2
{
var post = await _postService.CreatePostAsync(timelineId, GetAuthUserId(), createRequest);
- var group = TimelineHub.GenerateTimelinePostChangeListeningGroupName(timeline);
- await _timelineHubContext.Clients.Group(group).SendAsync(nameof(ITimelineClient.OnTimelinePostChanged), timeline);
+ var group = TimelineHub.GenerateTimelinePostChangeListeningGroupName(owner, timeline);
+ await _timelineHubContext.Clients.Group(group).SendAsync(nameof(ITimelineClient.OnTimelinePostChangedV2), timeline);
var result = await MapAsync<HttpTimelinePost>(post);
return CreatedAtAction("Get", new { owner = owner, timeline = timeline, post = post.LocalId }, result);
diff --git a/BackEnd/Timeline/Models/Http/HttpUserLinks.cs b/BackEnd/Timeline/Models/Http/HttpUserLinks.cs
index d5f909c2..a1ba6f6f 100644
--- a/BackEnd/Timeline/Models/Http/HttpUserLinks.cs
+++ b/BackEnd/Timeline/Models/Http/HttpUserLinks.cs
@@ -8,11 +8,10 @@
{
public HttpUserLinks() { }
- public HttpUserLinks(string self, string avatar, string timeline)
+ public HttpUserLinks(string self, string avatar)
{
Self = self;
Avatar = avatar;
- Timeline = timeline;
}
/// <summary>
@@ -23,9 +22,5 @@
/// Avatar url.
/// </summary>
public string Avatar { get; set; } = default!;
- /// <summary>
- /// Personal timeline url.
- /// </summary>
- public string Timeline { get; set; } = default!;
}
}
diff --git a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
index a59b906c..be517c9a 100644
--- a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
+++ b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
@@ -73,11 +73,14 @@ namespace Timeline.Services.Mapper
postable = await _timelineService.IsMemberOfAsync(entity.Id, userId.Value);
}
+ var nameV2 = entity.Name is null ? "self" : entity.Name;
+ var ownerUsername = entity.Owner.Username;
+
return new HttpTimeline(
uniqueId: entity.UniqueId,
title: string.IsNullOrEmpty(entity.Title) ? timelineName : entity.Title,
name: timelineName,
- nameV2: entity.Name is null ? "self" : entity.Name,
+ nameV2: nameV2,
nameLastModifed: entity.NameLastModified,
description: entity.Description ?? "",
owner: await _userMapper.MapAsync(entity.Owner, urlHelper, user),
@@ -91,13 +94,17 @@ namespace Timeline.Services.Mapper
manageable: manageable,
postable: postable,
links: new HttpTimelineLinks(
- self: urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { timeline = timelineName })!,
- posts: urlHelper.ActionLink(nameof(TimelinePostController.List), nameof(TimelinePostController)[0..^nameof(Controller).Length], new { timeline = timelineName })!
+ self: urlHelper.ActionLink("Get", "TimelineV2", new { owner = ownerUsername, timeline = nameV2 }) ?? throw Exception("Failed to generate link for timeline self."),
+ posts: urlHelper.ActionLink("List", "TimelinePostV2", new { owner = ownerUsername, timeline = nameV2 }) ?? throw Exception("Failed to generate link for timeline posts.")
)
);
+ }
+
+ private System.Exception Exception(string v)
+ {
+ throw new System.NotImplementedException();
}
-
public async Task<HttpTimelinePost> MapAsync(TimelinePostEntity entity, IUrlHelper urlHelper, ClaimsPrincipal? user)
{
var userId = user.GetOptionalUserId();
diff --git a/BackEnd/Timeline/Services/Mapper/UserMapper.cs b/BackEnd/Timeline/Services/Mapper/UserMapper.cs
index d8c9e294..8a41cd4b 100644
--- a/BackEnd/Timeline/Services/Mapper/UserMapper.cs
+++ b/BackEnd/Timeline/Services/Mapper/UserMapper.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
+using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Timeline.Controllers;
@@ -27,9 +28,8 @@ namespace Timeline.Services.Mapper
nickname: string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname,
permissions: (await _userPermissionService.GetPermissionsOfUserAsync(entity.Id, false)).ToStringList(),
links: new HttpUserLinks(
- self: urlHelper.ActionLink(nameof(UserController.Get), nameof(UserController)[0..^nameof(Controller).Length], new { entity.Username })!,
- avatar: urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController)[0..^nameof(Controller).Length], new { entity.Username })!,
- timeline: urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { timeline = "@" + entity.Username })!
+ self: urlHelper.ActionLink("Get", "UserV2", new { username = entity.Username }) ?? throw new Exception("Failed to generate link for user self."),
+ avatar: urlHelper.ActionLink("Get", "UserAvatarV2", new { username = entity.Username }) ?? throw new Exception("Failed to generate link for user avatar.")
)
);
}
diff --git a/BackEnd/Timeline/Services/Timeline/MarkdownProcessor.cs b/BackEnd/Timeline/Services/Timeline/MarkdownProcessor.cs
index ef8022c0..5c8aabc0 100644
--- a/BackEnd/Timeline/Services/Timeline/MarkdownProcessor.cs
+++ b/BackEnd/Timeline/Services/Timeline/MarkdownProcessor.cs
@@ -1,4 +1,4 @@
-using Markdig;
+using Markdig;
using Markdig.Renderers.Normalize;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
@@ -8,7 +8,7 @@ using System.IO;
using System.Linq;
using System.Text;
using Timeline.Controllers;
-
+
namespace Timeline.Services.Timeline
{
public class MarkdownProcessor
@@ -32,6 +32,7 @@ namespace Timeline.Services.Timeline
}
/// <summary>Convert data url to true url with post id.</summary>
+ [Obsolete("Use overload with 'owner'.")]
public string Process(string text, IUrlHelper url, string timeline, long post)
{
return Process(
@@ -44,9 +45,28 @@ namespace Timeline.Services.Timeline
);
}
+ [Obsolete("Use overload with 'owner'.")]
public byte[] Process(byte[] data, IUrlHelper url, string timeline, long post)
{
return Encoding.UTF8.GetBytes(Process(Encoding.UTF8.GetString(data), url, timeline, post));
+ }
+
+ /// <summary>Convert data url to true url with post id.</summary>
+ public string Process(string text, IUrlHelper url, string owner, string timeline, long post)
+ {
+ return Process(
+ text,
+ dataIndex => url.ActionLink(
+ "DataGet",
+ "TimelinePostV2",
+ new { owner, timeline, post, data_index = dataIndex }
+ )!
+ );
+ }
+
+ public byte[] Process(byte[] data, IUrlHelper url, string owner, string timeline, long post)
+ {
+ return Encoding.UTF8.GetBytes(Process(Encoding.UTF8.GetString(data), url, owner, timeline, post));
}
}
}
diff --git a/BackEnd/Timeline/SignalRHub/ITimelineClient.cs b/BackEnd/Timeline/SignalRHub/ITimelineClient.cs
index 0d1be093..675643aa 100644
--- a/BackEnd/Timeline/SignalRHub/ITimelineClient.cs
+++ b/BackEnd/Timeline/SignalRHub/ITimelineClient.cs
@@ -1,9 +1,12 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
namespace Timeline.SignalRHub
{
public interface ITimelineClient
{
+ [Obsolete("Use v2.")]
Task OnTimelinePostChanged(string timelineName);
+ Task OnTimelinePostChangedV2(string owner, string timeline);
}
}
diff --git a/BackEnd/Timeline/SignalRHub/TimelineHub.cs b/BackEnd/Timeline/SignalRHub/TimelineHub.cs
index 0bfda84c..1925a992 100644
--- a/BackEnd/Timeline/SignalRHub/TimelineHub.cs
+++ b/BackEnd/Timeline/SignalRHub/TimelineHub.cs
@@ -20,11 +20,18 @@ namespace Timeline.SignalRHub
_timelineService = timelineService;
}
+ [Obsolete("Use overload with owner.")]
public static string GenerateTimelinePostChangeListeningGroupName(string timelineName)
{
return $"timeline-post-change-{timelineName}";
+ }
+
+ public static string GenerateTimelinePostChangeListeningGroupName(string owner, string timeline)
+ {
+ return $"v2-timeline-post-change-{owner}/{timeline}";
}
+ [Obsolete("Use v2.")]
public async Task SubscribeTimelinePostChange(string timelineName)
{
try
@@ -48,11 +55,42 @@ namespace Timeline.SignalRHub
}
}
+ public async Task SubscribeTimelinePostChangeV2(string owner, string timeline)
+ {
+ try
+ {
+ var timelineId = await _timelineService.GetTimelineIdAsync(owner, timeline);
+ var user = Context.User;
+ if (!user.HasPermission(UserPermission.AllTimelineManagement) && !await _timelineService.HasReadPermissionAsync(timelineId, user.GetOptionalUserId()))
+ throw new HubException(Resource.MessageForbidden);
+
+ var group = GenerateTimelinePostChangeListeningGroupName(owner, timeline);
+ await Groups.AddToGroupAsync(Context.ConnectionId, group);
+ _logger.LogInformation(Resource.LogSubscribeTimelinePostChange, Context.ConnectionId, group);
+ }
+ catch (ArgumentException)
+ {
+ throw new HubException(Resource.MessageTimelineNameInvalid);
+ }
+ catch (EntityNotExistException)
+ {
+ throw new HubException(Resource.MessageTimelineNotExist);
+ }
+ }
+
+ [Obsolete("Use v2.")]
public async Task UnsubscribeTimelinePostChange(string timelineName)
{
var group = GenerateTimelinePostChangeListeningGroupName(timelineName);
await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
_logger.LogInformation(Resource.LogUnsubscribeTimelinePostChange, Context.ConnectionId, group);
+ }
+
+ public async Task UnsubscribeTimelinePostChangeV2(string owner, string timeline)
+ {
+ var group = GenerateTimelinePostChangeListeningGroupName(owner, timeline);
+ await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
+ _logger.LogInformation(Resource.LogUnsubscribeTimelinePostChange, Context.ConnectionId, group);
}
}
}
diff --git a/BackEnd/Timeline/Timeline.csproj b/BackEnd/Timeline/Timeline.csproj
index 6ab2f9d4..33ad5950 100644
--- a/BackEnd/Timeline/Timeline.csproj
+++ b/BackEnd/Timeline/Timeline.csproj
@@ -36,7 +36,7 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
- <PackageReference Include="Markdig" Version="0.30.0" />
+ <PackageReference Include="Markdig" Version="0.30.2" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="6.0.4" />
diff --git a/BackEnd/Timeline/packages.lock.json b/BackEnd/Timeline/packages.lock.json
index 948b30cc..683332f9 100644
--- a/BackEnd/Timeline/packages.lock.json
+++ b/BackEnd/Timeline/packages.lock.json
@@ -23,9 +23,9 @@
},
"Markdig": {
"type": "Direct",
- "requested": "[0.30.0, )",
- "resolved": "0.30.0",
- "contentHash": "wpuC7a5W/c9bsAzQs8Pxuo2S1KlLDIL3stOHpWW7UwGneYbdh3fByPCAyGmsu5FzPsJsqpbjVSYU5RLKkwk2lA=="
+ "requested": "[0.30.2, )",
+ "resolved": "0.30.2",
+ "contentHash": "rm5rfHGP0BZtcB31CmQeWLLPdpG6xA6xN92x2KcNrl+9MPJONq9u4dfG7VgnTOxdjDnuEkX6GCxMYS+LpoKGOQ=="
},
"Microsoft.AspNetCore.SpaServices.Extensions": {
"type": "Direct",
diff --git a/FrontEnd/src/services/timeline.ts b/FrontEnd/src/services/timeline.ts
index d8c0ae00..cb5b1e32 100644
--- a/FrontEnd/src/services/timeline.ts
+++ b/FrontEnd/src/services/timeline.ts
@@ -21,7 +21,8 @@ export const timelineVisibilityTooltipTranslationMap: Record<
};
export function getTimelinePostUpdate$(
- timelineName: string
+ owner: string,
+ timeline: string
): Observable<{ update: boolean; state: HubConnectionState }> {
return new Observable((subscriber) => {
subscriber.next({
@@ -37,8 +38,11 @@ export function getTimelinePostUpdate$(
.withAutomaticReconnect()
.build();
- const handler = (tn: string): void => {
- if (timelineName === tn) {
+ const o = owner;
+ const t = timeline;
+
+ const handler = (owner: string, timeline: string): void => {
+ if (owner === o && timeline === t) {
subscriber.next({ update: true, state: connection.state });
}
};
@@ -64,12 +68,16 @@ export function getTimelinePostUpdate$(
});
});
- connection.on("OnTimelinePostChanged", handler);
+ connection.on("OnTimelinePostChangedV2", handler);
void connection.start().then(() => {
subscriber.next({ update: false, state: HubConnectionState.Connected });
- return connection.invoke("SubscribeTimelinePostChange", timelineName);
+ return connection.invoke(
+ "SubscribeTimelinePostChangeV2",
+ owner,
+ timeline
+ );
});
return () => {
@@ -77,7 +85,7 @@ export function getTimelinePostUpdate$(
if (connection.state === HubConnectionState.Connected) {
void connection
- .invoke("UnsubscribeTimelinePostChange", timelineName)
+ .invoke("UnsubscribeTimelinePostChangeV2", owner, timeline)
.then(() => connection.stop());
}
};
diff --git a/FrontEnd/src/utilities/useReverseScrollPositionRemember.ts b/FrontEnd/src/utilities/useReverseScrollPositionRemember.ts
deleted file mode 100644
index a5812808..00000000
--- a/FrontEnd/src/utilities/useReverseScrollPositionRemember.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from "react";
-
-let on = false;
-
-let reverseScrollPosition = getReverseScrollPosition();
-let reverseScrollToPosition: number | null = null;
-let lastScrollPosition = window.scrollY;
-
-export function getReverseScrollPosition(): number {
- if (document.documentElement.scrollHeight <= window.innerHeight) {
- return 0;
- } else {
- return (
- document.documentElement.scrollHeight -
- document.documentElement.scrollTop -
- window.innerHeight
- );
- }
-}
-
-export function scrollToReverseScrollPosition(reversePosition: number): void {
- if (document.documentElement.scrollHeight <= window.innerHeight) return;
-
- const old = document.documentElement.style.scrollBehavior;
- document.documentElement.style.scrollBehavior = "auto";
-
- const newPosition =
- document.documentElement.scrollHeight -
- window.innerHeight -
- reversePosition;
-
- reverseScrollToPosition = newPosition;
-
- window.scrollTo(0, newPosition);
-
- document.documentElement.style.scrollBehavior = old;
-}
-
-const scrollListener = (): void => {
- if (
- reverseScrollToPosition != null &&
- Math.abs(window.scrollY - reverseScrollToPosition) > 50
- ) {
- scrollToReverseScrollPosition(reverseScrollPosition);
- return;
- }
- if (
- reverseScrollToPosition == null &&
- Math.abs(window.scrollY - lastScrollPosition) > 1000
- ) {
- scrollToReverseScrollPosition(reverseScrollPosition);
- return;
- }
-
- reverseScrollToPosition = null;
- lastScrollPosition = window.scrollY;
- reverseScrollPosition = getReverseScrollPosition();
-};
-
-const resizeObserver = new ResizeObserver(() => {
- scrollToReverseScrollPosition(reverseScrollPosition);
-});
-
-export default function useReverseScrollPositionRemember(): void {
- React.useEffect(() => {
- if (on) return;
- on = true;
- window.addEventListener("scroll", scrollListener);
- resizeObserver.observe(document.documentElement);
-
- return () => {
- window.removeEventListener("scroll", scrollListener);
- resizeObserver.disconnect();
- on = false;
- };
- }, []);
-}
diff --git a/FrontEnd/src/utilities/useScrollToTop.ts b/FrontEnd/src/utilities/useScrollToTop.ts
index 892e3545..95c8b7b9 100644
--- a/FrontEnd/src/utilities/useScrollToTop.ts
+++ b/FrontEnd/src/utilities/useScrollToTop.ts
@@ -6,7 +6,7 @@ function useScrollToTop(
handler: () => void,
enable = true,
option = {
- maxOffset: 50,
+ maxOffset: 5,
throttle: 1000,
}
): void {
diff --git a/FrontEnd/src/views/timeline/Timeline.tsx b/FrontEnd/src/views/timeline/Timeline.tsx
index 7fb58e0c..e8b1147f 100644
--- a/FrontEnd/src/views/timeline/Timeline.tsx
+++ b/FrontEnd/src/views/timeline/Timeline.tsx
@@ -62,7 +62,10 @@ const Timeline: React.FC<TimelineProps> = (props) => {
React.useEffect(() => {
if (timelineName != null && state === "loaded") {
- const timelinePostUpdate$ = getTimelinePostUpdate$(timelineName);
+ const timelinePostUpdate$ = getTimelinePostUpdate$(
+ timelineOwner,
+ timelineName
+ );
const subscription = timelinePostUpdate$.subscribe(
({ update, state }) => {
if (update) {
@@ -75,7 +78,7 @@ const Timeline: React.FC<TimelineProps> = (props) => {
subscription.unsubscribe();
};
}
- }, [timelineName, state, onReload, onConnectionStateChanged]);
+ }, [timelineOwner, timelineName, state, onReload, onConnectionStateChanged]);
React.useEffect(() => {
if (timelineName != null) {
diff --git a/FrontEnd/src/views/timeline/index.tsx b/FrontEnd/src/views/timeline/index.tsx
index 3bd3ae3c..65bb90f6 100644
--- a/FrontEnd/src/views/timeline/index.tsx
+++ b/FrontEnd/src/views/timeline/index.tsx
@@ -4,7 +4,6 @@ import { useParams } from "react-router-dom";
import { UiLogicError } from "@/common";
import { HttpTimelineInfo } from "@/http/timeline";
-import useReverseScrollPositionRemember from "@/utilities/useReverseScrollPositionRemember";
import { generatePalette, setPalette } from "@/palette";
import Timeline from "./Timeline";
@@ -29,8 +28,6 @@ const TimelinePage: React.FC = () => {
const [connectionStatus, setConnectionStatus] =
React.useState<HubConnectionState>(HubConnectionState.Connecting);
- useReverseScrollPositionRemember();
-
React.useEffect(() => {
if (timeline != null && timeline.color != null) {
return setPalette(generatePalette({ primary: timeline.color }));