diff --git a/build-conventions-secant/src/main/kotlin/publish/ChangelogEntry.kt b/build-conventions-secant/src/main/kotlin/publish/ChangelogEntry.kt index 11a4f555f..5599c3fe1 100644 --- a/build-conventions-secant/src/main/kotlin/publish/ChangelogEntry.kt +++ b/build-conventions-secant/src/main/kotlin/publish/ChangelogEntry.kt @@ -2,6 +2,9 @@ package publish import com.google.gson.GsonBuilder +private const val RELEASE_NOTES_MAX_LENGTH = 500 +private const val NEW_LINE = "\n" + data class ChangelogEntry( val version: String, val date: String, @@ -27,9 +30,17 @@ data class ChangelogEntry( } private fun StringBuilder.appendChangeLogSection(section: ChangelogEntrySection) { - appendLine(section.title) - appendLine(section.content) - appendLine() + appendIfCan(section.title) + appendIfCan(section.content) + appendIfCan(NEW_LINE) + } + + private fun StringBuilder.appendIfCan(content: String) { + if (length + content.length <= RELEASE_NOTES_MAX_LENGTH) { + append(content) + } else { + println("WARN: Some in-app update release notes have been skipped: $content") + } } fun toJsonString(): String = diff --git a/build-conventions-secant/src/main/kotlin/publish/ChangelogParser.kt b/build-conventions-secant/src/main/kotlin/publish/ChangelogParser.kt index 8da26accd..59e103c0e 100644 --- a/build-conventions-secant/src/main/kotlin/publish/ChangelogParser.kt +++ b/build-conventions-secant/src/main/kotlin/publish/ChangelogParser.kt @@ -10,7 +10,7 @@ import java.util.Locale object ChangelogParser { // Enable this when you need detailed parser logging. This should be turned off for production builds. - private const val DEBUG_LOGS_ENABLED = false + private const val DEBUG_LOGS_ENABLED = true private const val CHANGELOG_TITLE_POSITION = 0 private const val UNRELEASED_TITLE_POSITION = 4 @@ -23,7 +23,8 @@ object ChangelogParser { fun getChangelogEntry( filePath: String, - versionNameFallback: String + versionNameFallback: String, + keywords: LocalizedKeywords, ): ChangelogEntry { log("Parser: starting...") @@ -40,13 +41,13 @@ object ChangelogParser { // Validate content check( - nodes[CHANGELOG_TITLE_POSITION].contains("# Changelog") && - nodes[UNRELEASED_TITLE_POSITION].contains("## [Unreleased]") + nodes[CHANGELOG_TITLE_POSITION].contains("# ${keywords.changelog}") && + nodes[UNRELEASED_TITLE_POSITION].contains("## [${keywords.unreleased}]") ) { "Provided changelog file is incorrect or its structure is malformed." } - val fromIndex = findFirstValidNodeIndex(nodes) + val fromIndex = findFirstValidNodeIndex(nodes, keywords) log("Parser: index from: $fromIndex") val toIndex = @@ -65,12 +66,12 @@ object ChangelogParser { val lastChangelogEntry = nodes.subList(fromIndex = fromIndex, toIndex = toIndex).let { parts -> ChangelogEntry( - version = parts.getVersionPart(versionNameFallback), - date = parts.getDatePart(), - added = parts.getNodePart("Added"), - changed = parts.getNodePart("Changed"), - fixed = parts.getNodePart("Fixed"), - removed = parts.getNodePart("Removed"), + version = parts.getVersionPart(versionNameFallback, keywords), + date = parts.getDatePart(keywords), + added = parts.getNodePart(keywords.added), + changed = parts.getNodePart(keywords.changed), + fixed = parts.getNodePart(keywords.fixed), + removed = parts.getNodePart(keywords.removed), ) } @@ -78,9 +79,9 @@ object ChangelogParser { return lastChangelogEntry } - private fun findFirstValidNodeIndex(nodes: List): Int { + private fun findFirstValidNodeIndex(nodes: List, keywords: LocalizedKeywords): Int { nodes.forEachIndexed { index, node -> - if (findNodeByPrefix(node) && findValidSubNodeByPrefix(nodes[index + 1])) { + if (findNodeByPrefix(node) && findValidSubNodeByPrefix(nodes[index + 1], keywords)) { return index } } @@ -90,25 +91,25 @@ object ChangelogParser { private fun findNodeByPrefix(node: String): Boolean = node.startsWith("## [") - private fun findValidSubNodeByPrefix(subNode: String): Boolean = - subNode.startsWith("### Added") || - subNode.startsWith("### Changed") || - subNode.startsWith("### Fixed") || - subNode.startsWith("### Removed") + private fun findValidSubNodeByPrefix(subNode: String, keywords: LocalizedKeywords): Boolean = + subNode.startsWith("### ${keywords.added}") || + subNode.startsWith("### ${keywords.changed}") || + subNode.startsWith("### ${keywords.fixed}") || + subNode.startsWith("### ${keywords.removed}") - private fun List.getVersionPart(versionNameFallback: String): String { - return if (this.contains("## [Unreleased]")) { + private fun List.getVersionPart(versionNameFallback: String, keywords: LocalizedKeywords): String { + return if (this.contains("## [${keywords.unreleased}]")) { versionNameFallback } else { this[0].split("[")[1].split("]")[0].trim() } } - private val dateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) + private val fallbackDateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) - private fun List.getDatePart(): String { - return if (this.contains("## [Unreleased]")) { - dateFormatter.format(Date()) + private fun List.getDatePart(keywords: LocalizedKeywords): String { + return if (this.contains("## [${keywords.unreleased}]")) { + fallbackDateFormatter.format(Date()) } else { this[0].split("- ")[1].trim() } @@ -134,9 +135,19 @@ object ChangelogParser { } } return subList(startIndex, endIndex) - .joinToString(prefix = "\n", separator = "\n") + .map { it.replace("\n- ", "\n• ") } + .joinToString(separator = "\n") .takeIf { it.isNotBlank() }?.let { ChangelogEntrySection(title = title, content = it) } } + + data class LocalizedKeywords( + val changelog: String, + val unreleased: String, + val added: String, + val changed: String, + val fixed: String, + val removed: String, + ) } diff --git a/build-conventions-secant/src/main/kotlin/publish/secant.publish-conventions.gradle.kts b/build-conventions-secant/src/main/kotlin/publish/secant.publish-conventions.gradle.kts index 48758dadc..dad8ee15c 100644 --- a/build-conventions-secant/src/main/kotlin/publish/secant.publish-conventions.gradle.kts +++ b/build-conventions-secant/src/main/kotlin/publish/secant.publish-conventions.gradle.kts @@ -246,7 +246,15 @@ abstract class PublishToGooglePlay @Inject constructor( language = Locale.ENGLISH.toLanguageTag() text = ChangelogParser.getChangelogEntry( filePath = "docs/whatsNew/WHATS_NEW_EN.md", - versionNameFallback = gradleVersionName + versionNameFallback = gradleVersionName, + keywords = ChangelogParser.LocalizedKeywords( + changelog = "Changelog", + unreleased = "Unreleased", + added = "Added", + changed = "Changed", + fixed = "Fixed", + removed = "Removed", + ) ).toInAppUpdateReleaseNotesText() } val releaseNotes: MutableList = arrayListOf(localizedText) diff --git a/build-info-lib/build.gradle.kts b/build-info-lib/build.gradle.kts index ee8f646eb..4ed7536e7 100644 --- a/build-info-lib/build.gradle.kts +++ b/build-info-lib/build.gradle.kts @@ -26,7 +26,15 @@ val generateBuildConfigTask = tasks.create("buildConfig") { val releaseNotesJson = ChangelogParser.getChangelogEntry( filePath = "docs/whatsNew/WHATS_NEW_EN.md", - versionNameFallback = gradleVersionName + versionNameFallback = gradleVersionName, + keywords = ChangelogParser.LocalizedKeywords( + changelog = "Changelog", + unreleased = "Unreleased", + added = "Added", + changed = "Changed", + fixed = "Fixed", + removed = "Removed", + ) ).toJsonString() inputs.property("gitSha", gitInfo.sha) diff --git a/docs/whatsNew/WHATS_NEW_EN.md b/docs/whatsNew/WHATS_NEW_EN.md index 69bda7ea4..a1504204d 100644 --- a/docs/whatsNew/WHATS_NEW_EN.md +++ b/docs/whatsNew/WHATS_NEW_EN.md @@ -20,3 +20,6 @@ directly impact users rather than highlighting other key architectural updates.* ### Fixed - Support Screen now shows the Send button above keyboard instead of overlaying it - QR code scanning speed and reliability have been improved to address the latest reported scan issue + +### Removed +- Test - test - test fine! diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/whatsnew/view/WhatsNewView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/whatsnew/view/WhatsNewView.kt index 3646168ba..ea21603dd 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/whatsnew/view/WhatsNewView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/whatsnew/view/WhatsNewView.kt @@ -8,19 +8,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.ParagraphStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextIndent -import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.sp import co.electriccoin.zcash.ui.R @@ -50,15 +42,15 @@ fun WhatsNewView( ) { paddingValues -> Column( modifier = - Modifier - .fillMaxSize() - .padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault, - start = ZcashTheme.dimens.screenHorizontalSpacingRegular, - end = ZcashTheme.dimens.screenHorizontalSpacingRegular - ) - .verticalScroll(rememberScrollState()) + Modifier + .fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault, + start = ZcashTheme.dimens.screenHorizontalSpacingRegular, + end = ZcashTheme.dimens.screenHorizontalSpacingRegular + ) + .verticalScroll(rememberScrollState()) ) { Row { Text( @@ -87,43 +79,17 @@ fun WhatsNewView( @Composable private fun WhatsNewSection(state: WhatsNewSectionState) { - val bulletString = "\u2022\t\t" - val bulletTextStyle = MaterialTheme.typography.bodySmall - val bulletTextMeasurer = rememberTextMeasurer() - val bulletStringWidth = - remember(bulletTextStyle, bulletTextMeasurer) { - bulletTextMeasurer.measure(text = bulletString, style = bulletTextStyle).size.width - } - val bulletRestLine = with(LocalDensity.current) { bulletStringWidth.toSp() } - val bulletParagraphStyle = ParagraphStyle(textIndent = TextIndent(restLine = bulletRestLine)) - val bulletStyle = - state.content.getValue().split("\n-") - .filter { it.isNotBlank() } - .map { - it.replace("\n-", "").trim() - } - .let { text -> - buildAnnotatedString { - text.forEach { - withStyle(style = bulletParagraphStyle) { - append(bulletString) - append(it) - } - } - } - } - Column { Text( - text = state.title.getValue(), + text = stringResource(id = R.string.whats_new_entry_title, state.title.getValue()), style = ZcashTheme.typography.primary.titleSmall, ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingMin)) Text( - text = bulletStyle, - style = bulletTextStyle + text = state.content.getValue(), + style = ZcashTheme.typography.primary.bodySmall ) } } diff --git a/ui-lib/src/main/res/ui/whats_new/values/strings.xml b/ui-lib/src/main/res/ui/whats_new/values/strings.xml index 06fab3054..9c988be14 100644 --- a/ui-lib/src/main/res/ui/whats_new/values/strings.xml +++ b/ui-lib/src/main/res/ui/whats_new/values/strings.xml @@ -2,4 +2,5 @@ What\'s new Zashi Version %s + %1$s: