diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index 45a963b05..d2ca49a89 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -114,6 +114,15 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } + val isAndroid = gradle.startParameter.taskNames.find { + val lower = it.toLowerCase() + lower.contains("release") || lower.startsWith("assemble") || lower.startsWith("install") + } != null + if (isAndroid) { + // This is not needed on Android but can't be moved to desktopMain because MR lib don't support this. + // No other ways to exclude a file work but it's large and should be excluded + kotlin.sourceSets["commonMain"].resources.exclude("/MR/fonts/NotoColorEmoji-Regular.ttf") + } } multiplatformResources { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Type.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Type.android.kt index 056ae7495..2e7404f24 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Type.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Type.android.kt @@ -11,3 +11,5 @@ actual val Inter: FontFamily = FontFamily( Font(MR.fonts.Inter.medium.fontResourceId, FontWeight.Medium), Font(MR.fonts.Inter.light.fontResourceId, FontWeight.Light) ) + +actual val EmojiFont: FontFamily = FontFamily.Default diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.android.kt index c05672864..15421299a 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.android.kt @@ -2,18 +2,27 @@ package chat.simplex.common.views.chat.item import android.Manifest import android.os.Build +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.sp import chat.simplex.common.model.ChatItem import chat.simplex.common.model.MsgContent import chat.simplex.common.platform.FileChooserLauncher import chat.simplex.common.platform.saveImage +import chat.simplex.common.views.helpers.SharedContent import chat.simplex.common.views.helpers.withApi import chat.simplex.res.MR import com.google.accompanist.permissions.rememberPermissionState import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +@Composable +actual fun ReactionIcon(text: String, fontSize: TextUnit) { + Text(text, fontSize = fontSize) +} + @Composable actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserLauncher, showMenu: MutableState) { val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.android.kt new file mode 100644 index 000000000..f0e8c1295 --- /dev/null +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.android.kt @@ -0,0 +1,10 @@ +package chat.simplex.common.views.chat.item + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable + +@Composable +actual fun EmojiText(text: String) { + val s = text.trim() + Text(s, style = if (s.codePoints().count() < 4) largeEmojiFont else mediumEmojiFont) +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt index 17ff695e7..4a7521efb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt @@ -12,6 +12,7 @@ import chat.simplex.common.views.helpers.generalGetString // https://github.com/rsms/inter // I place it here because IDEA shows an error (but still works anyway) when this declaration inside Type.kt expect val Inter: FontFamily +expect val EmojiFont: FontFamily object ThemeManager { private val appPrefs: AppPreferences = ChatController.appPrefs diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt index 8e2f13329..0597e2d13 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt @@ -17,8 +17,7 @@ import androidx.compose.ui.text.AnnotatedString import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* @@ -84,7 +83,7 @@ fun ChatItemView( @Composable fun ChatItemReactions() { - Row { + Row(verticalAlignment = Alignment.CenterVertically) { cItem.reactions.forEach { r -> var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp)) if (cInfo.featureEnabled(ChatFeature.Reactions) && (cItem.allowAddReaction || r.userReacted)) { @@ -93,13 +92,14 @@ fun ChatItemView( } } Row(modifier.padding(2.dp)) { - Text(r.reaction.text, fontSize = 12.sp) + ReactionIcon(r.reaction.text, fontSize = 12.sp) if (r.totalReacted > 1) { Spacer(Modifier.width(4.dp)) Text("${r.totalReacted}", fontSize = 11.5.sp, fontWeight = if (r.userReacted) FontWeight.Bold else FontWeight.Normal, color = if (r.userReacted) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, + modifier = if (appPlatform.isAndroid) Modifier else Modifier.padding(top = 4.dp) ) } } @@ -145,7 +145,7 @@ fun ChatItemView( } } if (rs.isNotEmpty()) { - Row(modifier = Modifier.padding(horizontal = DEFAULT_PADDING).horizontalScroll(rememberScrollState())) { + Row(modifier = Modifier.padding(horizontal = DEFAULT_PADDING).horizontalScroll(rememberScrollState()), verticalAlignment = Alignment.CenterVertically) { rs.forEach() { r -> Box( Modifier.size(36.dp).clickable { @@ -154,7 +154,7 @@ fun ChatItemView( }, contentAlignment = Alignment.Center ) { - Text(r.text) + ReactionIcon(r.text, 12.sp) } } } @@ -324,6 +324,9 @@ fun ChatItemView( } } +@Composable +expect fun ReactionIcon(text: String, fontSize: TextUnit = TextUnit.Unspecified) + @Composable expect fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserLauncher, showMenu: MutableState) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.kt index d99de4019..3ede737ff 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.kt @@ -10,9 +10,11 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.common.model.ChatItem +import chat.simplex.common.model.MREmojiChar +import chat.simplex.common.ui.theme.EmojiFont -val largeEmojiFont: TextStyle = TextStyle(fontSize = 48.sp) -val mediumEmojiFont: TextStyle = TextStyle(fontSize = 36.sp) +val largeEmojiFont: TextStyle = TextStyle(fontSize = 48.sp, fontFamily = EmojiFont) +val mediumEmojiFont: TextStyle = TextStyle(fontSize = 36.sp, fontFamily = EmojiFont) @Composable fun EmojiItemView(chatItem: ChatItem, timedMessagesTTL: Int?) { @@ -26,10 +28,7 @@ fun EmojiItemView(chatItem: ChatItem, timedMessagesTTL: Int?) { } @Composable -fun EmojiText(text: String) { - val s = text.trim() - Text(s, style = if (s.codePoints().count() < 4) largeEmojiFont else mediumEmojiFont) -} +expect fun EmojiText(text: String) // https://stackoverflow.com/a/46279500 private const val emojiStr = "^(" + diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fonts/NotoColorEmoji-Regular.ttf b/apps/multiplatform/common/src/commonMain/resources/MR/fonts/NotoColorEmoji-Regular.ttf new file mode 100644 index 000000000..42799e84e Binary files /dev/null and b/apps/multiplatform/common/src/commonMain/resources/MR/fonts/NotoColorEmoji-Regular.ttf differ diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_heart@4x.png b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_heart@4x.png new file mode 100644 index 000000000..96536ef63 Binary files /dev/null and b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_heart@4x.png differ diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt index 8fabcf828..2121b9cfd 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt @@ -16,6 +16,7 @@ enum class DesktopPlatform(val libPath: String, val libExtension: String, val co MAC_AARCH64("/libs/mac-aarch64", "dylib", unixConfigPath, unixDataPath); fun isLinux() = this == LINUX_X86_64 || this == LINUX_AARCH64 + fun isMac() = this == MAC_X86_64 || this == MAC_AARCH64 } private fun detectDesktopPlatform(): DesktopPlatform { diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Type.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Type.desktop.kt index 902675236..082036558 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Type.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Type.desktop.kt @@ -2,6 +2,7 @@ package chat.simplex.common.ui.theme import androidx.compose.ui.text.font.* import androidx.compose.ui.text.platform.Font +import chat.simplex.common.platform.desktopPlatform import chat.simplex.res.MR actual val Inter: FontFamily = FontFamily( @@ -12,3 +13,16 @@ actual val Inter: FontFamily = FontFamily( Font(MR.fonts.Inter.medium.file, FontWeight.Medium), Font(MR.fonts.Inter.light.file, FontWeight.Light) ) + +actual val EmojiFont: FontFamily = if (desktopPlatform.isMac()) { + FontFamily.Default +} else { + FontFamily( + Font(MR.fonts.NotoColorEmoji.regular.file), + Font(MR.fonts.NotoColorEmoji.regular.file, style = FontStyle.Italic), + Font(MR.fonts.NotoColorEmoji.regular.file, FontWeight.Bold), + Font(MR.fonts.NotoColorEmoji.regular.file, FontWeight.SemiBold), + Font(MR.fonts.NotoColorEmoji.regular.file, FontWeight.Medium), + Font(MR.fonts.NotoColorEmoji.regular.file, FontWeight.Light) + ) +} diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt index c86572fe5..9b265a5f5 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.desktop.kt @@ -1,15 +1,34 @@ package chat.simplex.common.views.chat.item +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.size +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.* import chat.simplex.common.model.ChatItem import chat.simplex.common.model.MsgContent import chat.simplex.common.platform.FileChooserLauncher +import chat.simplex.common.platform.desktopPlatform +import chat.simplex.common.ui.theme.EmojiFont import chat.simplex.common.views.helpers.* import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +@Composable +actual fun ReactionIcon(text: String, fontSize: TextUnit) { + if (desktopPlatform.isMac() && isHeartEmoji(text)) { + val sp = with(LocalDensity.current) { (fontSize.value + 8).sp.toDp() } + Image(painterResource(MR.images.ic_heart), null, Modifier.size(sp).padding(top = 4.dp, bottom = 2.dp)) + } else { + Text(text, fontSize = fontSize, fontFamily = EmojiFont) + } +} + @Composable actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserLauncher, showMenu: MutableState) { ItemAction(stringResource(MR.strings.save_verb), painterResource(MR.images.ic_download), onClick = { diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.desktop.kt new file mode 100644 index 000000000..05ed7002e --- /dev/null +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/EmojiItemView.desktop.kt @@ -0,0 +1,27 @@ +package chat.simplex.common.views.chat.item + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import chat.simplex.common.model.MREmojiChar +import chat.simplex.common.platform.desktopPlatform +import chat.simplex.res.MR +import dev.icerock.moko.resources.compose.painterResource + +@Composable +actual fun EmojiText(text: String) { + val s = text.trim() + if (desktopPlatform.isMac() && isHeartEmoji(s)) { + Image(painterResource(MR.images.ic_heart), null, Modifier.height(62.dp).width(54.dp).padding(vertical = 8.dp)) + } else { + Text(s, style = if (s.codePoints().count() < 4) largeEmojiFont else mediumEmojiFont) + } +} + +/** [MREmojiChar.Heart.value] */ +fun isHeartEmoji(s: String): Boolean = + (s.codePoints().count() == 2L && s.codePointAt(0) == 10084 && s.codePointAt(1) == 65039) || + s.codePoints().count() == 1L && s.codePointAt(0) == 10084