Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,23 @@ class GroupInviteException(

if (second != null && third != null) {
val errorString =
if (isPromotion) R.string.adminPromotionFailedDescriptionMultiple else
if (isPromotion) if (isReinvite) R.string.failedResendPromotionMultiple else R.string.adminPromotionFailedDescriptionMultiple else
if (isReinvite) R.string.failedResendInviteMultiple else R.string.groupInviteFailedMultiple
return Phrase.from(context, errorString)
.put(NAME_KEY, first)
.put(COUNT_KEY, inviteeAccountIds.size - 1)
.put(GROUP_NAME_KEY, groupName)
.format()
} else if (second != null) {
val errorString = if (isPromotion) R.string.adminPromotionFailedDescriptionTwo else
val errorString = if (isPromotion) if (isReinvite) R.string.failedResendPromotionTwo else R.string.adminPromotionFailedDescriptionTwo else
if (isReinvite) R.string.failedResendInviteTwo else R.string.groupInviteFailedTwo
return Phrase.from(context, errorString)
.put(NAME_KEY, first)
.put(OTHER_NAME_KEY, second)
.put(GROUP_NAME_KEY, groupName)
.format()
} else {
val errorString = if (isPromotion) R.string.adminPromotionFailedDescription else
val errorString = if (isPromotion) if (isReinvite) R.string.failedResendPromotion else R.string.adminPromotionFailedDescription else
if (isReinvite) R.string.failedResendInvite else R.string.groupInviteFailedUser
return Phrase.from(context, errorString)
.put(NAME_KEY, first)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ interface GroupManagerV2 {

fun getLeaveGroupConfirmationDialogData(groupId: AccountId, name: String): ConfirmDialogData?

fun getAdminLeaveGroupDialogData(groupId : AccountId, name : String) : ConfirmDialogData?

fun isCurrentUserLastAdmin(groupId : AccountId) : Boolean

data class ConfirmDialogData(
val title: String,
val message: CharSequence,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ fun ConversationSettingsDialogs(
buttons.add(
DialogButtonData(
text = GetString(dialogsState.showSimpleDialog.negativeText),
color = if (dialogsState.showSimpleDialog.negativeStyleDanger) LocalColors.current.danger
else LocalColors.current.text,
qaTag = dialogsState.showSimpleDialog.negativeQaTag,
onClick = dialogsState.showSimpleDialog.onNegative
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
Expand All @@ -28,14 +31,22 @@ import org.thoughtcrime.securesms.conversation.v2.settings.notification.Notifica
import org.thoughtcrime.securesms.conversation.v2.settings.notification.NotificationSettingsViewModel
import org.thoughtcrime.securesms.groups.ManageGroupMembersViewModel
import org.thoughtcrime.securesms.groups.GroupMembersViewModel
import org.thoughtcrime.securesms.groups.SelectContactsViewModel
import org.thoughtcrime.securesms.groups.InviteMembersViewModel
import org.thoughtcrime.securesms.groups.ManageGroupAdminsViewModel
import org.thoughtcrime.securesms.groups.PromoteMembersViewModel
import org.thoughtcrime.securesms.groups.compose.ManageGroupMembersScreen
import org.thoughtcrime.securesms.groups.compose.GroupMembersScreen
import org.thoughtcrime.securesms.groups.compose.InviteAccountIdScreen
import org.thoughtcrime.securesms.groups.compose.InviteContactsScreen
import org.thoughtcrime.securesms.groups.compose.ManageGroupAdminsScreen
import org.thoughtcrime.securesms.groups.compose.PromoteMembersScreen
import org.thoughtcrime.securesms.home.startconversation.newmessage.NewMessageViewModel
import org.thoughtcrime.securesms.home.startconversation.newmessage.State
import org.thoughtcrime.securesms.media.MediaOverviewScreen
import org.thoughtcrime.securesms.media.MediaOverviewViewModel
import org.thoughtcrime.securesms.ui.NavigationAction
import org.thoughtcrime.securesms.ui.ObserveAsEvents
import org.thoughtcrime.securesms.ui.OpenURLAlertDialog
import org.thoughtcrime.securesms.ui.UINavigator
import org.thoughtcrime.securesms.ui.horizontalSlideComposable

Expand Down Expand Up @@ -65,6 +76,30 @@ sealed interface ConversationSettingsDestination: Parcelable {
val groupAddress: Address.Group get() = Address.Group(AccountId(address))
}

@Serializable
@Parcelize
data class RouteManageAdmins private constructor(
private val address: String,
val navigateToPromoteMembers: Boolean = false
) : ConversationSettingsDestination {
constructor(groupAddress: Address.Group, navigateToPromoteMembers: Boolean = false) : this(
groupAddress.address,
navigateToPromoteMembers
)

val groupAddress: Address.Group get() = Address.Group(AccountId(address))
}

@Serializable
@Parcelize
data class RoutePromoteMembers(
private val address: String
): ConversationSettingsDestination {
constructor(groupAddress: Address.Group): this(groupAddress.address)

val groupAddress: Address.Group get() = Address.Group(AccountId(address))
}

@Serializable
@Parcelize
data class RouteInviteToGroup private constructor(
Expand Down Expand Up @@ -94,6 +129,18 @@ sealed interface ConversationSettingsDestination: Parcelable {
data class RouteInviteToCommunity(
val communityUrl: String
): ConversationSettingsDestination

@Serializable
@Parcelize
data class RouteInviteAccountIdToGroup private constructor(
private val address: String,
val excludingAccountIDs: List<String>
): ConversationSettingsDestination {
constructor(groupAddress: Address.Group, excludingAccountIDs: List<String>)
: this(groupAddress.address, excludingAccountIDs)

val groupAddress: Address.Group get() = Address.Group(AccountId(address))
}
}

@SuppressLint("RestrictedApi")
Expand Down Expand Up @@ -195,13 +242,31 @@ fun ConversationSettingsNavHost(
)
}

// Manage group Admins
horizontalSlideComposable<RouteManageAdmins> { backStackEntry ->
val data: RouteManageAdmins = backStackEntry.toRoute()

val viewModel =
hiltViewModel<ManageGroupAdminsViewModel, ManageGroupAdminsViewModel.Factory> { factory ->
factory.create(data.groupAddress, navigator, data.navigateToPromoteMembers)
}

ManageGroupAdminsScreen(
viewModel = viewModel,
onBack = dropUnlessResumed {
handleBack()
},
)
}

// Invite Contacts to group
horizontalSlideComposable<RouteInviteToGroup> { backStackEntry ->
val data: RouteInviteToGroup = backStackEntry.toRoute()

val viewModel =
hiltViewModel<SelectContactsViewModel, SelectContactsViewModel.Factory> { factory ->
hiltViewModel<InviteMembersViewModel, InviteMembersViewModel.Factory> { factory ->
factory.create(
groupAddress = data.groupAddress,
excludingAccountIDs = data.excludingAccountIDs.map(Address::fromSerialized).toSet()
)
}
Expand All @@ -216,23 +281,22 @@ fun ConversationSettingsNavHost(

InviteContactsScreen(
viewModel = viewModel,
onDoneClicked = dropUnlessResumed {
onDoneClicked = { shareHistory ->
//send invites from the manage group screen
manageGroupMembersViewModel.onContactSelected(viewModel.currentSelected)

manageGroupMembersViewModel.onSendInviteClicked(viewModel.currentSelected, shareHistory)
handleBack()
},
onBack = dropUnlessResumed {
handleBack()
},
banner = {}
forCommunity = false
)
}

// Invite Contacts to community
horizontalSlideComposable<RouteInviteToCommunity> { backStackEntry ->
val viewModel =
hiltViewModel<SelectContactsViewModel, SelectContactsViewModel.Factory> { factory ->
hiltViewModel<InviteMembersViewModel, InviteMembersViewModel.Factory> { factory ->
factory.create()
}

Expand All @@ -252,10 +316,94 @@ fun ConversationSettingsNavHost(

// clear selected contacts
viewModel.clearSelection()
handleBack()
},
onBack = dropUnlessResumed {
handleBack()
},
forCommunity = true
)
}

// Invite contacts using Account ID
horizontalSlideComposable<RouteInviteAccountIdToGroup> { backStackEntry ->
val data: RouteInviteAccountIdToGroup = backStackEntry.toRoute()

val viewModel =
hiltViewModel<InviteMembersViewModel, InviteMembersViewModel.Factory> { factory ->
factory.create(
groupAddress = data.groupAddress,
excludingAccountIDs = data.excludingAccountIDs.map(Address::fromSerialized).toSet()
)
}

val newMessageViewModel = hiltViewModel<NewMessageViewModel>()
val uiState by newMessageViewModel.state.collectAsState(State())

// grab a hold of manage group's VM
val parentEntry = remember(backStackEntry) {
navController.getBackStackEntry(
RouteManageMembers(data.groupAddress)
)
}

val manageGroupMembersViewModel: ManageGroupMembersViewModel = hiltViewModel(parentEntry)

LaunchedEffect(Unit) {
newMessageViewModel.success.collect { success ->
viewModel.sendCommand(
InviteMembersViewModel.Commands.HandleAccountId(
address = success.address
)
)
}
}

InviteAccountIdScreen(
viewModel = viewModel,
state = uiState,
qrErrors = newMessageViewModel.qrErrors,
callbacks = newMessageViewModel,
onBack = { handleBack() },
onHelp = { newMessageViewModel.onCommand(NewMessageViewModel.Commands.ShowUrlDialog) },
onDismissHelpDialog = {
newMessageViewModel.onCommand(NewMessageViewModel.Commands.DismissUrlDialog)
},
onSendInvite = { shareHistory ->
manageGroupMembersViewModel.onCommand(
ManageGroupMembersViewModel.Commands.SendInvites(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is SendInvites part of this viewmodel?
Shouldn't
val manageGroupMembersViewModel: ManageGroupMembersViewModel = hiltViewModel(parentEntry)
be removed here? And SendInvites be moved to the appropriate viewmodel?
Or is SendInvites used somewhere as well? I think the relationship between all those viewmodels need to be really clear and solid

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it similar to how InviteContactsScreen sends the invite since it is the same with only a difference in how to get the Address. InviteContactsScreen does not use onCommand though, maybe I can update this one to make it uniform, or should I change it completely and add the same functions into InviteMembersViewModel?

address = viewModel.currentSelected,
shareHistory = shareHistory
)
)
handleBack()
},
)
}

// Promote Members to group Admin
horizontalSlideComposable<RoutePromoteMembers> { backStackEntry ->
val data: RoutePromoteMembers = backStackEntry.toRoute()

val viewModel =
hiltViewModel<PromoteMembersViewModel, PromoteMembersViewModel.Factory> { factory ->
factory.create(groupAddress = data.groupAddress)
}

val parentEntry = remember(backStackEntry) {
navController.previousBackStackEntry ?: error("RouteManageAdmin not in backstack")
}
val manageGroupAdminsViewModel: ManageGroupAdminsViewModel = hiltViewModel(parentEntry)

PromoteMembersScreen(
viewModel = viewModel,
onBack = dropUnlessResumed {
handleBack()
},
onPromoteClicked = { selectedMembers ->
manageGroupAdminsViewModel.onSendPromotionsClicked(selectedMembers)
handleBack()
}
)
}

Expand Down
Loading