show story progress and reset last read when resetting progress.
This commit is contained in:
@@ -595,6 +595,7 @@ public class StoryController {
|
|||||||
// Reading progress fields
|
// Reading progress fields
|
||||||
dto.setIsRead(story.getIsRead());
|
dto.setIsRead(story.getIsRead());
|
||||||
dto.setReadingPosition(story.getReadingPosition());
|
dto.setReadingPosition(story.getReadingPosition());
|
||||||
|
dto.setReadingProgressPercentage(calculateReadingProgressPercentage(story));
|
||||||
dto.setLastReadAt(story.getLastReadAt());
|
dto.setLastReadAt(story.getLastReadAt());
|
||||||
|
|
||||||
if (story.getAuthor() != null) {
|
if (story.getAuthor() != null) {
|
||||||
@@ -614,6 +615,27 @@ public class StoryController {
|
|||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer calculateReadingProgressPercentage(Story story) {
|
||||||
|
if (story.getReadingPosition() == null || story.getReadingPosition() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the total content length
|
||||||
|
int totalLength = 0;
|
||||||
|
if (story.getContentPlain() != null && !story.getContentPlain().isEmpty()) {
|
||||||
|
totalLength = story.getContentPlain().length();
|
||||||
|
} else if (story.getContentHtml() != null && !story.getContentHtml().isEmpty()) {
|
||||||
|
totalLength = story.getContentHtml().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalLength == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate percentage and round to nearest integer
|
||||||
|
return Math.round((float) story.getReadingPosition() * 100 / totalLength);
|
||||||
|
}
|
||||||
|
|
||||||
private StoryReadingDto convertToReadingDto(Story story) {
|
private StoryReadingDto convertToReadingDto(Story story) {
|
||||||
StoryReadingDto dto = new StoryReadingDto();
|
StoryReadingDto dto = new StoryReadingDto();
|
||||||
dto.setId(story.getId());
|
dto.setId(story.getId());
|
||||||
@@ -632,6 +654,7 @@ public class StoryController {
|
|||||||
// Reading progress fields
|
// Reading progress fields
|
||||||
dto.setIsRead(story.getIsRead());
|
dto.setIsRead(story.getIsRead());
|
||||||
dto.setReadingPosition(story.getReadingPosition());
|
dto.setReadingPosition(story.getReadingPosition());
|
||||||
|
dto.setReadingProgressPercentage(calculateReadingProgressPercentage(story));
|
||||||
dto.setLastReadAt(story.getLastReadAt());
|
dto.setLastReadAt(story.getLastReadAt());
|
||||||
|
|
||||||
if (story.getAuthor() != null) {
|
if (story.getAuthor() != null) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class StoryDto {
|
|||||||
// Reading progress fields
|
// Reading progress fields
|
||||||
private Boolean isRead;
|
private Boolean isRead;
|
||||||
private Integer readingPosition;
|
private Integer readingPosition;
|
||||||
|
private Integer readingProgressPercentage; // Pre-calculated percentage (0-100)
|
||||||
private LocalDateTime lastReadAt;
|
private LocalDateTime lastReadAt;
|
||||||
|
|
||||||
// Related entities as simple references
|
// Related entities as simple references
|
||||||
@@ -147,6 +148,14 @@ public class StoryDto {
|
|||||||
this.readingPosition = readingPosition;
|
this.readingPosition = readingPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getReadingProgressPercentage() {
|
||||||
|
return readingProgressPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadingProgressPercentage(Integer readingProgressPercentage) {
|
||||||
|
this.readingProgressPercentage = readingProgressPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDateTime getLastReadAt() {
|
public LocalDateTime getLastReadAt() {
|
||||||
return lastReadAt;
|
return lastReadAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class StoryReadingDto {
|
|||||||
// Reading progress fields
|
// Reading progress fields
|
||||||
private Boolean isRead;
|
private Boolean isRead;
|
||||||
private Integer readingPosition;
|
private Integer readingPosition;
|
||||||
|
private Integer readingProgressPercentage; // Pre-calculated percentage (0-100)
|
||||||
private LocalDateTime lastReadAt;
|
private LocalDateTime lastReadAt;
|
||||||
|
|
||||||
// Related entities as simple references
|
// Related entities as simple references
|
||||||
@@ -136,6 +137,14 @@ public class StoryReadingDto {
|
|||||||
this.readingPosition = readingPosition;
|
this.readingPosition = readingPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getReadingProgressPercentage() {
|
||||||
|
return readingProgressPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadingProgressPercentage(Integer readingProgressPercentage) {
|
||||||
|
this.readingProgressPercentage = readingProgressPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDateTime getLastReadAt() {
|
public LocalDateTime getLastReadAt() {
|
||||||
return lastReadAt;
|
return lastReadAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,10 +287,17 @@ public class Story {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the reading progress and timestamp
|
* Updates the reading progress and timestamp
|
||||||
|
* When position is 0 or null, resets lastReadAt to null so the story won't appear in "last read" sorting
|
||||||
*/
|
*/
|
||||||
public void updateReadingProgress(Integer position) {
|
public void updateReadingProgress(Integer position) {
|
||||||
this.readingPosition = position;
|
this.readingPosition = position;
|
||||||
this.lastReadAt = LocalDateTime.now();
|
// Only update lastReadAt if there's actual reading progress
|
||||||
|
// Reset to null when position is 0 or null to remove from "last read" sorting
|
||||||
|
if (position == null || position == 0) {
|
||||||
|
this.lastReadAt = null;
|
||||||
|
} else {
|
||||||
|
this.lastReadAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ class StoryServiceTest {
|
|||||||
Story result = storyService.updateReadingProgress(testId, position);
|
Story result = storyService.updateReadingProgress(testId, position);
|
||||||
|
|
||||||
assertEquals(0, result.getReadingPosition());
|
assertEquals(0, result.getReadingPosition());
|
||||||
assertNotNull(result.getLastReadAt());
|
// When position is 0, lastReadAt should be reset to null so the story doesn't appear in "last read" sorting
|
||||||
|
assertNull(result.getLastReadAt());
|
||||||
verify(storyRepository).save(testStory);
|
verify(storyRepository).save(testStory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +112,8 @@ class StoryServiceTest {
|
|||||||
Story result = storyService.updateReadingProgress(testId, position);
|
Story result = storyService.updateReadingProgress(testId, position);
|
||||||
|
|
||||||
assertNull(result.getReadingPosition());
|
assertNull(result.getReadingPosition());
|
||||||
assertNotNull(result.getLastReadAt());
|
// When position is null, lastReadAt should be reset to null so the story doesn't appear in "last read" sorting
|
||||||
|
assertNull(result.getLastReadAt());
|
||||||
verify(storyRepository).save(testStory);
|
verify(storyRepository).save(testStory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,16 +72,8 @@ export default function StoryCard({
|
|||||||
return new Date(dateString).toLocaleDateString();
|
return new Date(dateString).toLocaleDateString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateReadingPercentage = (story: Story): number => {
|
// Use the pre-calculated percentage from the backend
|
||||||
if (!story.readingPosition) return 0;
|
const readingPercentage = story.readingProgressPercentage || 0;
|
||||||
|
|
||||||
const totalLength = story.contentPlain?.length || story.contentHtml?.length || 0;
|
|
||||||
if (totalLength === 0) return 0;
|
|
||||||
|
|
||||||
return Math.round((story.readingPosition / totalLength) * 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
const readingPercentage = calculateReadingPercentage(story);
|
|
||||||
|
|
||||||
if (viewMode === 'list') {
|
if (viewMode === 'list') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface Story {
|
|||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
tagNames?: string[] | null; // Used in search results
|
tagNames?: string[] | null; // Used in search results
|
||||||
readingPosition?: number;
|
readingPosition?: number;
|
||||||
|
readingProgressPercentage?: number; // Pre-calculated percentage (0-100) from backend
|
||||||
lastReadAt?: string;
|
lastReadAt?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user