Skip to content
Open
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 @@ -22,6 +22,7 @@ import com.wire.android.di.KaliumCoreLogic
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.meeting.CreateNewMeetingUseCase
import com.wire.kalium.logic.feature.meeting.GetNextMeetingOccurrenceUseCase
import com.wire.kalium.logic.feature.meeting.GetPaginatedMeetingOccurrencesUseCase
import com.wire.kalium.logic.feature.meeting.MeetingScope
import com.wire.kalium.logic.feature.meeting.ObserveMeetingOccurrenceUseCase
Expand Down Expand Up @@ -52,4 +53,12 @@ class MeetingModule {
@Provides
fun provideCreateNewMeetingUseCase(meetingScope: MeetingScope): CreateNewMeetingUseCase =
meetingScope.createNewMeeting

@Provides
fun provideUpdateMeetingUseCase(meetingScope: MeetingScope): com.wire.kalium.logic.feature.meeting.UpdateMeetingUseCase =
meetingScope.updateMeeting

@Provides
fun provideGetNextMeetingOccurrenceUseCase(meetingScope: MeetingScope): GetNextMeetingOccurrenceUseCase =
meetingScope.getNextMeetingOccurrence
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ fun MeetingsScreen(
)
)
},
editMeeting = { meetingId ->
homeStateHolder.navigator.navigate(NavigationCommand(NewMeetingScreenDestination(NewMeetingType.Edit(meetingId))))
},
)

viewModel.callManager.actions.HandleActions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.wire.android.model.ImageAsset
import com.wire.android.model.UserAvatarData
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.meeting.Meeting
import com.wire.kalium.logic.data.meeting.MeetingOccurrence
import kotlinx.collections.immutable.toImmutableList
import kotlinx.datetime.Instant
Expand All @@ -35,7 +36,7 @@ fun MeetingOccurrence.toMeetingItem(time: Instant, ongoingCallStatus: MeetingIte
meetingId = meeting.meetingId,
conversationId = meeting.conversationId,
belongingType = toBelongingType(),
repeatingInterval = meeting.recurrence?.let { MeetingItem.RepeatingInterval(it.frequency, it.interval.toInt()) },
repeatingInterval = meeting.recurrence?.toRepeatingInterval(),
title = meeting.title,
status = when {
occurrenceStartTime > time -> Status.Scheduled(
Expand All @@ -57,6 +58,8 @@ fun MeetingOccurrence.toMeetingItem(time: Instant, ongoingCallStatus: MeetingIte
selfRole = selfRole.toItemSelfRole()
)

fun Meeting.Recurrence.toRepeatingInterval(): MeetingItem.RepeatingInterval = MeetingItem.RepeatingInterval(frequency, interval.toInt())

fun MeetingOccurrence.SelfRole.toItemSelfRole(): MeetingItem.SelfRole = when (this) {
MeetingOccurrence.SelfRole.Creator -> MeetingItem.SelfRole.Creator
MeetingOccurrence.SelfRole.Member -> MeetingItem.SelfRole.Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.ui.UIText
import com.wire.android.util.ui.rememberLazyListStateProvider
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.MeetingId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch
Expand All @@ -62,6 +63,7 @@ fun AllMeetingsScreen(
startCall: (conversationId: ConversationId) -> Unit = {},
joinCall: (conversationId: ConversationId) -> Unit = {},
returnToCall: (conversationId: ConversationId) -> Unit = {},
editMeeting: (meetingId: MeetingId) -> Unit = {},
) {
Column(
modifier = Modifier.fillMaxSize()
Expand Down Expand Up @@ -109,7 +111,7 @@ fun AllMeetingsScreen(
)
}

MeetingOptionsModalSheetLayout(sheetState = meetingOptionsSheetState)
MeetingOptionsModalSheetLayout(sheetState = meetingOptionsSheetState, editMeeting = editMeeting)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ import com.wire.android.feature.meetings.ui.create.NewMeetingViewModelImpl
import com.wire.android.feature.meetings.ui.list.MeetingListViewModelImpl
import com.wire.android.feature.meetings.ui.options.MeetingOptionsMenuViewModelImpl
import com.wire.android.feature.meetings.ui.usecase.GetPaginatedFlowOfMeetingsUseCase
import com.wire.android.mapper.ContactMapper
import com.wire.android.util.CurrentTimeProvider
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.feature.call.usecase.ObserveActiveCallsUseCase
import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase
import com.wire.kalium.logic.feature.meeting.CreateNewMeetingUseCase
import com.wire.kalium.logic.feature.meeting.DeleteMeetingUseCase
import com.wire.kalium.logic.feature.meeting.GetNextMeetingOccurrenceUseCase
import com.wire.kalium.logic.feature.meeting.ObserveMeetingOccurrenceUseCase
import com.wire.kalium.logic.feature.meeting.UpdateMeetingUseCase
import dev.zacsweers.metro.Inject

@Suppress("LongParameterList")
class MeetingsViewModelFactory @Inject constructor(
private val currentTimeProvider: CurrentTimeProvider,
private val dispatcher: DispatcherProvider,
Expand All @@ -38,6 +43,10 @@ class MeetingsViewModelFactory @Inject constructor(
private val observeActiveCalls: ObserveActiveCallsUseCase,
private val deleteMeetingUseCase: DeleteMeetingUseCase,
private val createNewMeeting: CreateNewMeetingUseCase,
private val updateMeeting: UpdateMeetingUseCase,
private val getNextMeetingOccurrence: GetNextMeetingOccurrenceUseCase,
private val observeConversationMembers: ObserveConversationMembersUseCase,
private val contactMapper: ContactMapper,
) {
internal fun meetingListViewModel(type: MeetingsTabItem) = MeetingListViewModelImpl(
type = type,
Expand All @@ -48,13 +57,18 @@ class MeetingsViewModelFactory @Inject constructor(
)

internal fun meetingOptionsMenuViewModel() = MeetingOptionsMenuViewModelImpl(
currentTimeProvider = currentTimeProvider,
observeMeetingOccurrenceUseCase = observeMeetingOccurrence,
deleteMeetingUseCase = deleteMeetingUseCase,
)

internal fun newMeetingViewModel(savedStateHandle: SavedStateHandle) = NewMeetingViewModelImpl(
savedStateHandle = savedStateHandle,
currentTimeProvider = currentTimeProvider,
createNewMeeting = createNewMeeting
createNewMeeting = createNewMeeting,
updateMeeting = updateMeeting,
getNextMeetingOccurrence = getNextMeetingOccurrence,
observeConversationMembers = observeConversationMembers,
contactMapper = contactMapper,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fun NewMeetingErrorDialog(
val (titleResId, descriptionResId) = when (type) {
NewMeetingType.MeetNow -> R.string.new_meeting_now_failure_title to R.string.new_meeting_now_failure_description
NewMeetingType.Schedule -> R.string.new_meeting_schedule_failure_title to R.string.new_meeting_schedule_failure_description
is NewMeetingType.Edit -> R.string.new_meeting_edit_failure_title to R.string.new_meeting_edit_failure_description
}
WireDialog(
title = stringResource(titleResId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,46 @@
*/
package com.wire.android.feature.meetings.ui.create

import android.os.Parcelable
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.wire.android.feature.meetings.R
import kotlinx.parcelize.Parcelize
import com.wire.kalium.logic.data.id.MeetingId
import kotlinx.serialization.Serializable
import com.wire.android.ui.common.R as CommonUiR

data class NewMeetingNavArgs(
val type: NewMeetingType
)

@Parcelize
enum class NewMeetingType(
@StringRes val title: Int,
@StringRes val action: Int,
@DrawableRes val icon: Int,
) : Parcelable {
MeetNow(
title = R.string.new_meeting_now,
action = R.string.new_meeting_action_start,
icon = CommonUiR.drawable.ic_video_call,
),
Schedule(
title = R.string.new_meeting_schedule,
action = R.string.new_meeting_action_schedule,
icon = CommonUiR.drawable.ic_calendar,
)
@Serializable
sealed interface NewMeetingType {
@get:StringRes
val title: Int

@get:StringRes
val action: Int

@get:DrawableRes
val icon: Int

@Serializable
data object MeetNow : NewMeetingType {
override val title = R.string.new_meeting_now
override val action = R.string.new_meeting_action_start
override val icon = CommonUiR.drawable.ic_video_call
}

@Serializable
data object Schedule : NewMeetingType {
override val title = R.string.new_meeting_schedule
override val action = R.string.new_meeting_action_schedule
override val icon = CommonUiR.drawable.ic_calendar
}

@Serializable
data class Edit(val id: MeetingId) : NewMeetingType {
override val title = R.string.new_meeting_edit
override val action = R.string.new_meeting_action_edit
override val icon = CommonUiR.drawable.ic_calendar
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -77,6 +78,9 @@ import com.wire.android.navigation.annotation.features.meetings.WireNewMeetingDe
import com.wire.android.navigation.style.PopUpNavigationAnimation
import com.wire.android.ui.common.HandleActions
import com.wire.android.ui.common.VisibilityState
import com.wire.android.ui.common.WireDialog
import com.wire.android.ui.common.WireDialogButtonProperties
import com.wire.android.ui.common.WireDialogButtonType
import com.wire.android.ui.common.WireDropDown
import com.wire.android.ui.common.animation.ShakeAnimation
import com.wire.android.ui.common.button.WireButtonState
Expand All @@ -87,6 +91,7 @@ import com.wire.android.ui.common.datetime.WireDatePickerDialog
import com.wire.android.ui.common.datetime.WireTimePickerDialog
import com.wire.android.ui.common.datetime.asTimePickerResult
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.progress.WireCircularProgressIndicator
import com.wire.android.ui.common.rememberTopBarElevationState
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.spacers.VerticalSpace
Expand All @@ -107,6 +112,7 @@ import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.CurrentTimeProvider
import com.wire.android.util.DateAndTimeParsers
import com.wire.android.util.EMPTY
import com.wire.kalium.logic.data.id.MeetingId
import com.wire.kalium.logic.data.user.ConnectionState
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableList
Expand Down Expand Up @@ -138,18 +144,22 @@ fun NewMeetingScreen(
onParticipantsClicked = {
navigator.navigate(NavigationCommand(NewMeetingParticipantsScreenDestination))
},
onCreateClicked = newMeetingViewModel::createMeeting,
onCreateClicked = newMeetingViewModel::submitCreation,
onUpdateClicked = newMeetingViewModel::submitUpdate,
onStartTimeChanged = newMeetingViewModel::updateStartTime,
onEndTimeChanged = newMeetingViewModel::updateEndTime,
onRepeatingIntervalChanged = newMeetingViewModel::updateRepeatingInterval,
)

if (newMeetingViewModel.state.creationError != null) {
if (newMeetingViewModel.state.submitError != null) {
NewMeetingErrorDialog(
type = newMeetingViewModel.type,
onDismiss = newMeetingViewModel::dismissCreationError
)
}
if (newMeetingViewModel.state.initialLoading == NewMeetingState.InitialLoadingState.Error) {
FailedToLoadEditMeetingDataError(navigateBack = navigator::navigateBack)
}

HandleActions(newMeetingViewModel.actions) { action ->
when (action) {
Expand All @@ -167,6 +177,7 @@ fun NewMeetingContent(
onBackPressed: () -> Unit = {},
onParticipantsClicked: () -> Unit = {},
onCreateClicked: () -> Unit = {},
onUpdateClicked: () -> Unit = {},
onStartTimeChanged: (startTime: Instant) -> Unit = {},
onEndTimeChanged: (endTime: Instant) -> Unit = {},
onRepeatingIntervalChanged: (interval: MeetingItem.RepeatingInterval?) -> Unit = {},
Expand Down Expand Up @@ -202,7 +213,7 @@ fun NewMeetingContent(
titleError = state.titleError,
readOnly = state.isSubmitting,
)
if (type == NewMeetingType.Schedule) {
if (type != NewMeetingType.MeetNow) {
VerticalSpace.x24()
TimeInput(
time = state.startTime,
Expand Down Expand Up @@ -233,15 +244,29 @@ fun NewMeetingContent(
onClick = onParticipantsClicked,
)
}
if (state.isSubmitting) {
if (state.isSubmitting || state.initialLoading != NewMeetingState.InitialLoadingState.Loaded) {
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures { /* consume the touch while submitting data */ }
detectTapGestures { /* consume the touch while submitting or loading data */ }
},
)
}
AnimatedVisibility(state.initialLoading != NewMeetingState.InitialLoadingState.Loaded) {
Box(
modifier = Modifier
.fillMaxSize()
.background(colorsScheme().background),
contentAlignment = Alignment.Center
) {
WireCircularProgressIndicator(
size = dimensions().spacing32x,
strokeWidth = dimensions().spacing4x,
progressColor = MaterialTheme.wireColorScheme.onBackground,
)
}
}
},
bottomBar = {
Surface(
Expand All @@ -260,7 +285,10 @@ fun NewMeetingContent(
},
state = if (state.continueButtonEnabled) WireButtonState.Default else WireButtonState.Disabled,
loading = state.isSubmitting,
onClick = onCreateClicked,
onClick = when (type) {
is NewMeetingType.Edit -> onUpdateClicked
NewMeetingType.MeetNow, NewMeetingType.Schedule -> onCreateClicked
},
modifier = Modifier
.fillMaxWidth()
.padding(dimensions().spacing16x),
Expand Down Expand Up @@ -563,6 +591,21 @@ private fun RepeatingIntervalDropDown(
}
}

@Composable
private fun FailedToLoadEditMeetingDataError(navigateBack: () -> Unit) {
WireDialog(
title = stringResource(R.string.new_meeting_edit_init_failure_title),
text = stringResource(R.string.new_meeting_edit_init_failure_description),
onDismiss = navigateBack,
buttonsHorizontalAlignment = false,
optionButton1Properties = WireDialogButtonProperties(
onClick = navigateBack,
text = stringResource(commonR.string.label_ok),
type = WireDialogButtonType.Primary,
),
)
}

@PreviewMultipleThemes
@Composable
fun PreviewNewMeetingScreen_MeetNow() = WireTheme {
Expand All @@ -572,6 +615,7 @@ fun PreviewNewMeetingScreen_MeetNow() = WireTheme {
state = NewMeetingState.initialState(CurrentTimeProvider.Preview).copy(
confirmedContacts = buildContacts(names.size),
continueButtonEnabled = true,
initialLoading = NewMeetingState.InitialLoadingState.Loaded,
),
)
}
Expand All @@ -586,6 +630,22 @@ fun PreviewNewMeetingScreen_Schedule() = WireTheme {
startTime = getNextFullHour(CurrentTimeProvider.Preview.invoke()),
endTime = getNextFullHour(CurrentTimeProvider.Preview.invoke()).plus(1.hours),
repeatingInterval = MeetingItem.RepeatingInterval.Supported.first(),
initialLoading = NewMeetingState.InitialLoadingState.Loaded,
),
)
}

@PreviewMultipleThemes
@Composable
fun PreviewNewMeetingScreen_Edit() = WireTheme {
NewMeetingContent(
titleState = rememberTextFieldState(),
type = NewMeetingType.Edit(MeetingId("meetingId", "domain")),
state = NewMeetingState.initialState(CurrentTimeProvider.Preview).copy(
startTime = getNextFullHour(CurrentTimeProvider.Preview.invoke()),
endTime = getNextFullHour(CurrentTimeProvider.Preview.invoke()).plus(1.hours),
repeatingInterval = MeetingItem.RepeatingInterval.Supported.first(),
initialLoading = NewMeetingState.InitialLoadingState.Loaded,
),
)
}
Expand Down
Loading