mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-27 02:05:48 +00:00
android: notifications (#369)
* minimal implementation of notifications and broken framework for background check for messages * linting and need different id to have multiple messages * working notification on new messages * add autocancel to notifications * add rudimentary linking to chat from notification * group notifications from the same chat * clarify comment * revert to working version * refactor * minors * two channels, silent and shouty * rudimentary state control for notifications * check if running in foreground * more elegant solution to don't notify if in chat * tidy up DisposableEffect use * change message notification priority to high * nuke opt-ins * navigation via notification occasionally works with race condition (WIP) * notification navigation is working; remove chat list/view from navigation; refactor ChatListNavLinkView * group all simplex notifications, only show the latest message per chat, notification icons * increase time to 30 sec Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
@@ -40,6 +40,11 @@ android {
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material.ExperimentalMaterialApi"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.insets.ExperimentalAnimatedInsets"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi"
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -73,6 +78,10 @@ dependencies {
|
||||
implementation "androidx.navigation:navigation-compose:2.4.1"
|
||||
implementation "com.google.accompanist:accompanist-insets:0.23.0"
|
||||
|
||||
def work_version = "2.7.1"
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation "androidx.work:work-multiprocess:$work_version"
|
||||
|
||||
def camerax_version = "1.1.0-beta01"
|
||||
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||
|
||||
@@ -2,15 +2,14 @@ package chat.simplex.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.navigation.*
|
||||
import androidx.navigation.compose.*
|
||||
@@ -20,26 +19,20 @@ import chat.simplex.app.views.*
|
||||
import chat.simplex.app.views.chat.ChatInfoView
|
||||
import chat.simplex.app.views.chat.ChatView
|
||||
import chat.simplex.app.views.chatlist.ChatListView
|
||||
import chat.simplex.app.views.chatlist.openChat
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.newchat.*
|
||||
import chat.simplex.app.views.usersettings.*
|
||||
import com.google.accompanist.insets.ExperimentalAnimatedInsets
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.serialization.decodeFromString
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@ExperimentalPermissionsApi
|
||||
@ExperimentalMaterialApi
|
||||
class MainActivity: ComponentActivity() {
|
||||
private val vm by viewModels<SimplexViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// testJson()
|
||||
connectIfOpenedViaUri(intent, vm.chatModel)
|
||||
processIntent(intent, vm.chatModel)
|
||||
// vm.app.initiateBackgroundWork()
|
||||
setContent {
|
||||
SimpleXTheme {
|
||||
Navigation(vm.chatModel)
|
||||
@@ -48,50 +41,37 @@ class MainActivity: ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
class SimplexViewModel(application: Application): AndroidViewModel(application) {
|
||||
val chatModel = getApplication<SimplexApp>().chatModel
|
||||
val app = getApplication<SimplexApp>()
|
||||
val chatModel = app.chatModel
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalPermissionsApi
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun MainPage(chatModel: ChatModel, nav: NavController) {
|
||||
when (chatModel.userCreated.value) {
|
||||
null -> SplashView()
|
||||
false -> WelcomeView(chatModel) { nav.navigate(Pages.ChatList.route) }
|
||||
true -> ChatListView(chatModel, nav)
|
||||
false -> WelcomeView(chatModel) // { nav.navigate(Pages.ChatList.route) }
|
||||
true -> if (chatModel.chatId.value == null) {
|
||||
ChatListView(chatModel, nav)
|
||||
} else {
|
||||
ChatView(chatModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalPermissionsApi
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun Navigation(chatModel: ChatModel) {
|
||||
println("*** in Navigation")
|
||||
val nav = rememberNavController()
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
Box {
|
||||
NavHost(navController = nav, startDestination = Pages.Home.route) {
|
||||
composable(route = Pages.Home.route) {
|
||||
println("*** composable MainPage")
|
||||
MainPage(chatModel, nav)
|
||||
}
|
||||
composable(route = Pages.Welcome.route) {
|
||||
WelcomeView(chatModel) {
|
||||
nav.navigate(Pages.Home.route) {
|
||||
popUpTo(Pages.Home.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
composable(route = Pages.ChatList.route) {
|
||||
ChatListView(chatModel, nav)
|
||||
}
|
||||
composable(route = Pages.Chat.route) {
|
||||
ChatView(chatModel, nav)
|
||||
WelcomeView(chatModel)
|
||||
}
|
||||
composable(route = Pages.AddContact.route) {
|
||||
AddContactView(chatModel, nav)
|
||||
@@ -136,8 +116,6 @@ sealed class Pages(val route: String) {
|
||||
object Terminal: Pages("terminal")
|
||||
object Welcome: Pages("welcome")
|
||||
object TerminalItemDetails: Pages("details")
|
||||
object ChatList: Pages("chats")
|
||||
object Chat: Pages("chat")
|
||||
object AddContact: Pages("add_contact")
|
||||
object Connect: Pages("connect")
|
||||
object ChatInfo: Pages("chat_info")
|
||||
@@ -147,28 +125,42 @@ sealed class Pages(val route: String) {
|
||||
object Markdown: Pages("markdown")
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun connectIfOpenedViaUri(intent: Intent?, chatModel: ChatModel) {
|
||||
val uri = intent?.data
|
||||
if (intent?.action == "android.intent.action.VIEW" && uri != null) {
|
||||
Log.d("SIMPLEX", "connectIfOpenedViaUri: opened via link")
|
||||
if (chatModel.currentUser.value == null) {
|
||||
chatModel.appOpenUrl.value = uri
|
||||
} else {
|
||||
withUriAction(chatModel, uri) { action ->
|
||||
chatModel.alertManager.showAlertMsg(
|
||||
title = "Connect via $action link?",
|
||||
text = "Your profile will be sent to the contact that you received this link from.",
|
||||
confirmText = "Connect",
|
||||
onConfirm = {
|
||||
withApi {
|
||||
Log.d("SIMPLEX", "connectIfOpenedViaUri: connecting")
|
||||
connectViaUri(chatModel, action, uri)
|
||||
}
|
||||
}
|
||||
)
|
||||
fun processIntent(intent: Intent?, chatModel: ChatModel) {
|
||||
when (intent?.action) {
|
||||
NtfManager.OpenChatAction -> {
|
||||
val chatId = intent.getStringExtra("chatId")
|
||||
Log.d("SIMPLEX", "processIntent: OpenChatAction $chatId")
|
||||
if (chatId != null) {
|
||||
val cInfo = chatModel.getChat(chatId)?.chatInfo
|
||||
if (cInfo != null) withApi { openChat(chatModel, cInfo) }
|
||||
}
|
||||
}
|
||||
"android.intent.action.VIEW" -> {
|
||||
val uri = intent.data
|
||||
if (uri != null) connectIfOpenedViaUri(uri, chatModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) {
|
||||
Log.d("SIMPLEX", "connectIfOpenedViaUri: opened via link")
|
||||
if (chatModel.currentUser.value == null) {
|
||||
// TODO open from chat list view
|
||||
chatModel.appOpenUrl.value = uri
|
||||
} else {
|
||||
withUriAction(chatModel, uri) { action ->
|
||||
chatModel.alertManager.showAlertMsg(
|
||||
title = "Connect via $action link?",
|
||||
text = "Your profile will be sent to the contact that you received this link from.",
|
||||
confirmText = "Connect",
|
||||
onConfirm = {
|
||||
withApi {
|
||||
Log.d("SIMPLEX", "connectIfOpenedViaUri: connecting")
|
||||
connectViaUri(chatModel, action, uri)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ import android.util.Log
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.simplex.app.model.ChatController
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import androidx.work.*
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
import java.util.concurrent.Semaphore
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
// ghc's rts
|
||||
@@ -27,15 +27,28 @@ external fun chatInit(path: String): ChatCtrl
|
||||
external fun chatSendCmd(ctrl: ChatCtrl, msg: String) : String
|
||||
external fun chatRecvMsg(ctrl: ChatCtrl) : String
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
class SimplexApp: Application() {
|
||||
private lateinit var controller: ChatController
|
||||
lateinit var chatModel: ChatModel
|
||||
private lateinit var ntfManager: NtfManager
|
||||
|
||||
fun initiateBackgroundWork() {
|
||||
val backgroundConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val request = OneTimeWorkRequestBuilder<BackgroundAPIWorker>()
|
||||
.setInitialDelay(5, TimeUnit.MINUTES)
|
||||
.setConstraints(backgroundConstraints)
|
||||
.build()
|
||||
WorkManager.getInstance(applicationContext)
|
||||
.enqueue(request)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ntfManager = NtfManager(applicationContext)
|
||||
val ctrl = chatInit(applicationContext.filesDir.toString())
|
||||
controller = ChatController(ctrl, AlertManager())
|
||||
controller = ChatController(ctrl, AlertManager(), ntfManager, applicationContext)
|
||||
chatModel = controller.chatModel
|
||||
withApi {
|
||||
val user = controller.apiGetActiveUser()
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package chat.simplex.app.model
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.*
|
||||
import chat.simplex.app.chatRecvMsg
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class BackgroundAPIWorker(appContext: Context, workerParams: WorkerParameters, ctrl: ChatCtrl):
|
||||
Worker(appContext, workerParams) {
|
||||
val controller = ctrl
|
||||
override fun doWork(): Result {
|
||||
Log.d("BackgroundAPIWorker", "running")
|
||||
getNewItems()
|
||||
|
||||
// Enqueue another request for later to make this periodic
|
||||
val request = buildRequest()
|
||||
WorkManager.getInstance(applicationContext)
|
||||
.enqueue(request)
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun getNewItems() {
|
||||
val json = chatRecvMsg(controller)
|
||||
val r = APIResponse.decodeStr(json).resp
|
||||
Log.d("SIMPLEX", "chatRecvMsg: ${r.responseType}")
|
||||
}
|
||||
|
||||
private fun buildRequest(): OneTimeWorkRequest {
|
||||
val backgroundConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
return OneTimeWorkRequestBuilder<BackgroundAPIWorker>()
|
||||
.setInitialDelay(5, TimeUnit.MINUTES)
|
||||
.setConstraints(backgroundConstraints)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,15 @@ import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.*
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import chat.simplex.app.SimplexApp
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import chat.simplex.app.ui.theme.SecretColor
|
||||
import kotlinx.datetime.*
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
class ChatModel(val controller: ChatController, val alertManager: SimplexApp.AlertManager) {
|
||||
var currentUser = mutableStateOf<User?>(null)
|
||||
var userCreated = mutableStateOf<Boolean?>(null)
|
||||
var chats = mutableStateListOf<Chat>()
|
||||
var chatsLoaded = mutableStateOf<Boolean?>(null)
|
||||
var chatId = mutableStateOf<String?>(null)
|
||||
var chatItems = mutableStateListOf<ChatItem>()
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package chat.simplex.app.model
|
||||
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import chat.simplex.app.MainActivity
|
||||
import chat.simplex.app.R
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
class NtfManager(val context: Context) {
|
||||
companion object {
|
||||
const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION"
|
||||
const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION"
|
||||
const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT"
|
||||
}
|
||||
|
||||
private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
private var prevNtfTime = mutableMapOf<String, Long>()
|
||||
private val msgNtfTimeoutMs = 30000L
|
||||
|
||||
init {
|
||||
manager.createNotificationChannel(NotificationChannel(
|
||||
MessageChannel,
|
||||
"SimpleX Chat messages",
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
))
|
||||
}
|
||||
|
||||
fun notifyMessageReceived(cInfo: ChatInfo, cItem: ChatItem) {
|
||||
Log.d("SIMPLEX", "notifyMessageReceived ${cInfo.id}")
|
||||
val now = Clock.System.now().toEpochMilliseconds()
|
||||
val recentNotification = (now - prevNtfTime.getOrDefault(cInfo.id, 0) < msgNtfTimeoutMs)
|
||||
prevNtfTime[cInfo.id] = now
|
||||
|
||||
val notification = NotificationCompat.Builder(context, MessageChannel)
|
||||
.setContentTitle(cInfo.displayName)
|
||||
.setContentText(cItem.content.text)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setGroup(MessageGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
.setSmallIcon(R.drawable.ntf_icon)
|
||||
.setColor(0x88FFFF)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(getMsgPendingIntent(cInfo))
|
||||
.setSilent(recentNotification)
|
||||
.build()
|
||||
|
||||
val summary = NotificationCompat.Builder(context, MessageChannel)
|
||||
.setSmallIcon(R.drawable.ntf_icon)
|
||||
.setColor(0x88FFFF)
|
||||
.setGroup(MessageGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
.setGroupSummary(true)
|
||||
.build()
|
||||
|
||||
with(NotificationManagerCompat.from(context)) {
|
||||
// using cInfo.id only shows one notification per chat and updates it when the message arrives
|
||||
notify(cInfo.id.hashCode(), notification)
|
||||
notify(0, summary)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMsgPendingIntent(cInfo: ChatInfo) : PendingIntent{
|
||||
Log.d("SIMPLEX", "getMsgPendingIntent ${cInfo.id}")
|
||||
val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt()
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.putExtra("chatId", cInfo.id)
|
||||
.setAction(OpenChatAction)
|
||||
return TaskStackBuilder.create(context).run {
|
||||
addNextIntentWithParentStack(intent)
|
||||
getPendingIntent(uniqueInt, PendingIntent.FLAG_IMMUTABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package chat.simplex.app.model
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.ActivityManager.RunningAppProcessInfo
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.simplex.app.*
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.*
|
||||
@@ -14,19 +18,17 @@ import kotlin.concurrent.thread
|
||||
|
||||
typealias ChatCtrl = Long
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.AlertManager) {
|
||||
open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.AlertManager, val ntfManager: NtfManager, val appContext: Context) {
|
||||
var chatModel = ChatModel(this, alertManager)
|
||||
|
||||
suspend fun startChat(u: User) {
|
||||
chatModel.currentUser = mutableStateOf(u)
|
||||
chatModel.userCreated.value = true
|
||||
Log.d("SIMPLEX (user)", u.toString())
|
||||
try {
|
||||
apiStartChat()
|
||||
chatModel.userAddress.value = apiGetUserAddress()
|
||||
chatModel.chats.addAll(apiGetChats())
|
||||
chatModel.chatsLoaded.value = true
|
||||
chatModel.currentUser = mutableStateOf(u)
|
||||
chatModel.userCreated.value = true
|
||||
startReceiver()
|
||||
Log.d("SIMPLEX", "started chat")
|
||||
} catch(e: Error) {
|
||||
@@ -41,6 +43,18 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert
|
||||
}
|
||||
}
|
||||
|
||||
open fun isAppOnForeground(context: Context): Boolean {
|
||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val appProcesses = activityManager.runningAppProcesses ?: return false
|
||||
val packageName = context.packageName
|
||||
for (appProcess in appProcesses) {
|
||||
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun sendCmd(cmd: CC): CR {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val c = cmd.cmdString
|
||||
@@ -146,9 +160,9 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert
|
||||
|
||||
suspend fun apiDeleteChat(type: ChatType, id: Long): Boolean {
|
||||
val r = sendCmd(CC.ApiDeleteChat(type, id))
|
||||
when {
|
||||
r is CR.ContactDeleted -> return true // TODO groups
|
||||
r is CR.ChatCmdError -> {
|
||||
when (r) {
|
||||
is CR.ContactDeleted -> return true // TODO groups
|
||||
is CR.ChatCmdError -> {
|
||||
val e = r.chatError
|
||||
if (e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.ContactGroups) {
|
||||
alertManager.showAlertMsg(
|
||||
@@ -260,7 +274,9 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert
|
||||
val cInfo = r.chatItem.chatInfo
|
||||
val cItem = r.chatItem.chatItem
|
||||
chatModel.addChatItem(cInfo, cItem)
|
||||
// NtfManager.shared.notifyMessageReceived(cInfo, cItem)
|
||||
if (!isAppOnForeground(appContext) || chatModel.chatId.value != cInfo.id) {
|
||||
ntfManager.notifyMessageReceived(cInfo, cItem)
|
||||
}
|
||||
}
|
||||
// case let .chatItemUpdated(aChatItem):
|
||||
// let cInfo = aChatItem.chatInfo
|
||||
@@ -432,7 +448,7 @@ sealed class CR {
|
||||
is ChatStarted -> "chatStarted"
|
||||
is ChatRunning -> "chatRunning"
|
||||
is ApiChats -> "apiChats"
|
||||
is ApiChat -> "apiChats"
|
||||
is ApiChat -> "apiChat"
|
||||
is Invitation -> "invitation"
|
||||
is SentConfirmation -> "sentConfirmation"
|
||||
is SentInvitation -> "sentInvitation"
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package chat.simplex.app.views
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
|
||||
@Composable
|
||||
fun SplashView() {
|
||||
|
||||
@@ -23,10 +23,8 @@ import chat.simplex.app.views.helpers.CloseSheetBar
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun TerminalView(chatModel: ChatModel, nav: NavController) {
|
||||
TerminalLayout(chatModel.terminalItems, nav::popBackStack, nav::navigate) { cmd ->
|
||||
|
||||
@@ -19,11 +19,9 @@ import chat.simplex.app.model.Profile
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun WelcomeView(chatModel: ChatModel, routeHome: () -> Unit) {
|
||||
fun WelcomeView(chatModel: ChatModel) {
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -60,7 +58,7 @@ fun WelcomeView(chatModel: ChatModel, routeHome: () -> Unit) {
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
CreateProfilePanel(chatModel, routeHome)
|
||||
CreateProfilePanel(chatModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,9 +69,8 @@ fun isValidDisplayName(name: String) : Boolean {
|
||||
return (name.firstOrNull { it.isWhitespace() }) == null
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun CreateProfilePanel(chatModel: ChatModel, routeHome: () -> Unit) {
|
||||
fun CreateProfilePanel(chatModel: ChatModel) {
|
||||
var displayName by remember { mutableStateOf("") }
|
||||
var fullName by remember { mutableStateOf("") }
|
||||
|
||||
@@ -154,10 +151,9 @@ fun CreateProfilePanel(chatModel: ChatModel, routeHome: () -> Unit) {
|
||||
Profile(displayName, fullName)
|
||||
)
|
||||
chatModel.controller.startChat(user)
|
||||
routeHome()
|
||||
}
|
||||
},
|
||||
enabled = displayName.isNotEmpty()
|
||||
) { Text("Create")}
|
||||
) { Text("Create") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,16 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import chat.simplex.app.Pages
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun ChatInfoView(chatModel: ChatModel, nav: NavController) {
|
||||
val chat = chatModel.chats.firstOrNull { it.id == chatModel.chatId.value }
|
||||
if (chat != null) {
|
||||
ChatInfoLayout(chat,
|
||||
close = { nav.popBackStack() },
|
||||
close = nav::popBackStack,
|
||||
deleteContact = {
|
||||
chatModel.alertManager.showAlertMsg(
|
||||
title = "Delete contact?",
|
||||
@@ -39,7 +36,8 @@ fun ChatInfoView(chatModel: ChatModel, nav: NavController) {
|
||||
val r = chatModel.controller.apiDeleteChat(cInfo.chatType, cInfo.apiId)
|
||||
if (r) {
|
||||
chatModel.removeChat(cInfo.id)
|
||||
nav.navigate(Pages.ChatList.route)
|
||||
chatModel.chatId.value = null
|
||||
nav.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package chat.simplex.app.views.chat
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -12,7 +14,6 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -24,56 +25,54 @@ import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.item.ChatItemView
|
||||
import chat.simplex.app.views.helpers.ChatInfoImage
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.insets.*
|
||||
import kotlinx.coroutines.*
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@ExperimentalTextApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun ChatView(chatModel: ChatModel, nav: NavController) {
|
||||
if (chatModel.chatId.value != null && chatModel.chats.count() > 0) {
|
||||
val chat: Chat? = chatModel.chats.firstOrNull { chat -> chat.chatInfo.id == chatModel.chatId.value }
|
||||
if (chat != null) {
|
||||
// TODO a more advanced version would mark as read only if in view
|
||||
LaunchedEffect(chat.chatItems) {
|
||||
delay(1000L)
|
||||
if (chat.chatItems.count() > 0) {
|
||||
chatModel.markChatItemsRead(chat.chatInfo)
|
||||
withApi {
|
||||
chatModel.controller.apiChatRead(
|
||||
chat.chatInfo.chatType,
|
||||
chat.chatInfo.apiId,
|
||||
CC.ItemRange(chat.chatStats.minUnreadItemId, chat.chatItems.last().id)
|
||||
)
|
||||
}
|
||||
val chat: Chat? = chatModel.chats.firstOrNull { chat -> chat.chatInfo.id == chatModel.chatId.value }
|
||||
if (chat == null) {
|
||||
chatModel.chatId.value = null
|
||||
} else {
|
||||
BackHandler { chatModel.chatId.value = null }
|
||||
// TODO a more advanced version would mark as read only if in view
|
||||
LaunchedEffect(chat.chatItems) {
|
||||
Log.d("SIMPLEX", "ChatView ${chatModel.chatId.value}: LaunchedEffect")
|
||||
delay(1000L)
|
||||
if (chat.chatItems.count() > 0) {
|
||||
chatModel.markChatItemsRead(chat.chatInfo)
|
||||
withApi {
|
||||
chatModel.controller.apiChatRead(
|
||||
chat.chatInfo.chatType,
|
||||
chat.chatInfo.apiId,
|
||||
CC.ItemRange(chat.chatStats.minUnreadItemId, chat.chatItems.last().id)
|
||||
)
|
||||
}
|
||||
}
|
||||
ChatLayout(chat, chatModel.chatItems,
|
||||
back = { nav.popBackStack() },
|
||||
info = { nav.navigate(Pages.ChatInfo.route) },
|
||||
sendMessage = { msg ->
|
||||
withApi {
|
||||
// show "in progress"
|
||||
val cInfo = chat.chatInfo
|
||||
val newItem = chatModel.controller.apiSendMessage(
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
mc = MsgContent.MCText(msg)
|
||||
)
|
||||
// hide "in progress"
|
||||
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
ChatLayout(chat, chatModel.chatItems,
|
||||
back = { chatModel.chatId.value = null },
|
||||
info = { nav.navigate(Pages.ChatInfo.route) },
|
||||
sendMessage = { msg ->
|
||||
withApi {
|
||||
// show "in progress"
|
||||
val cInfo = chat.chatInfo
|
||||
val newItem = chatModel.controller.apiSendMessage(
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
mc = MsgContent.MCText(msg)
|
||||
)
|
||||
// hide "in progress"
|
||||
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@Composable
|
||||
fun ChatLayout(
|
||||
chat: Chat, chatItems: List<ChatItem>,
|
||||
@@ -101,7 +100,10 @@ fun ChatLayout(
|
||||
|
||||
@Composable
|
||||
fun ChatInfoToolbar(chat: Chat, back: () -> Unit, info: () -> Unit) {
|
||||
Box(Modifier.height(60.dp).padding(horizontal = 8.dp),
|
||||
Box(
|
||||
Modifier
|
||||
.height(60.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
IconButton(onClick = back) {
|
||||
@@ -136,9 +138,6 @@ fun ChatInfoToolbar(chat: Chat, back: () -> Unit, info: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@Composable
|
||||
fun ChatItemsList(chatItems: List<ChatItem>) {
|
||||
val listState = rememberLazyListState()
|
||||
@@ -157,8 +156,6 @@ fun ChatItemsList(chatItems: List<ChatItem>) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@ExperimentalAnimatedInsets
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package chat.simplex.app.views.chat.item
|
||||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.model.CIDirection
|
||||
@@ -13,7 +12,6 @@ import chat.simplex.app.model.ChatItem
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun ChatItemView(chatItem: ChatItem, uriHandler: UriHandler? = null) {
|
||||
val sent = chatItem.chatDir.sent
|
||||
@@ -33,7 +31,6 @@ fun ChatItemView(chatItem: ChatItem, uriHandler: UriHandler? = null) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewChatItemView() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package chat.simplex.app.views.chat.item
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
@@ -15,8 +16,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.LightGray
|
||||
import chat.simplex.app.model.CIDirection
|
||||
import chat.simplex.app.model.ChatItem
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@@ -24,7 +25,6 @@ import kotlinx.datetime.Clock
|
||||
val SentColorLight = Color(0x1E45B8FF)
|
||||
val ReceivedColorLight = Color(0x1EB1B0B5)
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun TextItemView(chatItem: ChatItem, uriHandler: UriHandler? = null) {
|
||||
val sent = chatItem.chatDir.sent
|
||||
@@ -55,7 +55,6 @@ fun appendGroupMember(b: AnnotatedString.Builder, chatItem: ChatItem, groupMembe
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun MarkdownText (
|
||||
chatItem: ChatItem,
|
||||
@@ -110,7 +109,6 @@ fun MarkdownText (
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewTextItemViewSnd() {
|
||||
@@ -123,7 +121,6 @@ fun PreviewTextItemViewSnd() {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewTextItemViewRcv() {
|
||||
@@ -136,7 +133,6 @@ fun PreviewTextItemViewRcv() {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewTextItemViewLong() {
|
||||
|
||||
+26
-54
@@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
@@ -17,41 +16,31 @@ import chat.simplex.app.Pages
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel, nav: NavController) {
|
||||
ChatListNavLink(
|
||||
ChatListNavLinkLayout(
|
||||
chat = chat,
|
||||
action = {
|
||||
when (chat.chatInfo) {
|
||||
is ChatInfo.Direct -> chatNavLink(chat, chatModel, nav)
|
||||
is ChatInfo.Group -> chatNavLink(chat, chatModel, nav)
|
||||
is ChatInfo.ContactRequest -> contactRequestNavLink(chat.chatInfo, chatModel, nav)
|
||||
click = {
|
||||
if (chat.chatInfo is ChatInfo.ContactRequest) {
|
||||
contactRequestAlertDialog(chat.chatInfo, chatModel, nav)
|
||||
} else {
|
||||
withApi { openChat(chatModel, chat.chatInfo) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun chatNavLink(chatPreview: Chat, chatModel: ChatModel, navController: NavController) {
|
||||
withApi {
|
||||
val chatInfo = chatPreview.chatInfo
|
||||
val chat = chatModel.controller.apiGetChat(chatInfo.chatType, chatInfo.apiId)
|
||||
if (chat != null) {
|
||||
chatModel.chatId.value = chatInfo.id
|
||||
chatModel.chatItems = chat.chatItems.toMutableStateList()
|
||||
navController.navigate(Pages.Chat.route)
|
||||
} else {
|
||||
// TODO show error? or will apiGetChat show it
|
||||
}
|
||||
suspend fun openChat(chatModel: ChatModel, cInfo: ChatInfo) {
|
||||
val chat = chatModel.controller.apiGetChat(cInfo.chatType, cInfo.apiId)
|
||||
if (chat != null) {
|
||||
chatModel.chatItems = chat.chatItems.toMutableStateList()
|
||||
chatModel.chatId.value = cInfo.id
|
||||
}
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun contactRequestNavLink(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel, navController: NavController) {
|
||||
fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel, navController: NavController) {
|
||||
chatModel.alertManager.showAlertDialog(
|
||||
title = "Accept connection request?",
|
||||
text = "If you choose to reject sender will NOT be notified",
|
||||
@@ -75,27 +64,12 @@ fun contactRequestNavLink(contactRequest: ChatInfo.ContactRequest, chatModel: Ch
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun ChatListNavLink(chat: Chat, action: () -> Unit) {
|
||||
ChatListNavLinkLayout(
|
||||
content = {
|
||||
when (chat.chatInfo) {
|
||||
is ChatInfo.Direct -> ChatPreviewView(chat)
|
||||
is ChatInfo.Group -> ChatPreviewView(chat)
|
||||
is ChatInfo.ContactRequest -> ContactRequestView(chat)
|
||||
}
|
||||
},
|
||||
action = action
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatListNavLinkLayout(content: (@Composable () -> Unit), action: () -> Unit) {
|
||||
fun ChatListNavLinkLayout(chat: Chat, click: () -> Unit) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = action)
|
||||
.clickable(onClick = click)
|
||||
.height(88.dp)
|
||||
) {
|
||||
Row(
|
||||
@@ -104,18 +78,18 @@ fun ChatListNavLinkLayout(content: (@Composable () -> Unit), action: () -> Unit)
|
||||
.padding(vertical = 8.dp)
|
||||
.padding(start = 8.dp)
|
||||
.padding(end = 12.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
// TODO?
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.SpaceEvenly
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
content.invoke()
|
||||
if (chat.chatInfo is ChatInfo.ContactRequest) {
|
||||
ContactRequestView(chat)
|
||||
} else {
|
||||
ChatPreviewView(chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
@@ -125,7 +99,7 @@ fun ChatListNavLinkLayout(content: (@Composable () -> Unit), action: () -> Unit)
|
||||
@Composable
|
||||
fun PreviewChatListNavLinkDirect() {
|
||||
SimpleXTheme {
|
||||
ChatListNavLink(
|
||||
ChatListNavLinkLayout(
|
||||
chat = Chat(
|
||||
chatInfo = ChatInfo.Direct.sampleData,
|
||||
chatItems = listOf(
|
||||
@@ -138,12 +112,11 @@ fun PreviewChatListNavLinkDirect() {
|
||||
),
|
||||
chatStats = Chat.ChatStats()
|
||||
),
|
||||
action = {}
|
||||
click = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
@@ -153,7 +126,7 @@ fun PreviewChatListNavLinkDirect() {
|
||||
@Composable
|
||||
fun PreviewChatListNavLinkGroup() {
|
||||
SimpleXTheme {
|
||||
ChatListNavLink(
|
||||
ChatListNavLinkLayout(
|
||||
chat = Chat(
|
||||
chatInfo = ChatInfo.Group.sampleData,
|
||||
chatItems = listOf(
|
||||
@@ -166,12 +139,11 @@ fun PreviewChatListNavLinkGroup() {
|
||||
),
|
||||
chatStats = Chat.ChatStats()
|
||||
),
|
||||
action = {}
|
||||
click = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
@@ -181,13 +153,13 @@ fun PreviewChatListNavLinkGroup() {
|
||||
@Composable
|
||||
fun PreviewChatListNavLinkContactRequest() {
|
||||
SimpleXTheme {
|
||||
ChatListNavLink(
|
||||
ChatListNavLinkLayout(
|
||||
chat = Chat(
|
||||
chatInfo = ChatInfo.ContactRequest.sampleData,
|
||||
chatItems = listOf(),
|
||||
chatStats = Chat.ChatStats()
|
||||
),
|
||||
action = {}
|
||||
click = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
@@ -22,10 +21,9 @@ import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.views.chat.ChatHelpView
|
||||
import chat.simplex.app.views.newchat.NewChatSheet
|
||||
import chat.simplex.app.views.usersettings.SettingsView
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ExperimentalMaterialApi
|
||||
class ScaffoldController(val scope: CoroutineScope) {
|
||||
lateinit var state: BottomSheetScaffoldState
|
||||
val expanded = mutableStateOf(false)
|
||||
@@ -49,7 +47,6 @@ class ScaffoldController(val scope: CoroutineScope) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun scaffoldController(): ScaffoldController {
|
||||
val ctrl = ScaffoldController(scope = rememberCoroutineScope())
|
||||
@@ -64,10 +61,6 @@ fun scaffoldController(): ScaffoldController {
|
||||
return ctrl
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalPermissionsApi
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun ChatListView(chatModel: ChatModel, nav: NavController) {
|
||||
val scaffoldCtrl = scaffoldController()
|
||||
@@ -86,14 +79,11 @@ fun ChatListView(chatModel: ChatModel, nav: NavController) {
|
||||
.background(MaterialTheme.colors.background)
|
||||
) {
|
||||
ChatListToolbar(scaffoldCtrl)
|
||||
when (chatModel.chatsLoaded.value) {
|
||||
true -> if (chatModel.chats.isNotEmpty()) {
|
||||
ChatList(chatModel, nav)
|
||||
} else {
|
||||
val user = chatModel.currentUser.value
|
||||
Help(scaffoldCtrl, displayName = user?.profile?.displayName)
|
||||
}
|
||||
else -> ChatList(chatModel, nav)
|
||||
if (chatModel.chats.isNotEmpty()) {
|
||||
ChatList(chatModel, nav)
|
||||
} else {
|
||||
val user = chatModel.currentUser.value
|
||||
Help(scaffoldCtrl, displayName = user?.profile?.displayName)
|
||||
}
|
||||
}
|
||||
if (scaffoldCtrl.expanded.value) {
|
||||
@@ -108,7 +98,6 @@ fun ChatListView(chatModel: ChatModel, nav: NavController) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun Help(scaffoldCtrl: ScaffoldController, displayName: String?) {
|
||||
Column(
|
||||
@@ -142,7 +131,6 @@ fun Help(scaffoldCtrl: ScaffoldController, displayName: String?) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun ChatListToolbar(scaffoldCtrl: ScaffoldController) {
|
||||
Row(
|
||||
@@ -178,8 +166,6 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun ChatList(chatModel: ChatModel, navController: NavController) {
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
@@ -191,15 +177,3 @@ fun ChatList(chatModel: ChatModel, navController: NavController) {
|
||||
}
|
||||
}
|
||||
}
|
||||
//@Preview
|
||||
//@Composable
|
||||
//fun PreviewChatListView() {
|
||||
// SimpleXTheme {
|
||||
// ChatListView(
|
||||
// chats = listOf(
|
||||
// Chat()
|
||||
// ),
|
||||
//
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -9,7 +9,6 @@ import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -23,7 +22,6 @@ import chat.simplex.app.views.chat.item.MarkdownText
|
||||
import chat.simplex.app.views.helpers.ChatInfoImage
|
||||
import chat.simplex.app.views.helpers.badgeLayout
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Composable
|
||||
fun ChatPreviewView(chat: Chat) {
|
||||
Row {
|
||||
@@ -78,7 +76,6 @@ fun ChatPreviewView(chat: Chat) {
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTextApi
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -9,8 +8,6 @@ import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.SupervisedUserCircle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@@ -2,6 +2,5 @@ package chat.simplex.app.views.helpers
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun withApi(action: suspend CoroutineScope.() -> Unit): Job =
|
||||
GlobalScope.launch { withContext(Dispatchers.Main, action) }
|
||||
|
||||
@@ -18,9 +18,7 @@ import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.CloseSheetBar
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Composable
|
||||
fun ConnectContactView(chatModel: ChatModel, nav: NavController) {
|
||||
ConnectContactLayout(
|
||||
@@ -44,7 +42,6 @@ fun ConnectContactView(chatModel: ChatModel, nav: NavController) {
|
||||
)
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
fun withUriAction(
|
||||
chatModel: ChatModel, uri: Uri,
|
||||
run: suspend (String) -> Unit
|
||||
|
||||
@@ -21,13 +21,8 @@ import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chatlist.ScaffoldController
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@ExperimentalPermissionsApi
|
||||
@ExperimentalMaterialApi
|
||||
@Composable
|
||||
fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController, nav: NavController) {
|
||||
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
|
||||
@@ -47,15 +42,12 @@ fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController, nav: Nav
|
||||
newChatCtrl.collapse()
|
||||
nav.navigate(Pages.Connect.route)
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
},
|
||||
close = {
|
||||
newChatCtrl.collapse()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit, close: () -> Unit) {
|
||||
fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) {
|
||||
Row(Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 48.dp),
|
||||
@@ -116,8 +108,7 @@ fun PreviewNewChatSheet() {
|
||||
SimpleXTheme {
|
||||
NewChatSheetLayout(
|
||||
addContact = {},
|
||||
scanCode = {},
|
||||
close = {},
|
||||
scanCode = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,7 @@ import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 861 B |
Binary file not shown.
|
After Width: | Height: | Size: 569 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
@@ -7,7 +7,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:1.3.2"
|
||||
|
||||
@@ -16,8 +16,8 @@ buildscript {
|
||||
}
|
||||
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.1' apply false
|
||||
id 'com.android.library' version '7.1.1' apply false
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user