Add additional call links moderation ui.

This commit is contained in:
Alex Hart
2023-08-08 15:10:47 -03:00
parent 7c209db146
commit 30d0b6fd0e
20 changed files with 531 additions and 105 deletions

View File

@@ -11,7 +11,9 @@ import org.signal.ringrtc.GroupCall
import org.signal.ringrtc.PeekInfo
import org.thoughtcrime.securesms.database.CallLinkTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.events.CallParticipant
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState
import org.whispersystems.signalservice.api.push.ServiceId
@@ -58,18 +60,19 @@ class CallLinkConnectedActionProcessor(
.build()
}
override fun handleSetCallLinkJoinRequestAccepted(currentState: WebRtcServiceState, participant: Recipient): WebRtcServiceState {
override fun handleSetCallLinkJoinRequestAccepted(currentState: WebRtcServiceState, participant: RecipientId): WebRtcServiceState {
Log.i(tag, "handleSetCallLinkJoinRequestAccepted():")
val groupCall: GroupCall = currentState.callInfoState.requireGroupCall()
val recipient = Recipient.resolved(participant)
return try {
groupCall.approveUser(participant.requireAci().rawUuid)
groupCall.approveUser(recipient.requireAci().rawUuid)
currentState
.builder()
.changeCallInfoState()
.setPendingParticipantApproved(participant)
.setPendingParticipantApproved(recipient)
.build()
} catch (e: CallException) {
Log.w(tag, "Failed to approve user.", e)
@@ -78,22 +81,51 @@ class CallLinkConnectedActionProcessor(
}
}
override fun handleSetCallLinkJoinRequestRejected(currentState: WebRtcServiceState, participant: Recipient): WebRtcServiceState {
override fun handleSetCallLinkJoinRequestRejected(currentState: WebRtcServiceState, participant: RecipientId): WebRtcServiceState {
Log.i(tag, "handleSetCallLinkJoinRequestRejected():")
val groupCall: GroupCall = currentState.callInfoState.requireGroupCall()
val recipient = Recipient.resolved(participant)
return try {
groupCall.denyUser(participant.requireAci().rawUuid)
groupCall.denyUser(recipient.requireAci().rawUuid)
currentState
.builder()
.changeCallInfoState()
.setPendingParticipantRejected(participant)
.setPendingParticipantRejected(recipient)
.build()
} catch (e: CallException) {
Log.w(tag, "Failed to deny user.", e)
currentState
}
}
override fun handleRemoveFromCallLink(currentState: WebRtcServiceState, participant: CallParticipant): WebRtcServiceState {
Log.i(tag, "handleRemoveFromCallLink():")
val groupCall: GroupCall = currentState.callInfoState.requireGroupCall()
try {
groupCall.removeClient(participant.callParticipantId.demuxId)
} catch (e: CallException) {
Log.w(tag, "Failed to remove user.", e)
}
return currentState
}
override fun handleBlockFromCallLink(currentState: WebRtcServiceState, participant: CallParticipant): WebRtcServiceState {
Log.i(tag, "handleBlockFromCallLink():")
val groupCall: GroupCall = currentState.callInfoState.requireGroupCall()
try {
groupCall.blockClient(participant.callParticipantId.demuxId)
} catch (e: CallException) {
Log.w(tag, "Failed to block user.", e)
}
return currentState
}
}

View File

@@ -21,12 +21,17 @@ data class PendingParticipantCollection(
private val nowProvider: () -> Duration = { System.currentTimeMillis().milliseconds }
) {
companion object {
private val MAX_DENIALS = 2
}
/**
* Creates a new collection with the given recipients applied to it with the following rules:
*
* 1. If the recipient is already in the collection, ignore it
* 1. Otherwise, insert the recipient at the end of the colleciton in the pending state
* 1. Otherwise, insert the recipient at the end of the collection in the pending state
* 1. Any recipients in the resulting collection that are [State.PENDING] and NOT in the passed recipient list are removed.
* 1. Any recipients in the resulting collection that are [State.DENIED] and have a denial count less than [MAX_DENIALS] is moved to [State.PENDING]
*/
fun withRecipients(recipients: List<Recipient>): PendingParticipantCollection {
val now = nowProvider()
@@ -38,25 +43,36 @@ data class PendingParticipantCollection(
)
}
val recipientIds = recipients.map { it.id }
val newEntryMap = (participantMap + newEntries).filterNot { it.value.state == State.PENDING && it.key !in recipientIds }
val submittedIdSet = recipients.map { it.id }.toSet()
val newEntryMap = (participantMap + newEntries)
.filterNot { it.value.state == State.PENDING && it.key !in submittedIdSet }
.mapValues {
if (it.value.state == State.DENIED && it.key in submittedIdSet && it.value.denialCount < MAX_DENIALS) {
it.value.copy(state = State.PENDING, stateChangeAt = now)
} else {
it.value
}
}
return copy(participantMap = newEntryMap)
}
/**
* Creates a new collection with the given recipient marked as [State.APPROVED]
* Creates a new collection with the given recipient marked as [State.APPROVED].
* Resets the denial count for that recipient.
*/
fun withApproval(recipient: Recipient): PendingParticipantCollection {
val now = nowProvider()
val entry = Entry(
recipient = recipient,
state = State.APPROVED,
stateChangeAt = now
)
val entry = participantMap[recipient.id] ?: return this
return copy(
participantMap = participantMap + (recipient.id to entry)
participantMap = participantMap + (
recipient.id to entry.copy(
denialCount = 0,
state = State.APPROVED,
stateChangeAt = now
)
)
)
}
@@ -65,14 +81,16 @@ data class PendingParticipantCollection(
*/
fun withDenial(recipient: Recipient): PendingParticipantCollection {
val now = nowProvider()
val entry = Entry(
recipient = recipient,
state = State.DENIED,
stateChangeAt = now
)
val entry = participantMap[recipient.id] ?: return this
return copy(
participantMap = participantMap + (recipient.id to entry)
participantMap = participantMap + (
recipient.id to entry.copy(
denialCount = entry.denialCount + 1,
state = State.DENIED,
stateChangeAt = now
)
)
)
}
@@ -103,7 +121,8 @@ data class PendingParticipantCollection(
data class Entry(
val recipient: Recipient,
val state: State,
val stateChangeAt: Duration
val stateChangeAt: Duration,
val denialCount: Int = 0
)
/**

View File

@@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.groups.GroupId;
@@ -354,19 +355,19 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
process((s, p) -> p.handleDropCall(s, callId));
}
public void setCallLinkJoinRequestAccepted(@NonNull Recipient participant) {
public void setCallLinkJoinRequestAccepted(@NonNull RecipientId participant) {
process((s, p) -> p.handleSetCallLinkJoinRequestAccepted(s, participant));
}
public void setCallLinkJoinRequestRejected(@NonNull Recipient participant) {
public void setCallLinkJoinRequestRejected(@NonNull RecipientId participant) {
process((s, p) -> p.handleSetCallLinkJoinRequestRejected(s, participant));
}
public void removeFromCallLink(@NonNull Recipient participant) {
public void removeFromCallLink(@NonNull CallParticipant participant) {
process((s, p) -> p.handleRemoveFromCallLink(s, participant));
}
public void blockFromCallLink(@NonNull Recipient participant) {
public void blockFromCallLink(@NonNull CallParticipant participant) {
process((s, p) -> p.handleBlockFromCallLink(s, participant));
}

View File

@@ -877,25 +877,25 @@ public abstract class WebRtcActionProcessor {
//region Call Links
protected @NonNull WebRtcServiceState handleSetCallLinkJoinRequestAccepted(@NonNull WebRtcServiceState currentState, @NonNull Recipient participant) {
protected @NonNull WebRtcServiceState handleSetCallLinkJoinRequestAccepted(@NonNull WebRtcServiceState currentState, @NonNull RecipientId participant) {
Log.i(tag, "handleSetCallLinkJoinRequestAccepted not processed");
return currentState;
}
protected @NonNull WebRtcServiceState handleSetCallLinkJoinRequestRejected(@NonNull WebRtcServiceState currentState, @NonNull Recipient participant) {
protected @NonNull WebRtcServiceState handleSetCallLinkJoinRequestRejected(@NonNull WebRtcServiceState currentState, @NonNull RecipientId participant) {
Log.i(tag, "handleSetCallLinkJoinRequestRejected not processed");
return currentState;
}
protected @NonNull WebRtcServiceState handleRemoveFromCallLink(@NonNull WebRtcServiceState currentState, @NonNull Recipient participant) {
protected @NonNull WebRtcServiceState handleRemoveFromCallLink(@NonNull WebRtcServiceState currentState, @NonNull CallParticipant participant) {
Log.i(tag, "handleRemoveFromCallLink not processed");
return currentState;
}
protected @NonNull WebRtcServiceState handleBlockFromCallLink(@NonNull WebRtcServiceState currentState, @NonNull Recipient participant) {
protected @NonNull WebRtcServiceState handleBlockFromCallLink(@NonNull WebRtcServiceState currentState, @NonNull CallParticipant participant) {
Log.i(tag, "handleBlockFromCallLink not processed");
return currentState;