From db31b5d6c151d31205753def45768fe646538c03 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:55:17 +0800 Subject: [PATCH 01/33] Deduplicate post view notifications --- .../repository/NotificationRepository.java | 2 ++ .../openisle/service/NotificationService.java | 3 +++ .../service/NotificationServiceTest.java | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/main/java/com/openisle/repository/NotificationRepository.java b/src/main/java/com/openisle/repository/NotificationRepository.java index f0d538c49..0cbec0d60 100644 --- a/src/main/java/com/openisle/repository/NotificationRepository.java +++ b/src/main/java/com/openisle/repository/NotificationRepository.java @@ -18,4 +18,6 @@ public interface NotificationRepository extends JpaRepository findByComment(Comment comment); void deleteByTypeAndFromUser(NotificationType type, User fromUser); + + void deleteByTypeAndFromUserAndPost(NotificationType type, User fromUser, Post post); } diff --git a/src/main/java/com/openisle/service/NotificationService.java b/src/main/java/com/openisle/service/NotificationService.java index 561b59b28..b2315269d 100644 --- a/src/main/java/com/openisle/service/NotificationService.java +++ b/src/main/java/com/openisle/service/NotificationService.java @@ -58,6 +58,9 @@ public class NotificationService { public Notification createNotification(User user, NotificationType type, Post post, Comment comment, Boolean approved, User fromUser, ReactionType reactionType, String content) { + if (type == NotificationType.POST_VIEWED && post != null && fromUser != null) { + notificationRepository.deleteByTypeAndFromUserAndPost(type, fromUser, post); + } Notification n = new Notification(); n.setUser(user); n.setType(type); diff --git a/src/test/java/com/openisle/service/NotificationServiceTest.java b/src/test/java/com/openisle/service/NotificationServiceTest.java index 59d43d5e4..c4ca007a1 100644 --- a/src/test/java/com/openisle/service/NotificationServiceTest.java +++ b/src/test/java/com/openisle/service/NotificationServiceTest.java @@ -168,4 +168,27 @@ class NotificationServiceTest { verify(email).sendEmail("a@a.com", "有人回复了你", "https://ex.com/posts/1#comment-2"); verify(push).sendNotification(eq(user), contains("/posts/1#comment-2")); } + + @Test + void postViewedNotificationDeletesOldOnes() { + NotificationRepository nRepo = mock(NotificationRepository.class); + UserRepository uRepo = mock(UserRepository.class); + ReactionRepository rRepo = mock(ReactionRepository.class); + EmailSender email = mock(EmailSender.class); + PushNotificationService push = mock(PushNotificationService.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); + org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); + + User owner = new User(); + User viewer = new User(); + Post post = new Post(); + + when(nRepo.save(any(Notification.class))).thenAnswer(i -> i.getArgument(0)); + + service.createNotification(owner, NotificationType.POST_VIEWED, post, null, null, viewer, null, null); + + verify(nRepo).deleteByTypeAndFromUserAndPost(NotificationType.POST_VIEWED, viewer, post); + verify(nRepo).save(any(Notification.class)); + } } From 14bd9d86c0dbd2797ffc814a8318470025aa4767 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 1 Aug 2025 12:01:44 +0800 Subject: [PATCH 02/33] =?UTF-8?q?feat:=20=E5=A4=84=E7=90=86=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E9=98=85=E8=AF=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/openisle/service/NotificationService.java | 5 ++--- src/main/java/com/openisle/service/PostService.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/openisle/service/NotificationService.java b/src/main/java/com/openisle/service/NotificationService.java index b2315269d..93948d5c7 100644 --- a/src/main/java/com/openisle/service/NotificationService.java +++ b/src/main/java/com/openisle/service/NotificationService.java @@ -9,6 +9,8 @@ import com.openisle.service.EmailSender; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.transaction.annotation.Transactional; + import java.util.Map; import java.util.regex.Pattern; @@ -58,9 +60,6 @@ public class NotificationService { public Notification createNotification(User user, NotificationType type, Post post, Comment comment, Boolean approved, User fromUser, ReactionType reactionType, String content) { - if (type == NotificationType.POST_VIEWED && post != null && fromUser != null) { - notificationRepository.deleteByTypeAndFromUserAndPost(type, fromUser, post); - } Notification n = new Notification(); n.setUser(user); n.setType(type); diff --git a/src/main/java/com/openisle/service/PostService.java b/src/main/java/com/openisle/service/PostService.java index 2e124f3f4..c3293c4c9 100644 --- a/src/main/java/com/openisle/service/PostService.java +++ b/src/main/java/com/openisle/service/PostService.java @@ -26,6 +26,7 @@ import java.util.List; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.transaction.annotation.Transactional; @Service public class PostService { @@ -143,6 +144,7 @@ public class PostService { return post; } + @Transactional public Post viewPost(Long id, String viewer) { Post post = postRepository.findById(id) .orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found")); @@ -164,9 +166,8 @@ public class PostService { if (viewer != null && !viewer.equals(post.getAuthor().getUsername())) { User viewerUser = userRepository.findByUsername(viewer).orElse(null); if (viewerUser != null) { + notificationRepository.deleteByTypeAndFromUserAndPost(NotificationType.POST_VIEWED, viewerUser, post); notificationService.createNotification(post.getAuthor(), NotificationType.POST_VIEWED, post, null, null, viewerUser, null, null); - } else { - notificationService.createNotification(post.getAuthor(), NotificationType.POST_VIEWED, post, null, null, null, null, null); } } return post; From 847426a507c875f3f660c2ffaa9feb3a510854a1 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:09:10 +0800 Subject: [PATCH 03/33] feat(frontend): expand first-level comments by default --- open-isle-cli/src/views/PostPageView.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue index 470d2f698..311383bcf 100644 --- a/open-isle-cli/src/views/PostPageView.vue +++ b/open-isle-cli/src/views/PostPageView.vue @@ -216,15 +216,15 @@ export default { } } - const mapComment = c => ({ + const mapComment = (c, level = 0) => ({ id: c.id, userName: c.author.username, time: TimeManager.format(c.createdAt), avatar: c.author.avatar, text: c.content, reactions: c.reactions || [], - reply: (c.replies || []).map(mapComment), - openReplies: false, + reply: (c.replies || []).map(r => mapComment(r, level + 1)), + openReplies: level === 0, src: c.author.avatar, iconClick: () => router.push(`/users/${c.author.id}`) }) From d9d4597e13b1bb044d3883df1a88098afe1c4c63 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:16:42 +0800 Subject: [PATCH 04/33] fix like counts --- .../openisle/repository/ReactionRepository.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/openisle/repository/ReactionRepository.java b/src/main/java/com/openisle/repository/ReactionRepository.java index 047742120..dfd76871e 100644 --- a/src/main/java/com/openisle/repository/ReactionRepository.java +++ b/src/main/java/com/openisle/repository/ReactionRepository.java @@ -30,9 +30,22 @@ public interface ReactionRepository extends JpaRepository { @Query("SELECT COUNT(r) FROM Reaction r WHERE r.user.username = :username AND r.createdAt >= :start") long countByUserAfter(@Param("username") String username, @Param("start") java.time.LocalDateTime start); - @Query("SELECT COUNT(r) FROM Reaction r WHERE r.type = com.openisle.model.ReactionType.LIKE AND ((r.post IS NOT NULL AND r.post.author.username = :username) OR (r.comment IS NOT NULL AND r.comment.author.username = :username))") + @Query(""" + SELECT COUNT(r) FROM Reaction r + LEFT JOIN r.post p + LEFT JOIN r.comment c + WHERE r.type = com.openisle.model.ReactionType.LIKE AND + ((p IS NOT NULL AND p.author.username = :username) OR + (c IS NOT NULL AND c.author.username = :username)) + """) long countLikesReceived(@Param("username") String username); - @Query("SELECT COUNT(r) FROM Reaction r WHERE (r.post IS NOT NULL AND r.post.author.username = :username) OR (r.comment IS NOT NULL AND r.comment.author.username = :username)") + @Query(""" + SELECT COUNT(r) FROM Reaction r + LEFT JOIN r.post p + LEFT JOIN r.comment c + WHERE (p IS NOT NULL AND p.author.username = :username) OR + (c IS NOT NULL AND c.author.username = :username) + """) long countReceived(@Param("username") String username); } From d056bc9120a3537a414e2cfb87468a6526b7e945 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:30:15 +0800 Subject: [PATCH 05/33] Include reactions in comment API --- .../controller/CommentController.java | 32 +++++++++++++++++++ .../controller/CommentControllerTest.java | 4 +++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/com/openisle/controller/CommentController.java b/src/main/java/com/openisle/controller/CommentController.java index b6dccfdfd..2de863617 100644 --- a/src/main/java/com/openisle/controller/CommentController.java +++ b/src/main/java/com/openisle/controller/CommentController.java @@ -4,6 +4,7 @@ import com.openisle.model.Comment; import com.openisle.service.CommentService; import com.openisle.service.CaptchaService; import com.openisle.service.LevelService; +import com.openisle.service.ReactionService; import com.openisle.model.CommentSort; import lombok.Data; import lombok.RequiredArgsConstructor; @@ -23,6 +24,7 @@ public class CommentController { private final CommentService commentService; private final LevelService levelService; private final CaptchaService captchaService; + private final ReactionService reactionService; @Value("${app.captcha.enabled:false}") private boolean captchaEnabled; @@ -70,6 +72,10 @@ public class CommentController { .map(this::toDtoWithReplies) .collect(Collectors.toList()); dto.setReplies(replies); + List reactions = reactionService.getReactionsForComment(comment.getId()).stream() + .map(this::toReactionDto) + .collect(Collectors.toList()); + dto.setReactions(reactions); return dto; } @@ -109,6 +115,7 @@ public class CommentController { private LocalDateTime createdAt; private AuthorDto author; private List replies; + private List reactions; private int reward; } @@ -118,4 +125,29 @@ public class CommentController { private String username; private String avatar; } + + private ReactionDto toReactionDto(com.openisle.model.Reaction reaction) { + ReactionDto dto = new ReactionDto(); + dto.setId(reaction.getId()); + dto.setType(reaction.getType()); + dto.setUser(reaction.getUser().getUsername()); + if (reaction.getPost() != null) { + dto.setPostId(reaction.getPost().getId()); + } + if (reaction.getComment() != null) { + dto.setCommentId(reaction.getComment().getId()); + } + dto.setReward(0); + return dto; + } + + @Data + private static class ReactionDto { + private Long id; + private com.openisle.model.ReactionType type; + private String user; + private Long postId; + private Long commentId; + private int reward; + } } diff --git a/src/test/java/com/openisle/controller/CommentControllerTest.java b/src/test/java/com/openisle/controller/CommentControllerTest.java index 8eeabbef9..eb9cfde2a 100644 --- a/src/test/java/com/openisle/controller/CommentControllerTest.java +++ b/src/test/java/com/openisle/controller/CommentControllerTest.java @@ -6,6 +6,7 @@ import com.openisle.model.User; import com.openisle.service.CommentService; import com.openisle.service.CaptchaService; import com.openisle.service.LevelService; +import com.openisle.service.ReactionService; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +38,8 @@ class CommentControllerTest { private CaptchaService captchaService; @MockBean private LevelService levelService; + @MockBean + private ReactionService reactionService; private Comment createComment(Long id, String content, String authorName) { User user = new User(); @@ -56,6 +59,7 @@ class CommentControllerTest { Mockito.when(commentService.addComment(eq("bob"), eq(1L), eq("hi"))).thenReturn(comment); Mockito.when(commentService.getCommentsForPost(eq(1L), any())).thenReturn(List.of(comment)); Mockito.when(commentService.getReplies(1L)).thenReturn(List.of()); + Mockito.when(reactionService.getReactionsForComment(1L)).thenReturn(List.of()); mockMvc.perform(post("/api/posts/1/comments") .contentType("application/json") From 624729ae9e4140541438783269540e67aca1e61d Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:57:35 +0800 Subject: [PATCH 06/33] fix: expand top-level comment replies --- open-isle-cli/src/components/CommentItem.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/open-isle-cli/src/components/CommentItem.vue b/open-isle-cli/src/components/CommentItem.vue index 724fb5038..61ab54538 100644 --- a/open-isle-cli/src/components/CommentItem.vue +++ b/open-isle-cli/src/components/CommentItem.vue @@ -97,11 +97,11 @@ const CommentItem = { }, setup(props, { emit }) { const router = useRouter() - const showReplies = ref(props.defaultShowReplies) + const showReplies = ref(props.level === 0 ? true : props.defaultShowReplies) watch( () => props.defaultShowReplies, (val) => { - showReplies.value = val + showReplies.value = props.level === 0 ? true : val } ) const showEditor = ref(false) From 486787084a36ed1aa0e55210fa09c514e5d3789e Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:04:34 +0800 Subject: [PATCH 07/33] Show detailed mark-all-read message only for admins --- open-isle-cli/src/views/MessagePageView.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/open-isle-cli/src/views/MessagePageView.vue b/open-isle-cli/src/views/MessagePageView.vue index ae0d64d1f..438177392 100644 --- a/open-isle-cli/src/views/MessagePageView.vue +++ b/open-isle-cli/src/views/MessagePageView.vue @@ -333,7 +333,11 @@ export default { if (n.type !== 'REGISTER_REQUEST') n.read = true }) await fetchUnreadCount() - toast.success('已读所有消息(注册请求除外)') + if (authState.role === 'ADMIN') { + toast.success('已读所有消息(注册请求除外)') + } else { + toast.success('已读所有消息') + } } } From 6642a248fd9dc37b796f3123fa2173e22ef90222 Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 3 Aug 2025 00:10:03 +0800 Subject: [PATCH 08/33] =?UTF-8?q?feat:=201.=E6=B7=BB=E5=8A=A0=E5=86=85?= =?UTF-8?q?=E7=BD=91=E8=B0=83=E8=AF=95=E5=9C=B0=E5=9D=80,=202.=20Spaceship?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E5=9F=9F=E5=90=8D=E4=BC=98=E6=83=A0=E7=A0=81?= =?UTF-8?q?=20=E8=BF=99=E6=9D=A1=E6=95=B0=E6=8D=AE=E6=A8=AA=E5=90=91?= =?UTF-8?q?=E8=B6=85=E5=87=BA=E4=BA=86=20=E6=8C=A1=E4=BD=8F=E5=8F=91?= =?UTF-8?q?=E5=B8=96=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- open-isle-cli/src/views/HomePageView.vue | 1 + src/main/java/com/openisle/config/SecurityConfig.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/open-isle-cli/src/views/HomePageView.vue b/open-isle-cli/src/views/HomePageView.vue index dbd6fd7f5..04e1492bf 100644 --- a/open-isle-cli/src/views/HomePageView.vue +++ b/open-isle-cli/src/views/HomePageView.vue @@ -715,6 +715,7 @@ export default { .article-item-description { margin-top: 2px; font-size: 10px; + max-width: 100%; } .main-info-text { diff --git a/src/main/java/com/openisle/config/SecurityConfig.java b/src/main/java/com/openisle/config/SecurityConfig.java index 1341ecab7..0f8f1bf75 100644 --- a/src/main/java/com/openisle/config/SecurityConfig.java +++ b/src/main/java/com/openisle/config/SecurityConfig.java @@ -79,6 +79,8 @@ public class SecurityConfig { "http://localhost", "http://30.211.97.254:8080", "http://30.211.97.254", + "http://192.168.7.70", + "http://192.168.7.70:8080", websiteUrl, websiteUrl.replace("://www.", "://") )); From ac08fc96ce16946f76f39297f510a28dd729060b Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 3 Aug 2025 00:14:56 +0800 Subject: [PATCH 09/33] =?UTF-8?q?bugfix:=20=E9=A6=96=E9=A1=B5=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=88=92=E5=88=B0=E5=BA=95=E9=83=A8=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E6=9D=A1=E6=95=B0=E6=8D=AE=E4=BC=9A=E8=A2=AB=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E6=A0=8F=E6=8C=A1=E4=BD=8F=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- open-isle-cli/src/views/HomePageView.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/open-isle-cli/src/views/HomePageView.vue b/open-isle-cli/src/views/HomePageView.vue index 04e1492bf..d89d8d894 100644 --- a/open-isle-cli/src/views/HomePageView.vue +++ b/open-isle-cli/src/views/HomePageView.vue @@ -483,6 +483,7 @@ export default { flex-direction: column; align-items: center; width: 100%; + padding-bottom: 100px; } .article-header-container { From 454c7fb8f372c6a1409cc9e5ed62a1957dbdfedc Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sun, 3 Aug 2025 00:33:34 +0800 Subject: [PATCH 10/33] Fix post scroller sync and top time --- open-isle-cli/src/views/PostPageView.vue | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue index 311383bcf..d527320f8 100644 --- a/open-isle-cli/src/views/PostPageView.vue +++ b/open-isle-cli/src/views/PostPageView.vue @@ -89,7 +89,7 @@
loading...
-
{{ postTime }}
+
{{ scrollerTopTime }}
@@ -323,6 +323,12 @@ export default { const lastReplyTime = computed(() => comments.value.length ? comments.value[comments.value.length - 1].time : postTime.value ) + const firstReplyTime = computed(() => + comments.value.length ? comments.value[0].time : postTime.value + ) + const scrollerTopTime = computed(() => + commentSort.value === 'OLDEST' ? postTime.value : firstReplyTime.value + ) watch( () => comments.value.length, @@ -351,8 +357,10 @@ export default { } } - const onSliderInput = () => { - const target = postItems.value[currentIndex.value - 1] + const onSliderInput = (e) => { + const index = Number(e.target.value) + currentIndex.value = index + const target = postItems.value[index - 1] if (target && mainContainer.value) { const top = getTopRelativeTo(target, mainContainer.value) mainContainer.value.scrollTo({ top, behavior: 'instant' }) @@ -584,6 +592,7 @@ export default { tags, comments, postTime, + scrollerTopTime, lastReplyTime, postItems, mainContainer, From a128bfa9600dbdaf124779d81b0af37bb3a8199c Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 3 Aug 2025 00:56:05 +0800 Subject: [PATCH 11/33] =?UTF-8?q?feat:=20=E6=97=B6=E5=BA=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- open-isle-cli/src/views/PostPageView.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue index d527320f8..01afe2316 100644 --- a/open-isle-cli/src/views/PostPageView.vue +++ b/open-isle-cli/src/views/PostPageView.vue @@ -545,6 +545,7 @@ export default { if (res.ok) { const data = await res.json() comments.value = data.map(mapComment) + isFetchingComments.value = false await nextTick() gatherPostItems() } From 06eb9e2c7efaa4d89e4f43e12e72a6b09e82aa19 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:15:14 +0800 Subject: [PATCH 12/33] feat: count nested replies --- open-isle-cli/src/components/CommentItem.vue | 8 +++++--- open-isle-cli/src/views/HomePageView.vue | 9 ++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/open-isle-cli/src/components/CommentItem.vue b/open-isle-cli/src/components/CommentItem.vue index 61ab54538..5050c5e6b 100644 --- a/open-isle-cli/src/components/CommentItem.vue +++ b/open-isle-cli/src/components/CommentItem.vue @@ -35,10 +35,10 @@
-
+
- {{ comment.reply.length }}条回复 + {{ replyCount }}条回复
@@ -110,6 +110,8 @@ const CommentItem = { const lightboxIndex = ref(0) const lightboxImgs = ref([]) const loggedIn = computed(() => authState.loggedIn) + const countReplies = (list) => list.reduce((sum, r) => sum + 1 + countReplies(r.reply || []), 0) + const replyCount = computed(() => countReplies(props.comment.reply || [])) const toggleReplies = () => { showReplies.value = !showReplies.value } @@ -206,7 +208,7 @@ const CommentItem = { lightboxVisible.value = true } } - return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleContentClick, loggedIn } + return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleContentClick, loggedIn, replyCount } } } diff --git a/open-isle-cli/src/views/HomePageView.vue b/open-isle-cli/src/views/HomePageView.vue index d89d8d894..8fea56c14 100644 --- a/open-isle-cli/src/views/HomePageView.vue +++ b/open-isle-cli/src/views/HomePageView.vue @@ -165,6 +165,9 @@ export default { const pageSize = 10 const allLoaded = ref(false) + const countComments = (list) => + list.reduce((sum, c) => sum + 1 + countComments(c.replies || []), 0) + const loadOptions = async () => { if (selectedCategory.value && !isNaN(selectedCategory.value)) { try { @@ -254,7 +257,7 @@ export default { category: p.category, tags: p.tags || [], members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })), - comments: (p.comments || []).length, + comments: countComments(p.comments || []), views: p.views, time: TimeManager.format(p.createdAt), pinned: !!p.pinnedAt @@ -291,7 +294,7 @@ export default { category: p.category, tags: p.tags || [], members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })), - comments: (p.comments || []).length, + comments: countComments(p.comments || []), views: p.views, time: TimeManager.format(p.createdAt), pinned: !!p.pinnedAt @@ -328,7 +331,7 @@ export default { category: p.category, tags: p.tags || [], members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })), - comments: (p.comments || []).length, + comments: countComments(p.comments || []), views: p.views, time: TimeManager.format(p.lastReplyAt || p.createdAt), pinned: !!p.pinnedAt From 561a622c269d859bc2be14d3c8a45f51a74babf0 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:16:06 +0800 Subject: [PATCH 13/33] feat: add optimistic reaction update --- .../src/components/ReactionsGroup.vue | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/open-isle-cli/src/components/ReactionsGroup.vue b/open-isle-cli/src/components/ReactionsGroup.vue index a35c0c2b9..7e429a443 100644 --- a/open-isle-cli/src/components/ReactionsGroup.vue +++ b/open-isle-cli/src/components/ReactionsGroup.vue @@ -128,10 +128,23 @@ export default { toast.error('请先登录') return } + const url = props.contentType === 'post' + ? `${API_BASE_URL}/api/posts/${props.contentId}/reactions` + : `${API_BASE_URL}/api/comments/${props.contentId}/reactions` + + // optimistic update + const existingIdx = reactions.value.findIndex(r => r.type === type && r.user === authState.username) + let tempReaction = null + let removedReaction = null + if (existingIdx > -1) { + removedReaction = reactions.value.splice(existingIdx, 1)[0] + } else { + tempReaction = { type, user: authState.username } + reactions.value.push(tempReaction) + } + emit('update:modelValue', reactions.value) + try { - const url = props.contentType === 'post' - ? `${API_BASE_URL}/api/posts/${props.contentId}/reactions` - : `${API_BASE_URL}/api/comments/${props.contentId}/reactions` const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, @@ -139,20 +152,40 @@ export default { }) if (res.ok) { if (res.status === 204) { - const idx = reactions.value.findIndex(r => r.type === type && r.user === authState.username) - if (idx > -1) reactions.value.splice(idx, 1) + // removal already reflected } else { const data = await res.json() - reactions.value.push(data) + const idx = tempReaction ? reactions.value.indexOf(tempReaction) : -1 + if (idx > -1) { + reactions.value.splice(idx, 1, data) + } else if (removedReaction) { + // server added back reaction even though we removed? restore data + reactions.value.push(data) + } if (data.reward && data.reward > 0) { toast.success(`获得 ${data.reward} 经验值`) } } emit('update:modelValue', reactions.value) } else { + // revert optimistic update on failure + if (tempReaction) { + const idx = reactions.value.indexOf(tempReaction) + if (idx > -1) reactions.value.splice(idx, 1) + } else if (removedReaction) { + reactions.value.push(removedReaction) + } + emit('update:modelValue', reactions.value) toast.error('操作失败') } } catch (e) { + if (tempReaction) { + const idx = reactions.value.indexOf(tempReaction) + if (idx > -1) reactions.value.splice(idx, 1) + } else if (removedReaction) { + reactions.value.push(removedReaction) + } + emit('update:modelValue', reactions.value) toast.error('操作失败') } } From c08723574d3ad84e423c244ba3806a7b5811593c Mon Sep 17 00:00:00 2001 From: WilliamColton Date: Sun, 3 Aug 2025 01:27:28 +0800 Subject: [PATCH 14/33] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml => backend/pom.xml | 0 .../com/openisle/OpenIsleApplication.java | 0 .../openisle/config/ActivityInitializer.java | 0 .../java/com/openisle/config/AsyncConfig.java | 0 .../config/CustomAccessDeniedHandler.java | 0 .../com/openisle/config/SecurityConfig.java | 0 .../controller/ActivityController.java | 0 .../controller/AdminConfigController.java | 0 .../openisle/controller/AdminController.java | 0 .../controller/AdminPostController.java | 0 .../controller/AdminTagController.java | 0 .../controller/AdminUserController.java | 0 .../com/openisle/controller/AiController.java | 0 .../openisle/controller/AuthController.java | 0 .../controller/CategoryController.java | 0 .../controller/CommentController.java | 0 .../openisle/controller/ConfigController.java | 0 .../openisle/controller/DraftController.java | 0 .../controller/GlobalExceptionHandler.java | 0 .../openisle/controller/HelloController.java | 0 .../controller/NotificationController.java | 0 .../openisle/controller/PostController.java | 0 .../PushSubscriptionController.java | 0 .../controller/ReactionController.java | 0 .../openisle/controller/SearchController.java | 0 .../controller/SitemapController.java | 0 .../openisle/controller/StatController.java | 0 .../controller/SubscriptionController.java | 0 .../openisle/controller/TagController.java | 0 .../openisle/controller/UploadController.java | 0 .../openisle/controller/UserController.java | 0 .../openisle/exception/FieldException.java | 0 .../openisle/exception/NotFoundException.java | 0 .../exception/RateLimitException.java | 0 .../java/com/openisle/model/Activity.java | 0 .../java/com/openisle/model/ActivityType.java | 0 .../com/openisle/model/AiFormatUsage.java | 0 .../java/com/openisle/model/Category.java | 0 .../main/java/com/openisle/model/Comment.java | 0 .../java/com/openisle/model/CommentSort.java | 0 .../openisle/model/CommentSubscription.java | 0 .../main/java/com/openisle/model/Draft.java | 0 .../com/openisle/model/ExperienceLog.java | 0 .../main/java/com/openisle/model/Image.java | 0 .../java/com/openisle/model/Notification.java | 0 .../com/openisle/model/NotificationType.java | 0 .../com/openisle/model/PasswordStrength.java | 0 .../main/java/com/openisle/model/Post.java | 0 .../java/com/openisle/model/PostRead.java | 0 .../java/com/openisle/model/PostStatus.java | 0 .../com/openisle/model/PostSubscription.java | 0 .../java/com/openisle/model/PublishMode.java | 0 .../com/openisle/model/PushSubscription.java | 0 .../java/com/openisle/model/Reaction.java | 0 .../java/com/openisle/model/ReactionType.java | 0 .../java/com/openisle/model/RegisterMode.java | 0 .../main/java/com/openisle/model/Role.java | 0 .../main/java/com/openisle/model/Tag.java | 0 .../main/java/com/openisle/model/User.java | 0 .../com/openisle/model/UserSubscription.java | 0 .../java/com/openisle/model/UserVisit.java | 0 .../repository/ActivityRepository.java | 0 .../repository/AiFormatUsageRepository.java | 0 .../repository/CategoryRepository.java | 0 .../repository/CommentRepository.java | 0 .../CommentSubscriptionRepository.java | 0 .../openisle/repository/DraftRepository.java | 0 .../repository/ExperienceLogRepository.java | 0 .../openisle/repository/ImageRepository.java | 0 .../repository/NotificationRepository.java | 0 .../repository/PostReadRepository.java | 0 .../openisle/repository/PostRepository.java | 0 .../PostSubscriptionRepository.java | 0 .../PushSubscriptionRepository.java | 0 .../repository/ReactionRepository.java | 0 .../openisle/repository/TagRepository.java | 0 .../openisle/repository/UserRepository.java | 0 .../UserSubscriptionRepository.java | 0 .../repository/UserVisitRepository.java | 0 .../com/openisle/service/ActivityService.java | 0 .../com/openisle/service/AiUsageService.java | 0 .../com/openisle/service/AvatarGenerator.java | 0 .../com/openisle/service/CaptchaService.java | 0 .../com/openisle/service/CategoryService.java | 0 .../com/openisle/service/CommentService.java | 0 .../openisle/service/CosImageUploader.java | 0 .../openisle/service/DiscordAuthService.java | 0 .../com/openisle/service/DraftService.java | 0 .../com/openisle/service/EmailSender.java | 0 .../openisle/service/GithubAuthService.java | 0 .../openisle/service/GoogleAuthService.java | 0 .../com/openisle/service/ImageUploader.java | 0 .../java/com/openisle/service/JwtService.java | 0 .../com/openisle/service/LevelService.java | 0 .../openisle/service/NotificationService.java | 0 .../com/openisle/service/OpenAiService.java | 0 .../openisle/service/PasswordValidator.java | 0 .../com/openisle/service/PostReadService.java | 0 .../com/openisle/service/PostService.java | 0 .../service/PushNotificationService.java | 0 .../service/PushSubscriptionService.java | 0 .../com/openisle/service/ReactionService.java | 0 .../openisle/service/RecaptchaService.java | 0 .../openisle/service/RegisterModeService.java | 0 .../openisle/service/ResendEmailSender.java | 0 .../com/openisle/service/SearchService.java | 0 .../openisle/service/SubscriptionService.java | 0 .../java/com/openisle/service/TagService.java | 0 .../com/openisle/service/TagValidator.java | 0 .../openisle/service/TwitterAuthService.java | 0 .../com/openisle/service/UserService.java | 0 .../openisle/service/UserVisitService.java | 0 .../openisle/service/UsernameValidator.java | 0 .../main/resources/application.properties | 0 .../controller/AdminControllerTest.java | 0 .../controller/AuthControllerTest.java | 0 .../controller/CategoryControllerTest.java | 0 .../controller/CommentControllerTest.java | 0 .../controller/HelloControllerTest.java | 0 .../NotificationControllerTest.java | 0 .../controller/PostControllerTest.java | 0 .../PushSubscriptionControllerTest.java | 0 .../controller/ReactionControllerTest.java | 0 .../controller/SearchControllerTest.java | 0 .../controller/StatControllerTest.java | 0 .../controller/TagControllerTest.java | 0 .../controller/UserControllerTest.java | 0 .../ComplexFlowIntegrationTest.java | 0 .../PublishModeIntegrationTest.java | 0 .../integration/SearchIntegrationTest.java | 0 .../openisle/service/CommentServiceTest.java | 0 .../service/CosImageUploaderTest.java | 0 .../service/NotificationServiceTest.java | 0 .../service/PasswordValidatorTest.java | 0 .../com/openisle/service/PostServiceTest.java | 0 .../openisle/service/ReactionServiceTest.java | 0 .../openisle/service/SearchServiceTest.java | 0 .../service/UsernameValidatorTest.java | 0 .../test/resources/application.properties | 0 {open-isle-cli => frontend}/babel.config.js | 0 {open-isle-cli => frontend}/jsconfig.json | 0 {open-isle-cli => frontend}/package-lock.json | 0 {open-isle-cli => frontend}/package.json | 0 .../public/about/about.md | 0 .../public/about/agreement.md | 0 .../public/about/guideline.md | 0 .../public/about/privacy.md | 0 .../public/favicon.ico | Bin .../public/googlea6f18c4a543fb356.html | 0 {open-isle-cli => frontend}/public/index.html | 0 .../public/notifications-sw.js | 0 {open-isle-cli => frontend}/public/robots.txt | 0 {open-isle-cli => frontend}/src/App.vue | 0 .../src/assets/global.css | 0 .../src/assets/icons/discord.svg | 0 .../src/assets/icons/github.svg | 0 .../src/assets/icons/google.svg | 0 .../src/assets/icons/twitter.svg | 0 .../src/assets/toast.css | 0 .../src/components/ActivityPopup.vue | 0 .../src/components/ArticleCategory.vue | 0 .../src/components/ArticleTags.vue | 0 .../src/components/BaseInput.vue | 0 .../src/components/BasePlaceholder.vue | 0 .../src/components/BasePopup.vue | 0 .../src/components/BaseTimeline.vue | 0 .../src/components/CategorySelect.vue | 0 .../src/components/CommentEditor.vue | 0 .../src/components/CommentItem.vue | 0 .../src/components/Dropdown.vue | 0 .../src/components/DropdownMenu.vue | 0 .../src/components/GlobalPopups.vue | 0 .../src/components/HeaderComponent.vue | 0 .../src/components/LevelProgress.vue | 0 .../src/components/LoginOverlay.vue | 0 .../src/components/MenuComponent.vue | 0 .../components/MilkTeaActivityComponent.vue | 4 ++-- .../src/components/NotificationContainer.vue | 0 .../src/components/PostEditor.vue | 0 .../src/components/ProgressBar.vue | 0 .../src/components/ReactionsGroup.vue | 0 .../src/components/SearchDropdown.vue | 0 .../src/components/TagSelect.vue | 0 .../src/components/UserList.vue | 0 {open-isle-cli => frontend}/src/constants.js | 0 {open-isle-cli => frontend}/src/main.js | 0 .../src/router/index.js | 0 {open-isle-cli => frontend}/src/utils/auth.js | 0 .../src/utils/discord.js | 0 .../src/utils/github.js | 0 .../src/utils/google.js | 0 .../src/utils/level.js | 0 .../src/utils/markdown.js | 0 .../src/utils/notification.js | 0 {open-isle-cli => frontend}/src/utils/push.js | 0 .../src/utils/screen.js | 0 .../src/utils/theme.js | 0 {open-isle-cli => frontend}/src/utils/time.js | 0 .../src/utils/twitter.js | 0 {open-isle-cli => frontend}/src/utils/user.js | 0 .../src/utils/vditor.js | 0 .../src/views/AboutPageView.vue | 0 .../src/views/ActivityListPageView.vue | 0 .../src/views/DiscordCallbackPageView.vue | 0 .../src/views/EditPostPageView.vue | 0 .../src/views/ForgotPasswordPageView.vue | 0 .../src/views/GithubCallbackPageView.vue | 0 .../src/views/HomePageView.vue | 0 .../src/views/LoginPageView.vue | 0 .../src/views/MessagePageView.vue | 0 .../src/views/NewPostPageView.vue | 0 .../src/views/NotFoundPageView.vue | 0 .../src/views/PostPageView.vue | 0 .../src/views/ProfileView.vue | 0 .../src/views/SettingsPageView.vue | 0 .../src/views/SignupPageView.vue | 0 .../src/views/SignupReasonPageView.vue | 0 .../src/views/SiteStatsPageView.vue | 0 .../src/views/TwitterCallbackPageView.vue | 0 {open-isle-cli => frontend}/vue.config.js | 0 package-lock.json | 18 ------------------ package.json | 5 ----- 222 files changed, 2 insertions(+), 25 deletions(-) rename pom.xml => backend/pom.xml (100%) rename {src => backend/src}/main/java/com/openisle/OpenIsleApplication.java (100%) rename {src => backend/src}/main/java/com/openisle/config/ActivityInitializer.java (100%) rename {src => backend/src}/main/java/com/openisle/config/AsyncConfig.java (100%) rename {src => backend/src}/main/java/com/openisle/config/CustomAccessDeniedHandler.java (100%) rename {src => backend/src}/main/java/com/openisle/config/SecurityConfig.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/ActivityController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AdminConfigController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AdminController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AdminPostController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AdminTagController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AdminUserController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AiController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/AuthController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/CategoryController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/CommentController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/ConfigController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/DraftController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/GlobalExceptionHandler.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/HelloController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/NotificationController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/PostController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/PushSubscriptionController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/ReactionController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/SearchController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/SitemapController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/StatController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/SubscriptionController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/TagController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/UploadController.java (100%) rename {src => backend/src}/main/java/com/openisle/controller/UserController.java (100%) rename {src => backend/src}/main/java/com/openisle/exception/FieldException.java (100%) rename {src => backend/src}/main/java/com/openisle/exception/NotFoundException.java (100%) rename {src => backend/src}/main/java/com/openisle/exception/RateLimitException.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Activity.java (100%) rename {src => backend/src}/main/java/com/openisle/model/ActivityType.java (100%) rename {src => backend/src}/main/java/com/openisle/model/AiFormatUsage.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Category.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Comment.java (100%) rename {src => backend/src}/main/java/com/openisle/model/CommentSort.java (100%) rename {src => backend/src}/main/java/com/openisle/model/CommentSubscription.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Draft.java (100%) rename {src => backend/src}/main/java/com/openisle/model/ExperienceLog.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Image.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Notification.java (100%) rename {src => backend/src}/main/java/com/openisle/model/NotificationType.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PasswordStrength.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Post.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PostRead.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PostStatus.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PostSubscription.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PublishMode.java (100%) rename {src => backend/src}/main/java/com/openisle/model/PushSubscription.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Reaction.java (100%) rename {src => backend/src}/main/java/com/openisle/model/ReactionType.java (100%) rename {src => backend/src}/main/java/com/openisle/model/RegisterMode.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Role.java (100%) rename {src => backend/src}/main/java/com/openisle/model/Tag.java (100%) rename {src => backend/src}/main/java/com/openisle/model/User.java (100%) rename {src => backend/src}/main/java/com/openisle/model/UserSubscription.java (100%) rename {src => backend/src}/main/java/com/openisle/model/UserVisit.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/ActivityRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/AiFormatUsageRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/CategoryRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/CommentRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/CommentSubscriptionRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/DraftRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/ExperienceLogRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/ImageRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/NotificationRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/PostReadRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/PostRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/PostSubscriptionRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/PushSubscriptionRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/ReactionRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/TagRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/UserRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/UserSubscriptionRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/repository/UserVisitRepository.java (100%) rename {src => backend/src}/main/java/com/openisle/service/ActivityService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/AiUsageService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/AvatarGenerator.java (100%) rename {src => backend/src}/main/java/com/openisle/service/CaptchaService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/CategoryService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/CommentService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/CosImageUploader.java (100%) rename {src => backend/src}/main/java/com/openisle/service/DiscordAuthService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/DraftService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/EmailSender.java (100%) rename {src => backend/src}/main/java/com/openisle/service/GithubAuthService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/GoogleAuthService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/ImageUploader.java (100%) rename {src => backend/src}/main/java/com/openisle/service/JwtService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/LevelService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/NotificationService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/OpenAiService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/PasswordValidator.java (100%) rename {src => backend/src}/main/java/com/openisle/service/PostReadService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/PostService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/PushNotificationService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/PushSubscriptionService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/ReactionService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/RecaptchaService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/RegisterModeService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/ResendEmailSender.java (100%) rename {src => backend/src}/main/java/com/openisle/service/SearchService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/SubscriptionService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/TagService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/TagValidator.java (100%) rename {src => backend/src}/main/java/com/openisle/service/TwitterAuthService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/UserService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/UserVisitService.java (100%) rename {src => backend/src}/main/java/com/openisle/service/UsernameValidator.java (100%) rename {src => backend/src}/main/resources/application.properties (100%) rename {src => backend/src}/test/java/com/openisle/controller/AdminControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/AuthControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/CategoryControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/CommentControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/HelloControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/NotificationControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/PostControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/PushSubscriptionControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/ReactionControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/SearchControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/StatControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/TagControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/controller/UserControllerTest.java (100%) rename {src => backend/src}/test/java/com/openisle/integration/ComplexFlowIntegrationTest.java (100%) rename {src => backend/src}/test/java/com/openisle/integration/PublishModeIntegrationTest.java (100%) rename {src => backend/src}/test/java/com/openisle/integration/SearchIntegrationTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/CommentServiceTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/CosImageUploaderTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/NotificationServiceTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/PasswordValidatorTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/PostServiceTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/ReactionServiceTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/SearchServiceTest.java (100%) rename {src => backend/src}/test/java/com/openisle/service/UsernameValidatorTest.java (100%) rename {src => backend/src}/test/resources/application.properties (100%) rename {open-isle-cli => frontend}/babel.config.js (100%) rename {open-isle-cli => frontend}/jsconfig.json (100%) rename {open-isle-cli => frontend}/package-lock.json (100%) rename {open-isle-cli => frontend}/package.json (100%) rename {open-isle-cli => frontend}/public/about/about.md (100%) rename {open-isle-cli => frontend}/public/about/agreement.md (100%) rename {open-isle-cli => frontend}/public/about/guideline.md (100%) rename {open-isle-cli => frontend}/public/about/privacy.md (100%) rename {open-isle-cli => frontend}/public/favicon.ico (100%) rename {open-isle-cli => frontend}/public/googlea6f18c4a543fb356.html (100%) rename {open-isle-cli => frontend}/public/index.html (100%) rename {open-isle-cli => frontend}/public/notifications-sw.js (100%) rename {open-isle-cli => frontend}/public/robots.txt (100%) rename {open-isle-cli => frontend}/src/App.vue (100%) rename {open-isle-cli => frontend}/src/assets/global.css (100%) rename {open-isle-cli => frontend}/src/assets/icons/discord.svg (100%) rename {open-isle-cli => frontend}/src/assets/icons/github.svg (100%) rename {open-isle-cli => frontend}/src/assets/icons/google.svg (100%) rename {open-isle-cli => frontend}/src/assets/icons/twitter.svg (100%) rename {open-isle-cli => frontend}/src/assets/toast.css (100%) rename {open-isle-cli => frontend}/src/components/ActivityPopup.vue (100%) rename {open-isle-cli => frontend}/src/components/ArticleCategory.vue (100%) rename {open-isle-cli => frontend}/src/components/ArticleTags.vue (100%) rename {open-isle-cli => frontend}/src/components/BaseInput.vue (100%) rename {open-isle-cli => frontend}/src/components/BasePlaceholder.vue (100%) rename {open-isle-cli => frontend}/src/components/BasePopup.vue (100%) rename {open-isle-cli => frontend}/src/components/BaseTimeline.vue (100%) rename {open-isle-cli => frontend}/src/components/CategorySelect.vue (100%) rename {open-isle-cli => frontend}/src/components/CommentEditor.vue (100%) rename {open-isle-cli => frontend}/src/components/CommentItem.vue (100%) rename {open-isle-cli => frontend}/src/components/Dropdown.vue (100%) rename {open-isle-cli => frontend}/src/components/DropdownMenu.vue (100%) rename {open-isle-cli => frontend}/src/components/GlobalPopups.vue (100%) rename {open-isle-cli => frontend}/src/components/HeaderComponent.vue (100%) rename {open-isle-cli => frontend}/src/components/LevelProgress.vue (100%) rename {open-isle-cli => frontend}/src/components/LoginOverlay.vue (100%) rename {open-isle-cli => frontend}/src/components/MenuComponent.vue (100%) rename {open-isle-cli => frontend}/src/components/MilkTeaActivityComponent.vue (98%) rename {open-isle-cli => frontend}/src/components/NotificationContainer.vue (100%) rename {open-isle-cli => frontend}/src/components/PostEditor.vue (100%) rename {open-isle-cli => frontend}/src/components/ProgressBar.vue (100%) rename {open-isle-cli => frontend}/src/components/ReactionsGroup.vue (100%) rename {open-isle-cli => frontend}/src/components/SearchDropdown.vue (100%) rename {open-isle-cli => frontend}/src/components/TagSelect.vue (100%) rename {open-isle-cli => frontend}/src/components/UserList.vue (100%) rename {open-isle-cli => frontend}/src/constants.js (100%) rename {open-isle-cli => frontend}/src/main.js (100%) rename {open-isle-cli => frontend}/src/router/index.js (100%) rename {open-isle-cli => frontend}/src/utils/auth.js (100%) rename {open-isle-cli => frontend}/src/utils/discord.js (100%) rename {open-isle-cli => frontend}/src/utils/github.js (100%) rename {open-isle-cli => frontend}/src/utils/google.js (100%) rename {open-isle-cli => frontend}/src/utils/level.js (100%) rename {open-isle-cli => frontend}/src/utils/markdown.js (100%) rename {open-isle-cli => frontend}/src/utils/notification.js (100%) rename {open-isle-cli => frontend}/src/utils/push.js (100%) rename {open-isle-cli => frontend}/src/utils/screen.js (100%) rename {open-isle-cli => frontend}/src/utils/theme.js (100%) rename {open-isle-cli => frontend}/src/utils/time.js (100%) rename {open-isle-cli => frontend}/src/utils/twitter.js (100%) rename {open-isle-cli => frontend}/src/utils/user.js (100%) rename {open-isle-cli => frontend}/src/utils/vditor.js (100%) rename {open-isle-cli => frontend}/src/views/AboutPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/ActivityListPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/DiscordCallbackPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/EditPostPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/ForgotPasswordPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/GithubCallbackPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/HomePageView.vue (100%) rename {open-isle-cli => frontend}/src/views/LoginPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/MessagePageView.vue (100%) rename {open-isle-cli => frontend}/src/views/NewPostPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/NotFoundPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/PostPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/ProfileView.vue (100%) rename {open-isle-cli => frontend}/src/views/SettingsPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/SignupPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/SignupReasonPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/SiteStatsPageView.vue (100%) rename {open-isle-cli => frontend}/src/views/TwitterCallbackPageView.vue (100%) rename {open-isle-cli => frontend}/vue.config.js (100%) delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/pom.xml b/backend/pom.xml similarity index 100% rename from pom.xml rename to backend/pom.xml diff --git a/src/main/java/com/openisle/OpenIsleApplication.java b/backend/src/main/java/com/openisle/OpenIsleApplication.java similarity index 100% rename from src/main/java/com/openisle/OpenIsleApplication.java rename to backend/src/main/java/com/openisle/OpenIsleApplication.java diff --git a/src/main/java/com/openisle/config/ActivityInitializer.java b/backend/src/main/java/com/openisle/config/ActivityInitializer.java similarity index 100% rename from src/main/java/com/openisle/config/ActivityInitializer.java rename to backend/src/main/java/com/openisle/config/ActivityInitializer.java diff --git a/src/main/java/com/openisle/config/AsyncConfig.java b/backend/src/main/java/com/openisle/config/AsyncConfig.java similarity index 100% rename from src/main/java/com/openisle/config/AsyncConfig.java rename to backend/src/main/java/com/openisle/config/AsyncConfig.java diff --git a/src/main/java/com/openisle/config/CustomAccessDeniedHandler.java b/backend/src/main/java/com/openisle/config/CustomAccessDeniedHandler.java similarity index 100% rename from src/main/java/com/openisle/config/CustomAccessDeniedHandler.java rename to backend/src/main/java/com/openisle/config/CustomAccessDeniedHandler.java diff --git a/src/main/java/com/openisle/config/SecurityConfig.java b/backend/src/main/java/com/openisle/config/SecurityConfig.java similarity index 100% rename from src/main/java/com/openisle/config/SecurityConfig.java rename to backend/src/main/java/com/openisle/config/SecurityConfig.java diff --git a/src/main/java/com/openisle/controller/ActivityController.java b/backend/src/main/java/com/openisle/controller/ActivityController.java similarity index 100% rename from src/main/java/com/openisle/controller/ActivityController.java rename to backend/src/main/java/com/openisle/controller/ActivityController.java diff --git a/src/main/java/com/openisle/controller/AdminConfigController.java b/backend/src/main/java/com/openisle/controller/AdminConfigController.java similarity index 100% rename from src/main/java/com/openisle/controller/AdminConfigController.java rename to backend/src/main/java/com/openisle/controller/AdminConfigController.java diff --git a/src/main/java/com/openisle/controller/AdminController.java b/backend/src/main/java/com/openisle/controller/AdminController.java similarity index 100% rename from src/main/java/com/openisle/controller/AdminController.java rename to backend/src/main/java/com/openisle/controller/AdminController.java diff --git a/src/main/java/com/openisle/controller/AdminPostController.java b/backend/src/main/java/com/openisle/controller/AdminPostController.java similarity index 100% rename from src/main/java/com/openisle/controller/AdminPostController.java rename to backend/src/main/java/com/openisle/controller/AdminPostController.java diff --git a/src/main/java/com/openisle/controller/AdminTagController.java b/backend/src/main/java/com/openisle/controller/AdminTagController.java similarity index 100% rename from src/main/java/com/openisle/controller/AdminTagController.java rename to backend/src/main/java/com/openisle/controller/AdminTagController.java diff --git a/src/main/java/com/openisle/controller/AdminUserController.java b/backend/src/main/java/com/openisle/controller/AdminUserController.java similarity index 100% rename from src/main/java/com/openisle/controller/AdminUserController.java rename to backend/src/main/java/com/openisle/controller/AdminUserController.java diff --git a/src/main/java/com/openisle/controller/AiController.java b/backend/src/main/java/com/openisle/controller/AiController.java similarity index 100% rename from src/main/java/com/openisle/controller/AiController.java rename to backend/src/main/java/com/openisle/controller/AiController.java diff --git a/src/main/java/com/openisle/controller/AuthController.java b/backend/src/main/java/com/openisle/controller/AuthController.java similarity index 100% rename from src/main/java/com/openisle/controller/AuthController.java rename to backend/src/main/java/com/openisle/controller/AuthController.java diff --git a/src/main/java/com/openisle/controller/CategoryController.java b/backend/src/main/java/com/openisle/controller/CategoryController.java similarity index 100% rename from src/main/java/com/openisle/controller/CategoryController.java rename to backend/src/main/java/com/openisle/controller/CategoryController.java diff --git a/src/main/java/com/openisle/controller/CommentController.java b/backend/src/main/java/com/openisle/controller/CommentController.java similarity index 100% rename from src/main/java/com/openisle/controller/CommentController.java rename to backend/src/main/java/com/openisle/controller/CommentController.java diff --git a/src/main/java/com/openisle/controller/ConfigController.java b/backend/src/main/java/com/openisle/controller/ConfigController.java similarity index 100% rename from src/main/java/com/openisle/controller/ConfigController.java rename to backend/src/main/java/com/openisle/controller/ConfigController.java diff --git a/src/main/java/com/openisle/controller/DraftController.java b/backend/src/main/java/com/openisle/controller/DraftController.java similarity index 100% rename from src/main/java/com/openisle/controller/DraftController.java rename to backend/src/main/java/com/openisle/controller/DraftController.java diff --git a/src/main/java/com/openisle/controller/GlobalExceptionHandler.java b/backend/src/main/java/com/openisle/controller/GlobalExceptionHandler.java similarity index 100% rename from src/main/java/com/openisle/controller/GlobalExceptionHandler.java rename to backend/src/main/java/com/openisle/controller/GlobalExceptionHandler.java diff --git a/src/main/java/com/openisle/controller/HelloController.java b/backend/src/main/java/com/openisle/controller/HelloController.java similarity index 100% rename from src/main/java/com/openisle/controller/HelloController.java rename to backend/src/main/java/com/openisle/controller/HelloController.java diff --git a/src/main/java/com/openisle/controller/NotificationController.java b/backend/src/main/java/com/openisle/controller/NotificationController.java similarity index 100% rename from src/main/java/com/openisle/controller/NotificationController.java rename to backend/src/main/java/com/openisle/controller/NotificationController.java diff --git a/src/main/java/com/openisle/controller/PostController.java b/backend/src/main/java/com/openisle/controller/PostController.java similarity index 100% rename from src/main/java/com/openisle/controller/PostController.java rename to backend/src/main/java/com/openisle/controller/PostController.java diff --git a/src/main/java/com/openisle/controller/PushSubscriptionController.java b/backend/src/main/java/com/openisle/controller/PushSubscriptionController.java similarity index 100% rename from src/main/java/com/openisle/controller/PushSubscriptionController.java rename to backend/src/main/java/com/openisle/controller/PushSubscriptionController.java diff --git a/src/main/java/com/openisle/controller/ReactionController.java b/backend/src/main/java/com/openisle/controller/ReactionController.java similarity index 100% rename from src/main/java/com/openisle/controller/ReactionController.java rename to backend/src/main/java/com/openisle/controller/ReactionController.java diff --git a/src/main/java/com/openisle/controller/SearchController.java b/backend/src/main/java/com/openisle/controller/SearchController.java similarity index 100% rename from src/main/java/com/openisle/controller/SearchController.java rename to backend/src/main/java/com/openisle/controller/SearchController.java diff --git a/src/main/java/com/openisle/controller/SitemapController.java b/backend/src/main/java/com/openisle/controller/SitemapController.java similarity index 100% rename from src/main/java/com/openisle/controller/SitemapController.java rename to backend/src/main/java/com/openisle/controller/SitemapController.java diff --git a/src/main/java/com/openisle/controller/StatController.java b/backend/src/main/java/com/openisle/controller/StatController.java similarity index 100% rename from src/main/java/com/openisle/controller/StatController.java rename to backend/src/main/java/com/openisle/controller/StatController.java diff --git a/src/main/java/com/openisle/controller/SubscriptionController.java b/backend/src/main/java/com/openisle/controller/SubscriptionController.java similarity index 100% rename from src/main/java/com/openisle/controller/SubscriptionController.java rename to backend/src/main/java/com/openisle/controller/SubscriptionController.java diff --git a/src/main/java/com/openisle/controller/TagController.java b/backend/src/main/java/com/openisle/controller/TagController.java similarity index 100% rename from src/main/java/com/openisle/controller/TagController.java rename to backend/src/main/java/com/openisle/controller/TagController.java diff --git a/src/main/java/com/openisle/controller/UploadController.java b/backend/src/main/java/com/openisle/controller/UploadController.java similarity index 100% rename from src/main/java/com/openisle/controller/UploadController.java rename to backend/src/main/java/com/openisle/controller/UploadController.java diff --git a/src/main/java/com/openisle/controller/UserController.java b/backend/src/main/java/com/openisle/controller/UserController.java similarity index 100% rename from src/main/java/com/openisle/controller/UserController.java rename to backend/src/main/java/com/openisle/controller/UserController.java diff --git a/src/main/java/com/openisle/exception/FieldException.java b/backend/src/main/java/com/openisle/exception/FieldException.java similarity index 100% rename from src/main/java/com/openisle/exception/FieldException.java rename to backend/src/main/java/com/openisle/exception/FieldException.java diff --git a/src/main/java/com/openisle/exception/NotFoundException.java b/backend/src/main/java/com/openisle/exception/NotFoundException.java similarity index 100% rename from src/main/java/com/openisle/exception/NotFoundException.java rename to backend/src/main/java/com/openisle/exception/NotFoundException.java diff --git a/src/main/java/com/openisle/exception/RateLimitException.java b/backend/src/main/java/com/openisle/exception/RateLimitException.java similarity index 100% rename from src/main/java/com/openisle/exception/RateLimitException.java rename to backend/src/main/java/com/openisle/exception/RateLimitException.java diff --git a/src/main/java/com/openisle/model/Activity.java b/backend/src/main/java/com/openisle/model/Activity.java similarity index 100% rename from src/main/java/com/openisle/model/Activity.java rename to backend/src/main/java/com/openisle/model/Activity.java diff --git a/src/main/java/com/openisle/model/ActivityType.java b/backend/src/main/java/com/openisle/model/ActivityType.java similarity index 100% rename from src/main/java/com/openisle/model/ActivityType.java rename to backend/src/main/java/com/openisle/model/ActivityType.java diff --git a/src/main/java/com/openisle/model/AiFormatUsage.java b/backend/src/main/java/com/openisle/model/AiFormatUsage.java similarity index 100% rename from src/main/java/com/openisle/model/AiFormatUsage.java rename to backend/src/main/java/com/openisle/model/AiFormatUsage.java diff --git a/src/main/java/com/openisle/model/Category.java b/backend/src/main/java/com/openisle/model/Category.java similarity index 100% rename from src/main/java/com/openisle/model/Category.java rename to backend/src/main/java/com/openisle/model/Category.java diff --git a/src/main/java/com/openisle/model/Comment.java b/backend/src/main/java/com/openisle/model/Comment.java similarity index 100% rename from src/main/java/com/openisle/model/Comment.java rename to backend/src/main/java/com/openisle/model/Comment.java diff --git a/src/main/java/com/openisle/model/CommentSort.java b/backend/src/main/java/com/openisle/model/CommentSort.java similarity index 100% rename from src/main/java/com/openisle/model/CommentSort.java rename to backend/src/main/java/com/openisle/model/CommentSort.java diff --git a/src/main/java/com/openisle/model/CommentSubscription.java b/backend/src/main/java/com/openisle/model/CommentSubscription.java similarity index 100% rename from src/main/java/com/openisle/model/CommentSubscription.java rename to backend/src/main/java/com/openisle/model/CommentSubscription.java diff --git a/src/main/java/com/openisle/model/Draft.java b/backend/src/main/java/com/openisle/model/Draft.java similarity index 100% rename from src/main/java/com/openisle/model/Draft.java rename to backend/src/main/java/com/openisle/model/Draft.java diff --git a/src/main/java/com/openisle/model/ExperienceLog.java b/backend/src/main/java/com/openisle/model/ExperienceLog.java similarity index 100% rename from src/main/java/com/openisle/model/ExperienceLog.java rename to backend/src/main/java/com/openisle/model/ExperienceLog.java diff --git a/src/main/java/com/openisle/model/Image.java b/backend/src/main/java/com/openisle/model/Image.java similarity index 100% rename from src/main/java/com/openisle/model/Image.java rename to backend/src/main/java/com/openisle/model/Image.java diff --git a/src/main/java/com/openisle/model/Notification.java b/backend/src/main/java/com/openisle/model/Notification.java similarity index 100% rename from src/main/java/com/openisle/model/Notification.java rename to backend/src/main/java/com/openisle/model/Notification.java diff --git a/src/main/java/com/openisle/model/NotificationType.java b/backend/src/main/java/com/openisle/model/NotificationType.java similarity index 100% rename from src/main/java/com/openisle/model/NotificationType.java rename to backend/src/main/java/com/openisle/model/NotificationType.java diff --git a/src/main/java/com/openisle/model/PasswordStrength.java b/backend/src/main/java/com/openisle/model/PasswordStrength.java similarity index 100% rename from src/main/java/com/openisle/model/PasswordStrength.java rename to backend/src/main/java/com/openisle/model/PasswordStrength.java diff --git a/src/main/java/com/openisle/model/Post.java b/backend/src/main/java/com/openisle/model/Post.java similarity index 100% rename from src/main/java/com/openisle/model/Post.java rename to backend/src/main/java/com/openisle/model/Post.java diff --git a/src/main/java/com/openisle/model/PostRead.java b/backend/src/main/java/com/openisle/model/PostRead.java similarity index 100% rename from src/main/java/com/openisle/model/PostRead.java rename to backend/src/main/java/com/openisle/model/PostRead.java diff --git a/src/main/java/com/openisle/model/PostStatus.java b/backend/src/main/java/com/openisle/model/PostStatus.java similarity index 100% rename from src/main/java/com/openisle/model/PostStatus.java rename to backend/src/main/java/com/openisle/model/PostStatus.java diff --git a/src/main/java/com/openisle/model/PostSubscription.java b/backend/src/main/java/com/openisle/model/PostSubscription.java similarity index 100% rename from src/main/java/com/openisle/model/PostSubscription.java rename to backend/src/main/java/com/openisle/model/PostSubscription.java diff --git a/src/main/java/com/openisle/model/PublishMode.java b/backend/src/main/java/com/openisle/model/PublishMode.java similarity index 100% rename from src/main/java/com/openisle/model/PublishMode.java rename to backend/src/main/java/com/openisle/model/PublishMode.java diff --git a/src/main/java/com/openisle/model/PushSubscription.java b/backend/src/main/java/com/openisle/model/PushSubscription.java similarity index 100% rename from src/main/java/com/openisle/model/PushSubscription.java rename to backend/src/main/java/com/openisle/model/PushSubscription.java diff --git a/src/main/java/com/openisle/model/Reaction.java b/backend/src/main/java/com/openisle/model/Reaction.java similarity index 100% rename from src/main/java/com/openisle/model/Reaction.java rename to backend/src/main/java/com/openisle/model/Reaction.java diff --git a/src/main/java/com/openisle/model/ReactionType.java b/backend/src/main/java/com/openisle/model/ReactionType.java similarity index 100% rename from src/main/java/com/openisle/model/ReactionType.java rename to backend/src/main/java/com/openisle/model/ReactionType.java diff --git a/src/main/java/com/openisle/model/RegisterMode.java b/backend/src/main/java/com/openisle/model/RegisterMode.java similarity index 100% rename from src/main/java/com/openisle/model/RegisterMode.java rename to backend/src/main/java/com/openisle/model/RegisterMode.java diff --git a/src/main/java/com/openisle/model/Role.java b/backend/src/main/java/com/openisle/model/Role.java similarity index 100% rename from src/main/java/com/openisle/model/Role.java rename to backend/src/main/java/com/openisle/model/Role.java diff --git a/src/main/java/com/openisle/model/Tag.java b/backend/src/main/java/com/openisle/model/Tag.java similarity index 100% rename from src/main/java/com/openisle/model/Tag.java rename to backend/src/main/java/com/openisle/model/Tag.java diff --git a/src/main/java/com/openisle/model/User.java b/backend/src/main/java/com/openisle/model/User.java similarity index 100% rename from src/main/java/com/openisle/model/User.java rename to backend/src/main/java/com/openisle/model/User.java diff --git a/src/main/java/com/openisle/model/UserSubscription.java b/backend/src/main/java/com/openisle/model/UserSubscription.java similarity index 100% rename from src/main/java/com/openisle/model/UserSubscription.java rename to backend/src/main/java/com/openisle/model/UserSubscription.java diff --git a/src/main/java/com/openisle/model/UserVisit.java b/backend/src/main/java/com/openisle/model/UserVisit.java similarity index 100% rename from src/main/java/com/openisle/model/UserVisit.java rename to backend/src/main/java/com/openisle/model/UserVisit.java diff --git a/src/main/java/com/openisle/repository/ActivityRepository.java b/backend/src/main/java/com/openisle/repository/ActivityRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/ActivityRepository.java rename to backend/src/main/java/com/openisle/repository/ActivityRepository.java diff --git a/src/main/java/com/openisle/repository/AiFormatUsageRepository.java b/backend/src/main/java/com/openisle/repository/AiFormatUsageRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/AiFormatUsageRepository.java rename to backend/src/main/java/com/openisle/repository/AiFormatUsageRepository.java diff --git a/src/main/java/com/openisle/repository/CategoryRepository.java b/backend/src/main/java/com/openisle/repository/CategoryRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/CategoryRepository.java rename to backend/src/main/java/com/openisle/repository/CategoryRepository.java diff --git a/src/main/java/com/openisle/repository/CommentRepository.java b/backend/src/main/java/com/openisle/repository/CommentRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/CommentRepository.java rename to backend/src/main/java/com/openisle/repository/CommentRepository.java diff --git a/src/main/java/com/openisle/repository/CommentSubscriptionRepository.java b/backend/src/main/java/com/openisle/repository/CommentSubscriptionRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/CommentSubscriptionRepository.java rename to backend/src/main/java/com/openisle/repository/CommentSubscriptionRepository.java diff --git a/src/main/java/com/openisle/repository/DraftRepository.java b/backend/src/main/java/com/openisle/repository/DraftRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/DraftRepository.java rename to backend/src/main/java/com/openisle/repository/DraftRepository.java diff --git a/src/main/java/com/openisle/repository/ExperienceLogRepository.java b/backend/src/main/java/com/openisle/repository/ExperienceLogRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/ExperienceLogRepository.java rename to backend/src/main/java/com/openisle/repository/ExperienceLogRepository.java diff --git a/src/main/java/com/openisle/repository/ImageRepository.java b/backend/src/main/java/com/openisle/repository/ImageRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/ImageRepository.java rename to backend/src/main/java/com/openisle/repository/ImageRepository.java diff --git a/src/main/java/com/openisle/repository/NotificationRepository.java b/backend/src/main/java/com/openisle/repository/NotificationRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/NotificationRepository.java rename to backend/src/main/java/com/openisle/repository/NotificationRepository.java diff --git a/src/main/java/com/openisle/repository/PostReadRepository.java b/backend/src/main/java/com/openisle/repository/PostReadRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/PostReadRepository.java rename to backend/src/main/java/com/openisle/repository/PostReadRepository.java diff --git a/src/main/java/com/openisle/repository/PostRepository.java b/backend/src/main/java/com/openisle/repository/PostRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/PostRepository.java rename to backend/src/main/java/com/openisle/repository/PostRepository.java diff --git a/src/main/java/com/openisle/repository/PostSubscriptionRepository.java b/backend/src/main/java/com/openisle/repository/PostSubscriptionRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/PostSubscriptionRepository.java rename to backend/src/main/java/com/openisle/repository/PostSubscriptionRepository.java diff --git a/src/main/java/com/openisle/repository/PushSubscriptionRepository.java b/backend/src/main/java/com/openisle/repository/PushSubscriptionRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/PushSubscriptionRepository.java rename to backend/src/main/java/com/openisle/repository/PushSubscriptionRepository.java diff --git a/src/main/java/com/openisle/repository/ReactionRepository.java b/backend/src/main/java/com/openisle/repository/ReactionRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/ReactionRepository.java rename to backend/src/main/java/com/openisle/repository/ReactionRepository.java diff --git a/src/main/java/com/openisle/repository/TagRepository.java b/backend/src/main/java/com/openisle/repository/TagRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/TagRepository.java rename to backend/src/main/java/com/openisle/repository/TagRepository.java diff --git a/src/main/java/com/openisle/repository/UserRepository.java b/backend/src/main/java/com/openisle/repository/UserRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/UserRepository.java rename to backend/src/main/java/com/openisle/repository/UserRepository.java diff --git a/src/main/java/com/openisle/repository/UserSubscriptionRepository.java b/backend/src/main/java/com/openisle/repository/UserSubscriptionRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/UserSubscriptionRepository.java rename to backend/src/main/java/com/openisle/repository/UserSubscriptionRepository.java diff --git a/src/main/java/com/openisle/repository/UserVisitRepository.java b/backend/src/main/java/com/openisle/repository/UserVisitRepository.java similarity index 100% rename from src/main/java/com/openisle/repository/UserVisitRepository.java rename to backend/src/main/java/com/openisle/repository/UserVisitRepository.java diff --git a/src/main/java/com/openisle/service/ActivityService.java b/backend/src/main/java/com/openisle/service/ActivityService.java similarity index 100% rename from src/main/java/com/openisle/service/ActivityService.java rename to backend/src/main/java/com/openisle/service/ActivityService.java diff --git a/src/main/java/com/openisle/service/AiUsageService.java b/backend/src/main/java/com/openisle/service/AiUsageService.java similarity index 100% rename from src/main/java/com/openisle/service/AiUsageService.java rename to backend/src/main/java/com/openisle/service/AiUsageService.java diff --git a/src/main/java/com/openisle/service/AvatarGenerator.java b/backend/src/main/java/com/openisle/service/AvatarGenerator.java similarity index 100% rename from src/main/java/com/openisle/service/AvatarGenerator.java rename to backend/src/main/java/com/openisle/service/AvatarGenerator.java diff --git a/src/main/java/com/openisle/service/CaptchaService.java b/backend/src/main/java/com/openisle/service/CaptchaService.java similarity index 100% rename from src/main/java/com/openisle/service/CaptchaService.java rename to backend/src/main/java/com/openisle/service/CaptchaService.java diff --git a/src/main/java/com/openisle/service/CategoryService.java b/backend/src/main/java/com/openisle/service/CategoryService.java similarity index 100% rename from src/main/java/com/openisle/service/CategoryService.java rename to backend/src/main/java/com/openisle/service/CategoryService.java diff --git a/src/main/java/com/openisle/service/CommentService.java b/backend/src/main/java/com/openisle/service/CommentService.java similarity index 100% rename from src/main/java/com/openisle/service/CommentService.java rename to backend/src/main/java/com/openisle/service/CommentService.java diff --git a/src/main/java/com/openisle/service/CosImageUploader.java b/backend/src/main/java/com/openisle/service/CosImageUploader.java similarity index 100% rename from src/main/java/com/openisle/service/CosImageUploader.java rename to backend/src/main/java/com/openisle/service/CosImageUploader.java diff --git a/src/main/java/com/openisle/service/DiscordAuthService.java b/backend/src/main/java/com/openisle/service/DiscordAuthService.java similarity index 100% rename from src/main/java/com/openisle/service/DiscordAuthService.java rename to backend/src/main/java/com/openisle/service/DiscordAuthService.java diff --git a/src/main/java/com/openisle/service/DraftService.java b/backend/src/main/java/com/openisle/service/DraftService.java similarity index 100% rename from src/main/java/com/openisle/service/DraftService.java rename to backend/src/main/java/com/openisle/service/DraftService.java diff --git a/src/main/java/com/openisle/service/EmailSender.java b/backend/src/main/java/com/openisle/service/EmailSender.java similarity index 100% rename from src/main/java/com/openisle/service/EmailSender.java rename to backend/src/main/java/com/openisle/service/EmailSender.java diff --git a/src/main/java/com/openisle/service/GithubAuthService.java b/backend/src/main/java/com/openisle/service/GithubAuthService.java similarity index 100% rename from src/main/java/com/openisle/service/GithubAuthService.java rename to backend/src/main/java/com/openisle/service/GithubAuthService.java diff --git a/src/main/java/com/openisle/service/GoogleAuthService.java b/backend/src/main/java/com/openisle/service/GoogleAuthService.java similarity index 100% rename from src/main/java/com/openisle/service/GoogleAuthService.java rename to backend/src/main/java/com/openisle/service/GoogleAuthService.java diff --git a/src/main/java/com/openisle/service/ImageUploader.java b/backend/src/main/java/com/openisle/service/ImageUploader.java similarity index 100% rename from src/main/java/com/openisle/service/ImageUploader.java rename to backend/src/main/java/com/openisle/service/ImageUploader.java diff --git a/src/main/java/com/openisle/service/JwtService.java b/backend/src/main/java/com/openisle/service/JwtService.java similarity index 100% rename from src/main/java/com/openisle/service/JwtService.java rename to backend/src/main/java/com/openisle/service/JwtService.java diff --git a/src/main/java/com/openisle/service/LevelService.java b/backend/src/main/java/com/openisle/service/LevelService.java similarity index 100% rename from src/main/java/com/openisle/service/LevelService.java rename to backend/src/main/java/com/openisle/service/LevelService.java diff --git a/src/main/java/com/openisle/service/NotificationService.java b/backend/src/main/java/com/openisle/service/NotificationService.java similarity index 100% rename from src/main/java/com/openisle/service/NotificationService.java rename to backend/src/main/java/com/openisle/service/NotificationService.java diff --git a/src/main/java/com/openisle/service/OpenAiService.java b/backend/src/main/java/com/openisle/service/OpenAiService.java similarity index 100% rename from src/main/java/com/openisle/service/OpenAiService.java rename to backend/src/main/java/com/openisle/service/OpenAiService.java diff --git a/src/main/java/com/openisle/service/PasswordValidator.java b/backend/src/main/java/com/openisle/service/PasswordValidator.java similarity index 100% rename from src/main/java/com/openisle/service/PasswordValidator.java rename to backend/src/main/java/com/openisle/service/PasswordValidator.java diff --git a/src/main/java/com/openisle/service/PostReadService.java b/backend/src/main/java/com/openisle/service/PostReadService.java similarity index 100% rename from src/main/java/com/openisle/service/PostReadService.java rename to backend/src/main/java/com/openisle/service/PostReadService.java diff --git a/src/main/java/com/openisle/service/PostService.java b/backend/src/main/java/com/openisle/service/PostService.java similarity index 100% rename from src/main/java/com/openisle/service/PostService.java rename to backend/src/main/java/com/openisle/service/PostService.java diff --git a/src/main/java/com/openisle/service/PushNotificationService.java b/backend/src/main/java/com/openisle/service/PushNotificationService.java similarity index 100% rename from src/main/java/com/openisle/service/PushNotificationService.java rename to backend/src/main/java/com/openisle/service/PushNotificationService.java diff --git a/src/main/java/com/openisle/service/PushSubscriptionService.java b/backend/src/main/java/com/openisle/service/PushSubscriptionService.java similarity index 100% rename from src/main/java/com/openisle/service/PushSubscriptionService.java rename to backend/src/main/java/com/openisle/service/PushSubscriptionService.java diff --git a/src/main/java/com/openisle/service/ReactionService.java b/backend/src/main/java/com/openisle/service/ReactionService.java similarity index 100% rename from src/main/java/com/openisle/service/ReactionService.java rename to backend/src/main/java/com/openisle/service/ReactionService.java diff --git a/src/main/java/com/openisle/service/RecaptchaService.java b/backend/src/main/java/com/openisle/service/RecaptchaService.java similarity index 100% rename from src/main/java/com/openisle/service/RecaptchaService.java rename to backend/src/main/java/com/openisle/service/RecaptchaService.java diff --git a/src/main/java/com/openisle/service/RegisterModeService.java b/backend/src/main/java/com/openisle/service/RegisterModeService.java similarity index 100% rename from src/main/java/com/openisle/service/RegisterModeService.java rename to backend/src/main/java/com/openisle/service/RegisterModeService.java diff --git a/src/main/java/com/openisle/service/ResendEmailSender.java b/backend/src/main/java/com/openisle/service/ResendEmailSender.java similarity index 100% rename from src/main/java/com/openisle/service/ResendEmailSender.java rename to backend/src/main/java/com/openisle/service/ResendEmailSender.java diff --git a/src/main/java/com/openisle/service/SearchService.java b/backend/src/main/java/com/openisle/service/SearchService.java similarity index 100% rename from src/main/java/com/openisle/service/SearchService.java rename to backend/src/main/java/com/openisle/service/SearchService.java diff --git a/src/main/java/com/openisle/service/SubscriptionService.java b/backend/src/main/java/com/openisle/service/SubscriptionService.java similarity index 100% rename from src/main/java/com/openisle/service/SubscriptionService.java rename to backend/src/main/java/com/openisle/service/SubscriptionService.java diff --git a/src/main/java/com/openisle/service/TagService.java b/backend/src/main/java/com/openisle/service/TagService.java similarity index 100% rename from src/main/java/com/openisle/service/TagService.java rename to backend/src/main/java/com/openisle/service/TagService.java diff --git a/src/main/java/com/openisle/service/TagValidator.java b/backend/src/main/java/com/openisle/service/TagValidator.java similarity index 100% rename from src/main/java/com/openisle/service/TagValidator.java rename to backend/src/main/java/com/openisle/service/TagValidator.java diff --git a/src/main/java/com/openisle/service/TwitterAuthService.java b/backend/src/main/java/com/openisle/service/TwitterAuthService.java similarity index 100% rename from src/main/java/com/openisle/service/TwitterAuthService.java rename to backend/src/main/java/com/openisle/service/TwitterAuthService.java diff --git a/src/main/java/com/openisle/service/UserService.java b/backend/src/main/java/com/openisle/service/UserService.java similarity index 100% rename from src/main/java/com/openisle/service/UserService.java rename to backend/src/main/java/com/openisle/service/UserService.java diff --git a/src/main/java/com/openisle/service/UserVisitService.java b/backend/src/main/java/com/openisle/service/UserVisitService.java similarity index 100% rename from src/main/java/com/openisle/service/UserVisitService.java rename to backend/src/main/java/com/openisle/service/UserVisitService.java diff --git a/src/main/java/com/openisle/service/UsernameValidator.java b/backend/src/main/java/com/openisle/service/UsernameValidator.java similarity index 100% rename from src/main/java/com/openisle/service/UsernameValidator.java rename to backend/src/main/java/com/openisle/service/UsernameValidator.java diff --git a/src/main/resources/application.properties b/backend/src/main/resources/application.properties similarity index 100% rename from src/main/resources/application.properties rename to backend/src/main/resources/application.properties diff --git a/src/test/java/com/openisle/controller/AdminControllerTest.java b/backend/src/test/java/com/openisle/controller/AdminControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/AdminControllerTest.java rename to backend/src/test/java/com/openisle/controller/AdminControllerTest.java diff --git a/src/test/java/com/openisle/controller/AuthControllerTest.java b/backend/src/test/java/com/openisle/controller/AuthControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/AuthControllerTest.java rename to backend/src/test/java/com/openisle/controller/AuthControllerTest.java diff --git a/src/test/java/com/openisle/controller/CategoryControllerTest.java b/backend/src/test/java/com/openisle/controller/CategoryControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/CategoryControllerTest.java rename to backend/src/test/java/com/openisle/controller/CategoryControllerTest.java diff --git a/src/test/java/com/openisle/controller/CommentControllerTest.java b/backend/src/test/java/com/openisle/controller/CommentControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/CommentControllerTest.java rename to backend/src/test/java/com/openisle/controller/CommentControllerTest.java diff --git a/src/test/java/com/openisle/controller/HelloControllerTest.java b/backend/src/test/java/com/openisle/controller/HelloControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/HelloControllerTest.java rename to backend/src/test/java/com/openisle/controller/HelloControllerTest.java diff --git a/src/test/java/com/openisle/controller/NotificationControllerTest.java b/backend/src/test/java/com/openisle/controller/NotificationControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/NotificationControllerTest.java rename to backend/src/test/java/com/openisle/controller/NotificationControllerTest.java diff --git a/src/test/java/com/openisle/controller/PostControllerTest.java b/backend/src/test/java/com/openisle/controller/PostControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/PostControllerTest.java rename to backend/src/test/java/com/openisle/controller/PostControllerTest.java diff --git a/src/test/java/com/openisle/controller/PushSubscriptionControllerTest.java b/backend/src/test/java/com/openisle/controller/PushSubscriptionControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/PushSubscriptionControllerTest.java rename to backend/src/test/java/com/openisle/controller/PushSubscriptionControllerTest.java diff --git a/src/test/java/com/openisle/controller/ReactionControllerTest.java b/backend/src/test/java/com/openisle/controller/ReactionControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/ReactionControllerTest.java rename to backend/src/test/java/com/openisle/controller/ReactionControllerTest.java diff --git a/src/test/java/com/openisle/controller/SearchControllerTest.java b/backend/src/test/java/com/openisle/controller/SearchControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/SearchControllerTest.java rename to backend/src/test/java/com/openisle/controller/SearchControllerTest.java diff --git a/src/test/java/com/openisle/controller/StatControllerTest.java b/backend/src/test/java/com/openisle/controller/StatControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/StatControllerTest.java rename to backend/src/test/java/com/openisle/controller/StatControllerTest.java diff --git a/src/test/java/com/openisle/controller/TagControllerTest.java b/backend/src/test/java/com/openisle/controller/TagControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/TagControllerTest.java rename to backend/src/test/java/com/openisle/controller/TagControllerTest.java diff --git a/src/test/java/com/openisle/controller/UserControllerTest.java b/backend/src/test/java/com/openisle/controller/UserControllerTest.java similarity index 100% rename from src/test/java/com/openisle/controller/UserControllerTest.java rename to backend/src/test/java/com/openisle/controller/UserControllerTest.java diff --git a/src/test/java/com/openisle/integration/ComplexFlowIntegrationTest.java b/backend/src/test/java/com/openisle/integration/ComplexFlowIntegrationTest.java similarity index 100% rename from src/test/java/com/openisle/integration/ComplexFlowIntegrationTest.java rename to backend/src/test/java/com/openisle/integration/ComplexFlowIntegrationTest.java diff --git a/src/test/java/com/openisle/integration/PublishModeIntegrationTest.java b/backend/src/test/java/com/openisle/integration/PublishModeIntegrationTest.java similarity index 100% rename from src/test/java/com/openisle/integration/PublishModeIntegrationTest.java rename to backend/src/test/java/com/openisle/integration/PublishModeIntegrationTest.java diff --git a/src/test/java/com/openisle/integration/SearchIntegrationTest.java b/backend/src/test/java/com/openisle/integration/SearchIntegrationTest.java similarity index 100% rename from src/test/java/com/openisle/integration/SearchIntegrationTest.java rename to backend/src/test/java/com/openisle/integration/SearchIntegrationTest.java diff --git a/src/test/java/com/openisle/service/CommentServiceTest.java b/backend/src/test/java/com/openisle/service/CommentServiceTest.java similarity index 100% rename from src/test/java/com/openisle/service/CommentServiceTest.java rename to backend/src/test/java/com/openisle/service/CommentServiceTest.java diff --git a/src/test/java/com/openisle/service/CosImageUploaderTest.java b/backend/src/test/java/com/openisle/service/CosImageUploaderTest.java similarity index 100% rename from src/test/java/com/openisle/service/CosImageUploaderTest.java rename to backend/src/test/java/com/openisle/service/CosImageUploaderTest.java diff --git a/src/test/java/com/openisle/service/NotificationServiceTest.java b/backend/src/test/java/com/openisle/service/NotificationServiceTest.java similarity index 100% rename from src/test/java/com/openisle/service/NotificationServiceTest.java rename to backend/src/test/java/com/openisle/service/NotificationServiceTest.java diff --git a/src/test/java/com/openisle/service/PasswordValidatorTest.java b/backend/src/test/java/com/openisle/service/PasswordValidatorTest.java similarity index 100% rename from src/test/java/com/openisle/service/PasswordValidatorTest.java rename to backend/src/test/java/com/openisle/service/PasswordValidatorTest.java diff --git a/src/test/java/com/openisle/service/PostServiceTest.java b/backend/src/test/java/com/openisle/service/PostServiceTest.java similarity index 100% rename from src/test/java/com/openisle/service/PostServiceTest.java rename to backend/src/test/java/com/openisle/service/PostServiceTest.java diff --git a/src/test/java/com/openisle/service/ReactionServiceTest.java b/backend/src/test/java/com/openisle/service/ReactionServiceTest.java similarity index 100% rename from src/test/java/com/openisle/service/ReactionServiceTest.java rename to backend/src/test/java/com/openisle/service/ReactionServiceTest.java diff --git a/src/test/java/com/openisle/service/SearchServiceTest.java b/backend/src/test/java/com/openisle/service/SearchServiceTest.java similarity index 100% rename from src/test/java/com/openisle/service/SearchServiceTest.java rename to backend/src/test/java/com/openisle/service/SearchServiceTest.java diff --git a/src/test/java/com/openisle/service/UsernameValidatorTest.java b/backend/src/test/java/com/openisle/service/UsernameValidatorTest.java similarity index 100% rename from src/test/java/com/openisle/service/UsernameValidatorTest.java rename to backend/src/test/java/com/openisle/service/UsernameValidatorTest.java diff --git a/src/test/resources/application.properties b/backend/src/test/resources/application.properties similarity index 100% rename from src/test/resources/application.properties rename to backend/src/test/resources/application.properties diff --git a/open-isle-cli/babel.config.js b/frontend/babel.config.js similarity index 100% rename from open-isle-cli/babel.config.js rename to frontend/babel.config.js diff --git a/open-isle-cli/jsconfig.json b/frontend/jsconfig.json similarity index 100% rename from open-isle-cli/jsconfig.json rename to frontend/jsconfig.json diff --git a/open-isle-cli/package-lock.json b/frontend/package-lock.json similarity index 100% rename from open-isle-cli/package-lock.json rename to frontend/package-lock.json diff --git a/open-isle-cli/package.json b/frontend/package.json similarity index 100% rename from open-isle-cli/package.json rename to frontend/package.json diff --git a/open-isle-cli/public/about/about.md b/frontend/public/about/about.md similarity index 100% rename from open-isle-cli/public/about/about.md rename to frontend/public/about/about.md diff --git a/open-isle-cli/public/about/agreement.md b/frontend/public/about/agreement.md similarity index 100% rename from open-isle-cli/public/about/agreement.md rename to frontend/public/about/agreement.md diff --git a/open-isle-cli/public/about/guideline.md b/frontend/public/about/guideline.md similarity index 100% rename from open-isle-cli/public/about/guideline.md rename to frontend/public/about/guideline.md diff --git a/open-isle-cli/public/about/privacy.md b/frontend/public/about/privacy.md similarity index 100% rename from open-isle-cli/public/about/privacy.md rename to frontend/public/about/privacy.md diff --git a/open-isle-cli/public/favicon.ico b/frontend/public/favicon.ico similarity index 100% rename from open-isle-cli/public/favicon.ico rename to frontend/public/favicon.ico diff --git a/open-isle-cli/public/googlea6f18c4a543fb356.html b/frontend/public/googlea6f18c4a543fb356.html similarity index 100% rename from open-isle-cli/public/googlea6f18c4a543fb356.html rename to frontend/public/googlea6f18c4a543fb356.html diff --git a/open-isle-cli/public/index.html b/frontend/public/index.html similarity index 100% rename from open-isle-cli/public/index.html rename to frontend/public/index.html diff --git a/open-isle-cli/public/notifications-sw.js b/frontend/public/notifications-sw.js similarity index 100% rename from open-isle-cli/public/notifications-sw.js rename to frontend/public/notifications-sw.js diff --git a/open-isle-cli/public/robots.txt b/frontend/public/robots.txt similarity index 100% rename from open-isle-cli/public/robots.txt rename to frontend/public/robots.txt diff --git a/open-isle-cli/src/App.vue b/frontend/src/App.vue similarity index 100% rename from open-isle-cli/src/App.vue rename to frontend/src/App.vue diff --git a/open-isle-cli/src/assets/global.css b/frontend/src/assets/global.css similarity index 100% rename from open-isle-cli/src/assets/global.css rename to frontend/src/assets/global.css diff --git a/open-isle-cli/src/assets/icons/discord.svg b/frontend/src/assets/icons/discord.svg similarity index 100% rename from open-isle-cli/src/assets/icons/discord.svg rename to frontend/src/assets/icons/discord.svg diff --git a/open-isle-cli/src/assets/icons/github.svg b/frontend/src/assets/icons/github.svg similarity index 100% rename from open-isle-cli/src/assets/icons/github.svg rename to frontend/src/assets/icons/github.svg diff --git a/open-isle-cli/src/assets/icons/google.svg b/frontend/src/assets/icons/google.svg similarity index 100% rename from open-isle-cli/src/assets/icons/google.svg rename to frontend/src/assets/icons/google.svg diff --git a/open-isle-cli/src/assets/icons/twitter.svg b/frontend/src/assets/icons/twitter.svg similarity index 100% rename from open-isle-cli/src/assets/icons/twitter.svg rename to frontend/src/assets/icons/twitter.svg diff --git a/open-isle-cli/src/assets/toast.css b/frontend/src/assets/toast.css similarity index 100% rename from open-isle-cli/src/assets/toast.css rename to frontend/src/assets/toast.css diff --git a/open-isle-cli/src/components/ActivityPopup.vue b/frontend/src/components/ActivityPopup.vue similarity index 100% rename from open-isle-cli/src/components/ActivityPopup.vue rename to frontend/src/components/ActivityPopup.vue diff --git a/open-isle-cli/src/components/ArticleCategory.vue b/frontend/src/components/ArticleCategory.vue similarity index 100% rename from open-isle-cli/src/components/ArticleCategory.vue rename to frontend/src/components/ArticleCategory.vue diff --git a/open-isle-cli/src/components/ArticleTags.vue b/frontend/src/components/ArticleTags.vue similarity index 100% rename from open-isle-cli/src/components/ArticleTags.vue rename to frontend/src/components/ArticleTags.vue diff --git a/open-isle-cli/src/components/BaseInput.vue b/frontend/src/components/BaseInput.vue similarity index 100% rename from open-isle-cli/src/components/BaseInput.vue rename to frontend/src/components/BaseInput.vue diff --git a/open-isle-cli/src/components/BasePlaceholder.vue b/frontend/src/components/BasePlaceholder.vue similarity index 100% rename from open-isle-cli/src/components/BasePlaceholder.vue rename to frontend/src/components/BasePlaceholder.vue diff --git a/open-isle-cli/src/components/BasePopup.vue b/frontend/src/components/BasePopup.vue similarity index 100% rename from open-isle-cli/src/components/BasePopup.vue rename to frontend/src/components/BasePopup.vue diff --git a/open-isle-cli/src/components/BaseTimeline.vue b/frontend/src/components/BaseTimeline.vue similarity index 100% rename from open-isle-cli/src/components/BaseTimeline.vue rename to frontend/src/components/BaseTimeline.vue diff --git a/open-isle-cli/src/components/CategorySelect.vue b/frontend/src/components/CategorySelect.vue similarity index 100% rename from open-isle-cli/src/components/CategorySelect.vue rename to frontend/src/components/CategorySelect.vue diff --git a/open-isle-cli/src/components/CommentEditor.vue b/frontend/src/components/CommentEditor.vue similarity index 100% rename from open-isle-cli/src/components/CommentEditor.vue rename to frontend/src/components/CommentEditor.vue diff --git a/open-isle-cli/src/components/CommentItem.vue b/frontend/src/components/CommentItem.vue similarity index 100% rename from open-isle-cli/src/components/CommentItem.vue rename to frontend/src/components/CommentItem.vue diff --git a/open-isle-cli/src/components/Dropdown.vue b/frontend/src/components/Dropdown.vue similarity index 100% rename from open-isle-cli/src/components/Dropdown.vue rename to frontend/src/components/Dropdown.vue diff --git a/open-isle-cli/src/components/DropdownMenu.vue b/frontend/src/components/DropdownMenu.vue similarity index 100% rename from open-isle-cli/src/components/DropdownMenu.vue rename to frontend/src/components/DropdownMenu.vue diff --git a/open-isle-cli/src/components/GlobalPopups.vue b/frontend/src/components/GlobalPopups.vue similarity index 100% rename from open-isle-cli/src/components/GlobalPopups.vue rename to frontend/src/components/GlobalPopups.vue diff --git a/open-isle-cli/src/components/HeaderComponent.vue b/frontend/src/components/HeaderComponent.vue similarity index 100% rename from open-isle-cli/src/components/HeaderComponent.vue rename to frontend/src/components/HeaderComponent.vue diff --git a/open-isle-cli/src/components/LevelProgress.vue b/frontend/src/components/LevelProgress.vue similarity index 100% rename from open-isle-cli/src/components/LevelProgress.vue rename to frontend/src/components/LevelProgress.vue diff --git a/open-isle-cli/src/components/LoginOverlay.vue b/frontend/src/components/LoginOverlay.vue similarity index 100% rename from open-isle-cli/src/components/LoginOverlay.vue rename to frontend/src/components/LoginOverlay.vue diff --git a/open-isle-cli/src/components/MenuComponent.vue b/frontend/src/components/MenuComponent.vue similarity index 100% rename from open-isle-cli/src/components/MenuComponent.vue rename to frontend/src/components/MenuComponent.vue diff --git a/open-isle-cli/src/components/MilkTeaActivityComponent.vue b/frontend/src/components/MilkTeaActivityComponent.vue similarity index 98% rename from open-isle-cli/src/components/MilkTeaActivityComponent.vue rename to frontend/src/components/MilkTeaActivityComponent.vue index a27a9b431..b9231fcc4 100644 --- a/open-isle-cli/src/components/MilkTeaActivityComponent.vue +++ b/frontend/src/components/MilkTeaActivityComponent.vue @@ -43,8 +43,8 @@ + + + + + + + + diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 000000000..5bc639a6d --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "OpenIsle", + "short_name": "OpenIsle", + "start_url": "/", + "display": "standalone", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} From a34e213647459c525472a06752614dcfb668ffcd Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 3 Aug 2025 16:26:28 +0800 Subject: [PATCH 19/33] feat: ios PWA --- frontend/public/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/index.html b/frontend/public/index.html index d9e26e37a..dfdea33bc 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -16,9 +16,9 @@ - - - + + + From 67910317e8118e622ab77e7b69e7bacb5d5826e1 Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 3 Aug 2025 16:45:38 +0800 Subject: [PATCH 20/33] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E9=A1=B5=E9=9D=A2=E6=9C=89=E6=97=B6=E5=80=99=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=BF=87=E9=95=BF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/MessagePageView.vue | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/src/views/MessagePageView.vue b/frontend/src/views/MessagePageView.vue index 680e91954..75c5a357c 100644 --- a/frontend/src/views/MessagePageView.vue +++ b/frontend/src/views/MessagePageView.vue @@ -118,15 +118,18 @@ @@ -118,6 +112,27 @@ const CommentItem = { const toggleEditor = () => { showEditor.value = !showEditor.value } + + // 合并所有子回复为一个扁平数组 + const flattenReplies = (list) => { + let result = [] + for (const r of list) { + result.push(r) + if (r.reply && r.reply.length > 0) { + result = result.concat(flattenReplies(r.reply)) + } + } + return result + } + + const replyList = computed(() => { + if (props.level < 1) { + return props.comment.reply + } + + return flattenReplies(props.comment.reply || []) + }) + const isAuthor = computed(() => authState.username === props.comment.userName) const isAdmin = computed(() => authState.role === 'ADMIN') const commentMenuItems = computed(() => @@ -208,7 +223,7 @@ const CommentItem = { lightboxVisible.value = true } } - return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleContentClick, loggedIn, replyCount } + return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleContentClick, loggedIn, replyCount, replyList } } } @@ -249,6 +264,15 @@ export default CommentItem justify-content: space-between; } +.reply-icon { + margin-right: 10px; + margin-left: 10px; + opacity: 0.5; +} + +.reply-user-name { + opacity: 0.3; +} @keyframes highlight { from { diff --git a/frontend/src/views/PostPageView.vue b/frontend/src/views/PostPageView.vue index 01afe2316..89d6967ce 100644 --- a/frontend/src/views/PostPageView.vue +++ b/frontend/src/views/PostPageView.vue @@ -216,17 +216,18 @@ export default { } } - const mapComment = (c, level = 0) => ({ + const mapComment = (c, parentUserName = '', level = 0) => ({ id: c.id, userName: c.author.username, time: TimeManager.format(c.createdAt), avatar: c.author.avatar, text: c.content, reactions: c.reactions || [], - reply: (c.replies || []).map(r => mapComment(r, level + 1)), + reply: (c.replies || []).map(r => mapComment(r, c.author.username, level + 1)), openReplies: level === 0, src: c.author.avatar, - iconClick: () => router.push(`/users/${c.author.id}`) + iconClick: () => router.push(`/users/${c.author.id}`), + parentUserName: parentUserName }) const getTopRelativeTo = (el, container) => { From c154967564ba2f3d87ea8c017097a6634666e2c0 Mon Sep 17 00:00:00 2001 From: tim Date: Mon, 4 Aug 2025 02:06:24 +0800 Subject: [PATCH 33/33] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E5=B5=8C?= =?UTF-8?q?=E5=A5=97=E8=A7=84=E5=88=99=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/CommentItem.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/components/CommentItem.vue b/frontend/src/components/CommentItem.vue index 6c06771d9..7322b8bcd 100644 --- a/frontend/src/components/CommentItem.vue +++ b/frontend/src/components/CommentItem.vue @@ -283,4 +283,11 @@ export default CommentItem background-color: transparent; } } + +@media (max-width: 768px) { + .reply-icon { + margin-right: 3px; + margin-left: 3px; + } +}