SocialRootSection.kt (12962B) - raw


      1 package me.rhunk.snapenhance.ui.manager.pages.social
      2 
      3 import androidx.compose.foundation.ExperimentalFoundationApi
      4 import androidx.compose.foundation.clickable
      5 import androidx.compose.foundation.layout.*
      6 import androidx.compose.foundation.lazy.LazyColumn
      7 import androidx.compose.foundation.pager.HorizontalPager
      8 import androidx.compose.foundation.pager.rememberPagerState
      9 import androidx.compose.foundation.shape.RoundedCornerShape
     10 import androidx.compose.material.icons.Icons
     11 import androidx.compose.material.icons.filled.RemoveRedEye
     12 import androidx.compose.material.icons.rounded.Add
     13 import androidx.compose.material3.*
     14 import androidx.compose.runtime.*
     15 import androidx.compose.ui.Alignment
     16 import androidx.compose.ui.Modifier
     17 import androidx.compose.ui.graphics.vector.ImageVector
     18 import androidx.compose.ui.res.vectorResource
     19 import androidx.compose.ui.text.font.FontWeight
     20 import androidx.compose.ui.text.style.TextAlign
     21 import androidx.compose.ui.text.style.TextOverflow
     22 import androidx.compose.ui.unit.dp
     23 import androidx.compose.ui.unit.sp
     24 import androidx.navigation.NavBackStackEntry
     25 import kotlinx.coroutines.launch
     26 import me.rhunk.snapenhance.R
     27 import me.rhunk.snapenhance.common.data.MessagingFriendInfo
     28 import me.rhunk.snapenhance.common.data.MessagingGroupInfo
     29 import me.rhunk.snapenhance.common.data.SocialScope
     30 import me.rhunk.snapenhance.common.ui.rememberAsyncMutableState
     31 import me.rhunk.snapenhance.common.util.snap.BitmojiSelfie
     32 import me.rhunk.snapenhance.storage.*
     33 import me.rhunk.snapenhance.ui.manager.Routes
     34 import me.rhunk.snapenhance.ui.util.coil.BitmojiImage
     35 import me.rhunk.snapenhance.ui.util.pagerTabIndicatorOffset
     36 
     37 class SocialRootSection : Routes.Route() {
     38     private var friendList: List<MessagingFriendInfo> by mutableStateOf(emptyList())
     39     private var groupList: List<MessagingGroupInfo> by mutableStateOf(emptyList())
     40 
     41     private fun updateScopeLists() {
     42         context.coroutineScope.launch {
     43             friendList = context.database.getFriends(descOrder = true)
     44             groupList = context.database.getGroups()
     45         }
     46     }
     47 
     48     @Composable
     49     private fun ScopeList(scope: SocialScope) {
     50         val remainingHours = remember { context.config.root.streaksReminder.remainingHours.get() }
     51 
     52         LazyColumn(
     53             modifier = Modifier
     54                 .fillMaxSize(),
     55             contentPadding = PaddingValues(top = 10.dp, bottom = 110.dp, start = 8.dp, end = 8.dp),
     56             verticalArrangement = Arrangement.spacedBy(8.dp)
     57         ) {
     58             //check if scope list is empty
     59             val listSize = when (scope) {
     60                 SocialScope.GROUP -> groupList.size
     61                 SocialScope.FRIEND -> friendList.size
     62             }
     63 
     64             if (listSize == 0) {
     65                 item {
     66                     Text(
     67                         text = translation["empty_hint"], modifier = Modifier
     68                             .fillMaxWidth()
     69                             .padding(10.dp), textAlign = TextAlign.Center
     70                     )
     71                 }
     72             }
     73 
     74             items(listSize) { index ->
     75                 val id = when (scope) {
     76                     SocialScope.GROUP -> groupList[index].conversationId
     77                     SocialScope.FRIEND -> friendList[index].userId
     78                 }
     79 
     80                 ElevatedCard(
     81                     modifier = Modifier
     82                         .fillMaxWidth()
     83                         .height(70.dp),
     84                     onClick = {
     85                         routes.manageScope.navigate {
     86                             put("id", id)
     87                             put("scope", scope.key)
     88                         }
     89                     }
     90                 ) {
     91                     Row(
     92                         modifier = Modifier
     93                             .padding(10.dp)
     94                             .fillMaxSize(),
     95                         verticalAlignment = Alignment.CenterVertically
     96                     ) {
     97                         when (scope) {
     98                             SocialScope.GROUP -> {
     99                                 val group = groupList[index]
    100                                 Column(
    101                                     modifier = Modifier
    102                                         .padding(start = 7.dp)
    103                                         .fillMaxWidth()
    104                                         .weight(1f)
    105                                 ) {
    106                                     Text(
    107                                         text = group.name,
    108                                         maxLines = 1,
    109                                         fontWeight = FontWeight.Bold
    110                                     )
    111                                 }
    112                             }
    113 
    114                             SocialScope.FRIEND -> {
    115                                 val friend = friendList[index]
    116                                 val streaks by rememberAsyncMutableState(defaultValue = friend.streaks) {
    117                                     context.database.getFriendStreaks(friend.userId)
    118                                 }
    119 
    120                                 BitmojiImage(
    121                                     context = context,
    122                                     url = BitmojiSelfie.getBitmojiSelfie(
    123                                         friend.selfieId,
    124                                         friend.bitmojiId,
    125                                         BitmojiSelfie.BitmojiSelfieType.NEW_THREE_D
    126                                     )
    127                                 )
    128 
    129                                 Column(
    130                                     modifier = Modifier
    131                                         .padding(start = 7.dp)
    132                                         .fillMaxWidth()
    133                                         .weight(1f)
    134                                 ) {
    135                                     Text(
    136                                         text = friend.displayName ?: friend.mutableUsername,
    137                                         maxLines = 1,
    138                                         fontWeight = FontWeight.Bold
    139                                     )
    140                                     Text(
    141                                         text = friend.mutableUsername,
    142                                         maxLines = 1,
    143                                         fontSize = 12.sp,
    144                                         fontWeight = FontWeight.Light
    145                                     )
    146                                 }
    147                                 Row(verticalAlignment = Alignment.CenterVertically) {
    148                                     streaks?.takeIf { it.notify }?.let { streaks ->
    149                                         Icon(
    150                                             imageVector = ImageVector.vectorResource(id = R.drawable.streak_icon),
    151                                             contentDescription = null,
    152                                             modifier = Modifier.height(40.dp),
    153                                             tint = if (streaks.isAboutToExpire(remainingHours))
    154                                                 MaterialTheme.colorScheme.error
    155                                             else MaterialTheme.colorScheme.primary
    156                                         )
    157                                         Text(
    158                                             text = translation.format(
    159                                                 "streaks_expiration_short",
    160                                                 "hours" to (((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt().takeIf { it > 0 } ?: 0)
    161                                                     .toString()
    162                                             ),
    163                                             maxLines = 1,
    164                                             fontWeight = FontWeight.Bold
    165                                         )
    166                                     }
    167                                 }
    168                             }
    169                         }
    170 
    171                         FilledIconButton(onClick = {
    172                             routes.messagingPreview.navigate {
    173                                 put("id", id)
    174                                 put("scope", scope.key)
    175                             }
    176                         }) {
    177                             Icon(imageVector = Icons.Filled.RemoveRedEye, contentDescription = null)
    178                         }
    179                     }
    180                 }
    181             }
    182         }
    183     }
    184 
    185     @OptIn(ExperimentalFoundationApi::class)
    186     override val content: @Composable (NavBackStackEntry) -> Unit = {
    187         val titles = remember {
    188             listOf(translation["friends_tab"], translation["groups_tab"])
    189         }
    190         val coroutineScope = rememberCoroutineScope()
    191         val pagerState = rememberPagerState { titles.size }
    192         var addFriendDialog by remember { mutableStateOf(null as AddFriendDialog?) }
    193 
    194         if (addFriendDialog != null) {
    195             addFriendDialog?.Content {
    196                 addFriendDialog = null
    197             }
    198             DisposableEffect(Unit) {
    199                 onDispose {
    200                     updateScopeLists()
    201                 }
    202             }
    203         }
    204 
    205         LaunchedEffect(Unit) {
    206             updateScopeLists()
    207         }
    208 
    209         Scaffold(
    210             floatingActionButton = {
    211                 FloatingActionButton(
    212                     onClick = {
    213                         addFriendDialog = AddFriendDialog(
    214                             context,
    215                             AddFriendDialog.Actions(
    216                                 onFriendState = { friend, state ->
    217                                     if (state) {
    218                                         context.bridgeService?.triggerScopeSync(SocialScope.FRIEND, friend.userId)
    219                                     } else {
    220                                         context.database.deleteFriend(friend.userId)
    221                                     }
    222                                 },
    223                                 onGroupState = { group, state ->
    224                                     if (state) {
    225                                         context.bridgeService?.triggerScopeSync(SocialScope.GROUP, group.conversationId)
    226                                     } else {
    227                                         context.database.deleteGroup(group.conversationId)
    228                                     }
    229                                 },
    230                                 getFriendState = { friend -> context.database.getFriendInfo(friend.userId) != null },
    231                                 getGroupState = { group -> context.database.getGroupInfo(group.conversationId) != null }
    232                             ),
    233                             pinnedIds = (friendList.map { it.userId } + groupList.map { it.conversationId }).reversed(),
    234                         )
    235                     },
    236                     modifier = Modifier.padding(10.dp),
    237                     containerColor = MaterialTheme.colorScheme.primary,
    238                     contentColor = MaterialTheme.colorScheme.onPrimary,
    239                     shape = RoundedCornerShape(16.dp),
    240                 ) {
    241                     Icon(
    242                         imageVector = Icons.Rounded.Add,
    243                         contentDescription = null
    244                     )
    245                 }
    246             }
    247         ) { paddingValues ->
    248             Column(modifier = Modifier.padding(paddingValues)) {
    249                 TabRow(selectedTabIndex = pagerState.currentPage, indicator = { tabPositions ->
    250                     TabRowDefaults.SecondaryIndicator(
    251                         Modifier.pagerTabIndicatorOffset(
    252                             pagerState = pagerState,
    253                             tabPositions = tabPositions
    254                         )
    255                     )
    256                 }) {
    257                     titles.forEachIndexed { index, title ->
    258                         Tab(
    259                             selected = pagerState.currentPage == index,
    260                             onClick = {
    261                                 coroutineScope.launch {
    262                                     pagerState.animateScrollToPage(index)
    263                                 }
    264                             },
    265                             text = {
    266                                 Text(
    267                                     text = title,
    268                                     maxLines = 2,
    269                                     overflow = TextOverflow.Ellipsis
    270                                 )
    271                             }
    272                         )
    273                     }
    274                 }
    275 
    276                 HorizontalPager(
    277                     modifier = Modifier.padding(paddingValues),
    278                     state = pagerState
    279                 ) { page ->
    280                     when (page) {
    281                         0 -> ScopeList(SocialScope.FRIEND)
    282                         1 -> ScopeList(SocialScope.GROUP)
    283                     }
    284                 }
    285             }
    286         }
    287     }
    288 }