show reading progress in author page. Allow deletion of tags, even if assigned to story.

This commit is contained in:
Stefan Hardegger
2025-10-31 09:54:04 +01:00
parent 7a4dd567dc
commit 75768855e2
5 changed files with 61 additions and 17 deletions

View File

@@ -691,8 +691,9 @@ public class StoryController {
// Reading progress fields
dto.setIsRead(story.getIsRead());
dto.setReadingPosition(story.getReadingPosition());
dto.setReadingProgressPercentage(calculateReadingProgressPercentage(story));
dto.setLastReadAt(story.getLastReadAt());
if (story.getAuthor() != null) {
dto.setAuthorId(story.getAuthor().getId());
dto.setAuthorName(story.getAuthor().getName());

View File

@@ -23,6 +23,7 @@ public class StorySummaryDto {
// Reading progress fields
private Boolean isRead;
private Integer readingPosition;
private Integer readingProgressPercentage; // Pre-calculated percentage (0-100)
private LocalDateTime lastReadAt;
// Related entities as simple references
@@ -122,11 +123,19 @@ public class StorySummaryDto {
public Integer getReadingPosition() {
return readingPosition;
}
public void setReadingPosition(Integer readingPosition) {
this.readingPosition = readingPosition;
}
public Integer getReadingProgressPercentage() {
return readingProgressPercentage;
}
public void setReadingProgressPercentage(Integer readingProgressPercentage) {
this.readingProgressPercentage = readingProgressPercentage;
}
public LocalDateTime getLastReadAt() {
return lastReadAt;
}

View File

@@ -28,11 +28,12 @@ import java.util.UUID;
@Validated
@Transactional
public class TagService {
private static final Logger logger = LoggerFactory.getLogger(TagService.class);
private final TagRepository tagRepository;
private final TagAliasRepository tagAliasRepository;
private SolrService solrService;
@Autowired
public TagService(TagRepository tagRepository, TagAliasRepository tagAliasRepository) {
@@ -40,6 +41,11 @@ public class TagService {
this.tagAliasRepository = tagAliasRepository;
}
@Autowired(required = false)
public void setSolrService(SolrService solrService) {
this.solrService = solrService;
}
@Transactional(readOnly = true)
public List<Tag> findAll() {
return tagRepository.findAll();
@@ -142,13 +148,39 @@ public class TagService {
public void delete(UUID id) {
Tag tag = findById(id);
// Check if tag is used by any stories
// Remove tag from all stories before deletion and track for reindexing
List<Story> storiesToReindex = new ArrayList<>();
if (!tag.getStories().isEmpty()) {
throw new IllegalStateException("Cannot delete tag that is used by stories. Remove tag from all stories first.");
// Create a copy to avoid ConcurrentModificationException
List<Story> storiesToUpdate = new ArrayList<>(tag.getStories());
storiesToUpdate.forEach(story -> {
story.removeTag(tag);
storiesToReindex.add(story);
});
logger.info("Removed tag '{}' from {} stories before deletion", tag.getName(), storiesToUpdate.size());
}
// Remove tag from all collections before deletion
if (tag.getCollections() != null && !tag.getCollections().isEmpty()) {
tag.getCollections().forEach(collection -> collection.getTags().remove(tag));
logger.info("Removed tag '{}' from {} collections before deletion", tag.getName(), tag.getCollections().size());
}
tagRepository.delete(tag);
logger.info("Deleted tag '{}'", tag.getName());
// Reindex affected stories in Solr
if (solrService != null && !storiesToReindex.isEmpty()) {
try {
for (Story story : storiesToReindex) {
solrService.indexStory(story);
}
logger.info("Reindexed {} stories after tag deletion", storiesToReindex.size());
} catch (Exception e) {
logger.error("Failed to reindex stories after tag deletion", e);
}
}
}
public List<Tag> deleteUnusedTags() {