Skip to content

Add Manga Notes #428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3b2a747
Initial markdown render test
imkunet Feb 18, 2024
c58984b
Add the text editor
imkunet Feb 18, 2024
7a3bde6
Polishing off note editor
imkunet Feb 19, 2024
cd77546
Added tablet notes
imkunet Feb 19, 2024
1493264
Make the edit notes button edit directly
imkunet Feb 19, 2024
009a449
Added fade transition between states
imkunet Feb 19, 2024
a2d43d2
Make cursor jump to end by default
imkunet Feb 19, 2024
940ba9c
Fixed padding
imkunet Feb 19, 2024
9a6f651
Impose note editor length limit
imkunet Feb 20, 2024
22860e5
Fixes to pass relevant testing
imkunet Mar 5, 2024
68adf9b
Add migration to add notes to manga
imkunet Sep 19, 2024
54f85fc
Make spotless
imkunet Sep 19, 2024
44a8cdb
Design revision
imkunet Sep 19, 2024
95384f3
Design revision 2
imkunet Sep 19, 2024
2022822
Spotless
imkunet Sep 19, 2024
6bf4b01
Abandon markdown for rich text
imkunet Sep 20, 2024
4fbc585
Rename composable from example name
imkunet Sep 20, 2024
cca8d45
Reduce char limit, switch encoding
imkunet Sep 20, 2024
51c0ee9
Bump richeditor and fix character count
imkunet Jan 19, 2025
6c7e2a2
Edit strings
imkunet Jan 19, 2025
8525d4e
Resolve strange initialization pattern
imkunet Jan 27, 2025
fdae3a8
Removed note nullity, tidied up model
imkunet Jan 27, 2025
062508f
Changed strings to review recommendation
imkunet Jan 27, 2025
1cc0266
Removed unnecessary code modification
imkunet Jan 27, 2025
7c8cdf7
Animate out all bumps caused by updating
imkunet Jan 27, 2025
f947771
Fix characters remaining indicator
imkunet Jan 27, 2025
c531b58
Update domain/src/main/java/tachiyomi/domain/manga/interactor/SetMang…
imkunet Jan 30, 2025
fa36a68
Update app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
imkunet Jan 30, 2025
fede574
Spotless
imkunet Jan 30, 2025
df81029
Fix awaitSetNotes parameter
imkunet Jan 30, 2025
824f073
Put state and model into the screen
imkunet Jan 30, 2025
6e3efb8
Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNote…
imkunet Jan 30, 2025
656a48d
Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNote…
imkunet Jan 30, 2025
b334f21
Cleanup MangaNotesDisplay
imkunet Jan 30, 2025
1230f64
Idiomatic padding values on MangaNotesScreen
imkunet Feb 7, 2025
3f9accf
More explicit function parameter names
imkunet Feb 7, 2025
61b7c62
Manga notes in CHANGELOG
imkunet Feb 7, 2025
c74a858
Even more consistency in names
imkunet Feb 7, 2025
cb4d073
Change names to suggestion
imkunet Feb 13, 2025
c1b56d3
Refactor to include the names
imkunet Feb 13, 2025
ae6a028
Use lambda reference
imkunet Feb 14, 2025
09a27fd
Switch to collapsible top bar
imkunet Feb 18, 2025
66eb321
Restore title subtitle AppBar
imkunet Feb 19, 2025
f217b21
Migrate notes
imkunet Feb 19, 2025
5cb5d4f
Don't remove old notes on "replace" in library
imkunet Feb 25, 2025
88c5bb9
Not sure if this is a more sensible order but sure
imkunet Feb 26, 2025
1d0f022
Merge branch 'main' into pr/428
AntsyLich Mar 29, 2025
7672514
Cleanup
AntsyLich Mar 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
## [Unreleased]
### Added
- Add more Kaomoji for empty/error screens ([@ianfhunter](https://github.com/ianfhunter/)) ([#1909](https://github.com/mihonapp/mihon/pull/1909))
- Add user manga notes ([@imkunet](https://github.com/imkunet), [@AntsyLich](https://github.com/AntsyLich)) ([#428](https://github.com/mihonapp/mihon/pull/428))

### Fixes
- Fix Bangumi search results including novels ([@MajorTanya](https://github.com/MajorTanya)) ([#1885](https://github.com/mihonapp/mihon/pull/1885))
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ dependencies {
}
implementation(libs.insetter)
implementation(libs.bundles.richtext)
implementation(libs.richeditor.compose)
implementation(libs.aboutLibraries.compose)
implementation(libs.bundles.voyager)
implementation(libs.compose.materialmotion)
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/eu/kanade/domain/DomainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import tachiyomi.domain.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.domain.release.interactor.GetApplicationRelease
import tachiyomi.domain.release.service.ReleaseService
Expand Down Expand Up @@ -129,6 +130,7 @@ class DomainModule : InjektModule {
addFactory { SetMangaViewerFlags(get()) }
addFactory { NetworkToLocalManga(get()) }
addFactory { UpdateManga(get(), get()) }
addFactory { UpdateMangaNotes(get()) }
addFactory { SetMangaCategories(get()) }
addFactory { GetExcludedScanlators(get()) }
addFactory { SetExcludedScanlators(get()) }
Expand Down
45 changes: 45 additions & 0 deletions app/src/main/java/eu/kanade/presentation/manga/MangaNotesScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package eu.kanade.presentation.manga

import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarTitle
import eu.kanade.presentation.manga.components.MangaNotesTextArea
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource

@Composable
fun MangaNotesScreen(
state: MangaNotesScreen.State,
navigateUp: () -> Unit,
onUpdate: (String) -> Unit,
) {
Scaffold(
topBar = { topBarScrollBehavior ->
AppBar(
titleContent = {
AppBarTitle(
title = stringResource(MR.strings.action_edit_notes),
subtitle = state.manga.title,
)
},
navigateUp = navigateUp,
scrollBehavior = topBarScrollBehavior,
)
},
) { contentPadding ->
MangaNotesTextArea(
state = state,
onUpdate = onUpdate,
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding)
.imePadding(),
)
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ fun MangaScreen(
onEditCategoryClicked: (() -> Unit)?,
onEditFetchIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,

// For bottom action menu
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
Expand Down Expand Up @@ -160,6 +161,7 @@ fun MangaScreen(
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked,
onEditNotesClicked = onEditNotesClicked,
onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
Expand Down Expand Up @@ -195,6 +197,7 @@ fun MangaScreen(
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked,
onEditNotesClicked = onEditNotesClicked,
onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
Expand Down Expand Up @@ -240,6 +243,7 @@ private fun MangaScreenSmallImpl(
onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,

// For bottom action menu
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
Expand Down Expand Up @@ -302,6 +306,7 @@ private fun MangaScreenSmallImpl(
onClickEditCategory = onEditCategoryClicked,
onClickRefresh = onRefresh,
onClickMigrate = onMigrateClicked,
onClickEditNotes = onEditNotesClicked,
actionModeCounter = selectedChapterCount,
onCancelActionMode = { onAllChapterSelected(false) },
onSelectAll = { onAllChapterSelected(true) },
Expand Down Expand Up @@ -414,8 +419,10 @@ private fun MangaScreenSmallImpl(
defaultExpandState = state.isFromSource,
description = state.manga.description,
tagsProvider = { state.manga.genre },
notes = state.manga.notes,
onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
onEditNotes = onEditNotesClicked,
)
}

Expand Down Expand Up @@ -484,6 +491,7 @@ fun MangaScreenLargeImpl(
onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,

// For bottom action menu
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
Expand Down Expand Up @@ -539,6 +547,7 @@ fun MangaScreenLargeImpl(
onClickEditCategory = onEditCategoryClicked,
onClickRefresh = onRefresh,
onClickMigrate = onMigrateClicked,
onClickEditNotes = onEditNotesClicked,
onCancelActionMode = { onAllChapterSelected(false) },
actionModeCounter = selectedChapterCount,
onSelectAll = { onAllChapterSelected(true) },
Expand Down Expand Up @@ -640,8 +649,10 @@ fun MangaScreenLargeImpl(
defaultExpandState = true,
description = state.manga.description,
tagsProvider = { state.manga.genre },
notes = state.manga.notes,
onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
onEditNotes = onEditNotesClicked,
)
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@ fun ExpandableMangaDescription(
defaultExpandState: Boolean,
description: String?,
tagsProvider: () -> List<String>?,
notes: String,
onTagSearch: (String) -> Unit,
onCopyTagToClipboard: (tag: String) -> Unit,
onEditNotes: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
Expand All @@ -255,6 +257,8 @@ fun ExpandableMangaDescription(
expandedDescription = desc,
shrunkDescription = trimmedDescription,
expanded = expanded,
notes = notes,
onEditNotesClicked = onEditNotes,
modifier = Modifier
.padding(top = 8.dp)
.padding(horizontal = 16.dp)
Expand Down Expand Up @@ -559,7 +563,9 @@ private fun ColumnScope.MangaContentInfo(
private fun MangaSummary(
expandedDescription: String,
shrunkDescription: String,
notes: String,
expanded: Boolean,
onEditNotesClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
val animProgress by animateFloatAsState(
Expand All @@ -571,27 +577,43 @@ private fun MangaSummary(
contents = listOf(
{
Text(
text = "\n\n", // Shows at least 3 lines
// Shows at least 3 lines if no notes
// when there are notes show 6
text = if (notes.isBlank()) "\n\n" else "\n\n\n\n\n",
style = MaterialTheme.typography.bodyMedium,
)
},
{
Text(
text = expandedDescription,
style = MaterialTheme.typography.bodyMedium,
)
},
{
SelectionContainer {
Column {
MangaNotesSection(
content = notes,
expanded = true,
onEditNotes = onEditNotesClicked,
)
Text(
text = if (expanded) expandedDescription else shrunkDescription,
maxLines = Int.MAX_VALUE,
text = expandedDescription,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.secondaryItemAlpha(),
)
}
},
{
Column {
MangaNotesSection(
content = notes,
expanded = expanded,
onEditNotes = onEditNotesClicked,
)
SelectionContainer {
Text(
text = if (expanded) expandedDescription else shrunkDescription,
maxLines = Int.MAX_VALUE,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.secondaryItemAlpha(),
)
}
}
},
{
val colors = listOf(Color.Transparent, MaterialTheme.colorScheme.background)
Box(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package eu.kanade.presentation.manga.components

import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import com.mohamedrejeb.richeditor.model.rememberRichTextState
import com.mohamedrejeb.richeditor.ui.material3.RichText

private val FADE_TIME = tween<Float>(500)

@Composable
fun MangaNotesDisplay(
content: String,
modifier: Modifier,
) {
val alpha = remember { Animatable(1f) }
var contentUpdatedOnce by remember { mutableStateOf(false) }

val richTextState = rememberRichTextState()
val primaryColor = MaterialTheme.colorScheme.primary
LaunchedEffect(content) {
richTextState.setMarkdown(content)

if (!contentUpdatedOnce) {
contentUpdatedOnce = true
return@LaunchedEffect
}

alpha.snapTo(targetValue = 0f)
alpha.animateTo(targetValue = 1f, animationSpec = FADE_TIME)
}
LaunchedEffect(Unit) {
richTextState.config.unorderedListIndent = 4
richTextState.config.orderedListIndent = 20
}
LaunchedEffect(primaryColor) {
richTextState.config.linkColor = primaryColor
}

SelectionContainer {
RichText(
modifier = modifier
// Only animate size if the notes changes
.then(if (contentUpdatedOnce) Modifier.animateContentSize() else Modifier)
.alpha(alpha.value),
style = MaterialTheme.typography.bodyMedium,
state = richTextState,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package eu.kanade.presentation.manga.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EditNote
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.ButtonDefaults
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource

@Composable
fun MangaNotesSection(
content: String,
expanded: Boolean,
onEditNotes: () -> Unit,
modifier: Modifier = Modifier,
) {
if (content.isBlank()) return
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
MangaNotesDisplay(
content = content,
modifier = modifier.fillMaxWidth(),
)
if (expanded) {
Button(
onClick = onEditNotes,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.primary,
),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 4.dp),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = Icons.Filled.EditNote,
contentDescription = null,
modifier = Modifier
.size(16.dp),
)
Text(
stringResource(MR.strings.action_edit_notes),
)
}
}
}

HorizontalDivider(
modifier = Modifier
.padding(
top = if (expanded) 0.dp else 12.dp,
bottom = if (expanded) 16.dp else 12.dp,
),
)
}
}

@PreviewLightDark
@Composable
private fun MangaNotesSectionPreview() {
MangaNotesSection(
onEditNotes = {},
expanded = true,
content = "# Hello world\ntest1234 hi there!",
)
}
Loading