From fa67509ac286caab09151e83c3ae20858c3aae12 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 14 Nov 2019 15:34:50 +0100 Subject: [PATCH 01/19] Room profile: start initializing all the classes + move some packages --- vector/src/main/AndroidManifest.xml | 3 +- .../java/im/vector/riotx/AppStateHandler.kt | 4 +- .../im/vector/riotx/core/di/FragmentModule.kt | 12 +++- .../vector/riotx/core/di/ScreenComponent.kt | 2 +- .../vector/riotx/core/di/VectorComponent.kt | 2 +- .../vector/riotx/core/di/ViewModelModule.kt | 2 +- .../home => core/utils}/UserColor.kt | 2 +- .../createdirect/CreateDirectRoomAction.kt | 2 +- .../createdirect/CreateDirectRoomActivity.kt | 2 +- .../CreateDirectRoomDirectoryUsersFragment.kt | 2 +- .../CreateDirectRoomKnownUsersFragment.kt | 2 +- .../CreateDirectRoomLetterHeaderItem.kt | 2 +- .../CreateDirectRoomSharedAction.kt | 2 +- .../CreateDirectRoomSharedActionViewModel.kt | 2 +- .../createdirect/CreateDirectRoomUserItem.kt | 2 +- .../createdirect/CreateDirectRoomViewModel.kt | 2 +- .../createdirect/CreateDirectRoomViewState.kt | 2 +- .../createdirect/DirectoryUsersController.kt | 2 +- .../createdirect/KnownUsersController.kt | 2 +- .../group => grouplist}/GroupListAction.kt | 3 +- .../group => grouplist}/GroupListFragment.kt | 3 +- .../group => grouplist}/GroupListViewModel.kt | 3 +- .../group => grouplist}/GroupListViewState.kt | 2 +- .../GroupSummaryController.kt | 3 +- .../group => grouplist}/GroupSummaryItem.kt | 3 +- .../SelectedGroupDataSource.kt | 2 +- .../riotx/features/home/AvatarRenderer.kt | 1 + .../features/home/HomeDetailViewModel.kt | 2 +- .../riotx/features/home/HomeDrawerFragment.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 24 +++++--- .../helper/MessageInformationDataFactory.kt | 5 +- .../home/room/list/RoomListFragment.kt | 2 +- .../features/navigation/DefaultNavigator.kt | 7 ++- .../riotx/features/navigation/Navigator.kt | 2 +- .../features/roomprofile/RoomProfileAction.kt | 23 ++++++++ .../roomprofile/RoomProfileActivity.kt | 51 +++++++++++++++++ .../roomprofile/RoomProfileController.kt | 31 ++++++++++ .../roomprofile/RoomProfileFragment.kt | 56 +++++++++++++++++++ .../roomprofile/RoomProfileViewModel.kt | 47 ++++++++++++++++ .../roomprofile/RoomProfileViewState.kt | 27 +++++++++ .../main/res/layout/fragment_room_detail.xml | 1 + .../main/res/layout/fragment_room_profile.xml | 14 +++++ 42 files changed, 318 insertions(+), 47 deletions(-) rename vector/src/main/java/im/vector/riotx/{features/home => core/utils}/UserColor.kt (97%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomAction.kt (95%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomActivity.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomDirectoryUsersFragment.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomKnownUsersFragment.kt (99%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomLetterHeaderItem.kt (96%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomSharedAction.kt (94%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomSharedActionViewModel.kt (94%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomUserItem.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomViewModel.kt (99%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/CreateDirectRoomViewState.kt (96%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/DirectoryUsersController.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home => }/createdirect/KnownUsersController.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupListAction.kt (94%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupListFragment.kt (98%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupListViewModel.kt (99%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupListViewState.kt (95%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupSummaryController.kt (97%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/GroupSummaryItem.kt (97%) rename vector/src/main/java/im/vector/riotx/features/{home/group => grouplist}/SelectedGroupDataSource.kt (95%) create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt create mode 100644 vector/src/main/res/layout/fragment_room_profile.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 0c9bac61a1..6c6e918e1b 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -65,7 +65,7 @@ - + @@ -96,6 +96,7 @@ + { val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID) - ?: return + ?: return val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT) - ?: return + ?: return // TODO check if already reacted with that? roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) } @@ -1176,7 +1181,8 @@ class RoomDetailFragment @Inject constructor( // current user if (composerLayout.composerEditText.text.isNullOrBlank()) { composerLayout.composerEditText.append(Command.EMOTE.command + " ") - composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length ?: 0) + composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length + ?: 0) // vibrate = true } } else { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt index e44e657733..a7e081a165 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited import im.vector.riotx.core.extensions.localDateTime import im.vector.riotx.core.resources.ColorProvider -import im.vector.riotx.features.home.getColorFromUserId +import im.vector.riotx.core.utils.getColorFromUserId import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotx.features.home.room.detail.timeline.item.ReactionInfoData @@ -60,7 +60,8 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses val avatarUrl = event.senderAvatar val memberName = event.getDisambiguatedDisplayName() val formattedMemberName = span(memberName) { - textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId ?: "")) + textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId + ?: "")) } val displayReadMarker = readMarkerVisible && event.hasReadMarker diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index a5e9a7b4bf..9e6d5533f6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -229,7 +229,7 @@ class RoomListFragment @Inject constructor( roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE)) } is RoomListQuickActionsSharedAction.Settings -> { - vectorBaseActivity.notImplemented("Opening room settings") + navigator.openRoomProfile(requireActivity(), quickAction.roomId) } is RoomListQuickActionsSharedAction.Leave -> { AlertDialog.Builder(requireContext()) diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 685fa04fef..8c5fc57e6d 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -26,13 +26,14 @@ import im.vector.riotx.core.utils.toast import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.riotx.features.debug.DebugMenuActivity -import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity +import im.vector.riotx.features.createdirect.CreateDirectRoomActivity import im.vector.riotx.features.home.room.detail.RoomDetailActivity import im.vector.riotx.features.home.room.detail.RoomDetailArgs import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity +import im.vector.riotx.features.roomprofile.RoomProfileActivity import im.vector.riotx.features.settings.VectorSettingsActivity import im.vector.riotx.features.share.SharedData import timber.log.Timber @@ -113,7 +114,7 @@ class DefaultNavigator @Inject constructor() : Navigator { Timber.v("Open user detail $userId") } - override fun openRoomSettings(context: Context, roomId: String) { - Timber.v("Open room settings$roomId") + override fun openRoomProfile(context: Context, roomId: String) { + context.startActivity(RoomProfileActivity.newIntent(context, roomId)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index 83c4f7ce20..987dc6364e 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -51,5 +51,5 @@ interface Navigator { fun openUserDetail(userId: String, context: Context) - fun openRoomSettings(context: Context, roomId: String) + fun openRoomProfile(context: Context, roomId: String) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt new file mode 100644 index 0000000000..979a9d2a57 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class RoomProfileAction: VectorViewModelAction { +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt new file mode 100644 index 0000000000..2c2d2b795d --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import android.content.Context +import android.content.Intent +import im.vector.riotx.R +import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.platform.VectorBaseActivity + +class RoomProfileActivity : VectorBaseActivity() { + + companion object { + + private const val EXTRA_ROOM_PROFILE_ARGS = "EXTRA_ROOM_PROFILE_ARGS" + + fun newIntent(context: Context, roomId: String): Intent { + val roomProfileArgs = RoomProfileArgs(roomId) + return Intent(context, RoomProfileActivity::class.java).apply { + putExtra(EXTRA_ROOM_PROFILE_ARGS, roomProfileArgs) + } + } + } + + override fun getLayoutRes() = R.layout.activity_simple + + override fun initUiAndData() { + if (isFirstCreation()) { + val roomProfileArgs: RoomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) + ?: return + addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) + } + } + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt new file mode 100644 index 0000000000..7ef39131f2 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import com.airbnb.epoxy.TypedEpoxyController +import javax.inject.Inject + +class RoomProfileController @Inject constructor() + : TypedEpoxyController() { + + override fun buildModels(data: RoomProfileViewState?) { + if (data == null) { + return + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt new file mode 100644 index 0000000000..6501bfc2bb --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import com.airbnb.epoxy.EpoxyController +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorBaseFragment +import kotlinx.android.parcel.Parcelize +import javax.inject.Inject + +@Parcelize +data class RoomProfileArgs( + val roomId: String +) : Parcelable + +class RoomProfileFragment @Inject constructor( + private val roomProfileController: RoomProfileController, + val roomProfileViewModelFactory: RoomProfileViewModel.Factory +) : VectorBaseFragment() { + + private val roomProfileArgs: RoomProfileArgs by args() + private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_room_profile + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun invalidate() = withState(roomProfileViewModel) { + roomProfileController.setData(it) + } + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt new file mode 100644 index 0000000000..ca796d1e2e --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.riotx.core.platform.VectorViewModel + +class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomProfileViewState): RoomProfileViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomProfileViewState): RoomProfileViewModel? { + val fragment: RoomProfileFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.roomProfileViewModelFactory.create(state) + } + } + + override fun handle(action: RoomProfileAction) { + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt new file mode 100644 index 0000000000..a54fc2c8fb --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roomprofile + +import com.airbnb.mvrx.MvRxState + +data class RoomProfileViewState( + val roomId: String +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) +} diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml index 6661674edb..90455f0572 100644 --- a/vector/src/main/res/layout/fragment_room_detail.xml +++ b/vector/src/main/res/layout/fragment_room_detail.xml @@ -18,6 +18,7 @@ app:layout_constraintTop_toTopOf="parent"> diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml new file mode 100644 index 0000000000..2fe7ce2b2f --- /dev/null +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From 0edc953a23d183ad462d2acd69427cfc45d73ca4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 14 Nov 2019 20:13:59 +0100 Subject: [PATCH 02/19] Room profile: start creating epoxy items and implementing UI --- ...omSheetItemSeparator.kt => DividerItem.kt} | 9 +- .../core/epoxy/profiles/ProfileItemAction.kt | 62 +++++++++++ .../core/epoxy/profiles/ProfileItemSection.kt | 45 ++++++++ .../action/MessageActionsEpoxyController.kt | 7 +- .../RoomListQuickActionsEpoxyController.kt | 6 +- .../roomprofile/RoomProfileActivity.kt | 7 +- .../roomprofile/RoomProfileController.kt | 55 ++++++++++ .../roomprofile/RoomProfileFragment.kt | 36 ++++++- .../roomprofile/RoomProfileViewModel.kt | 20 +++- .../roomprofile/RoomProfileViewState.kt | 6 +- .../main/res/layout/fragment_room_profile.xml | 102 ++++++++++++++++-- ...tom_sheet_divider.xml => item_divider.xml} | 0 .../main/res/layout/item_profile_action.xml | 83 ++++++++++++++ .../main/res/layout/item_profile_section.xml | 17 +++ 14 files changed, 428 insertions(+), 27 deletions(-) rename vector/src/main/java/im/vector/riotx/core/epoxy/{bottomsheet/BottomSheetItemSeparator.kt => DividerItem.kt} (69%) create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt rename vector/src/main/res/layout/{item_bottom_sheet_divider.xml => item_divider.xml} (100%) create mode 100644 vector/src/main/res/layout/item_profile_action.xml create mode 100644 vector/src/main/res/layout/item_profile_section.xml diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/bottomsheet/BottomSheetItemSeparator.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/DividerItem.kt similarity index 69% rename from vector/src/main/java/im/vector/riotx/core/epoxy/bottomsheet/BottomSheetItemSeparator.kt rename to vector/src/main/java/im/vector/riotx/core/epoxy/DividerItem.kt index fddf507bf9..ab849a6909 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/bottomsheet/BottomSheetItemSeparator.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/DividerItem.kt @@ -12,17 +12,14 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ -package im.vector.riotx.core.epoxy.bottomsheet +package im.vector.riotx.core.epoxy import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R -import im.vector.riotx.core.epoxy.VectorEpoxyHolder -import im.vector.riotx.core.epoxy.VectorEpoxyModel -@EpoxyModelClass(layout = R.layout.item_bottom_sheet_divider) -abstract class BottomSheetItemSeparator : VectorEpoxyModel() { +@EpoxyModelClass(layout = R.layout.item_divider) +abstract class DividerItem : VectorEpoxyModel() { class Holder : VectorEpoxyHolder() } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt new file mode 100644 index 0000000000..a69cf96243 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.core.epoxy.profiles + +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.epoxy.VectorEpoxyModel +import im.vector.riotx.core.extensions.setTextOrHide + +@EpoxyModelClass(layout = R.layout.item_profile_action) +abstract class ProfileItemAction : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var title: String + @EpoxyAttribute + var subtitle: String? = null + @EpoxyAttribute + var iconRes: Int = 0 + @EpoxyAttribute + var editable: Boolean = true + + override fun bind(holder: Holder) { + super.bind(holder) + holder.editable.isVisible = editable + holder.title.text = title + holder.subtitle.setTextOrHide(subtitle) + if (iconRes != 0) { + holder.icon.setImageResource(iconRes) + holder.icon.isVisible = true + } else { + holder.icon.isVisible = false + } + } + + + class Holder : VectorEpoxyHolder() { + val icon by bind(R.id.actionIcon) + val title by bind(R.id.actionTitle) + val subtitle by bind(R.id.actionSubtitle) + val editable by bind(R.id.actionEditable) + } + +} diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt new file mode 100644 index 0000000000..24a9d1a57b --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.core.epoxy.profiles + +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.epoxy.VectorEpoxyModel + +@EpoxyModelClass(layout = R.layout.item_profile_section) +abstract class ProfileItemSection: VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var title: String + + override fun bind(holder: Holder) { + super.bind(holder) + holder.sectionView.text = title + } + + + + class Holder : VectorEpoxyHolder() { + val sectionView by bind(R.id.itemProfileSectionView) + } + + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index b561a6df3c..3ed5c2e6a5 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -21,6 +21,7 @@ import com.airbnb.mvrx.Success import im.vector.riotx.EmojiCompatFontProvider import im.vector.riotx.R import im.vector.riotx.core.epoxy.bottomsheet.* +import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.features.home.AvatarRenderer import javax.inject.Inject @@ -68,7 +69,7 @@ class MessageActionsEpoxyController @Inject constructor(private val stringProvid // Quick reactions if (state.canReact() && state.quickStates is Success) { // Separator - bottomSheetItemSeparator { + dividerItem { id("reaction_separator") } @@ -86,14 +87,14 @@ class MessageActionsEpoxyController @Inject constructor(private val stringProvid } // Separator - bottomSheetItemSeparator { + dividerItem { id("actions_separator") } // Action state.actions()?.forEachIndexed { index, action -> if (action is EventSharedAction.Separator) { - bottomSheetItemSeparator { + dividerItem { id("separator_$index") } } else { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 2e17464cc6..8562ec4153 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -20,7 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetItemAction import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetItemRoomPreview -import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetItemSeparator +import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.features.home.AvatarRenderer import javax.inject.Inject @@ -46,7 +46,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar } // Notifications - bottomSheetItemSeparator { + dividerItem { id("notifications_separator") } @@ -57,7 +57,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) // Leave - bottomSheetItemSeparator { + dividerItem { id("leave_separator") } RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index 2c2d2b795d..e57debee7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -19,11 +19,13 @@ package im.vector.riotx.features.roomprofile import android.content.Context import android.content.Intent +import androidx.appcompat.widget.Toolbar import im.vector.riotx.R import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity -class RoomProfileActivity : VectorBaseActivity() { +class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { @@ -47,5 +49,8 @@ class RoomProfileActivity : VectorBaseActivity() { } } + override fun configure(toolbar: Toolbar) { + configureToolbar(toolbar) + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index 7ef39131f2..a5f0682d11 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -18,6 +18,10 @@ package im.vector.riotx.features.roomprofile import com.airbnb.epoxy.TypedEpoxyController +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.dividerItem +import im.vector.riotx.core.epoxy.profiles.profileItemAction +import im.vector.riotx.core.epoxy.profiles.profileItemSection import javax.inject.Inject class RoomProfileController @Inject constructor() @@ -27,5 +31,56 @@ class RoomProfileController @Inject constructor() if (data == null) { return } + + profileItemSection { + id("section_security") + title("Security") + } + + profileItemAction { + id("action_learn_more") + title("Learn more") + editable(true) + subtitle("Messages in this room are not end-to-end encrypted.") + } + + dividerItem{ + id("action_learn_more_divider") + } + + profileItemSection { + id("section_options") + title("Options") + } + + profileItemAction { + iconRes(R.drawable.ic_person_outline_black) + id("action_member_list") + title("88 people") + editable(true) + } + + dividerItem{ + id("action_member_list_divider") + } + + profileItemAction { + iconRes(R.drawable.ic_attachment) + id("action_files") + title("12 files") + editable(true) + } + + dividerItem{ + id("action_files_divider") + } + + profileItemAction { + iconRes(R.drawable.ic_settings_x) + id("action_settings") + title("Room settings") + editable(true) + } + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 6501bfc2bb..ff0b8be634 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -20,13 +20,18 @@ package im.vector.riotx.features.roomprofile import android.os.Bundle import android.os.Parcelable import android.view.View -import com.airbnb.epoxy.EpoxyController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.features.home.AvatarRenderer import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_room_profile.* +import timber.log.Timber import javax.inject.Inject @Parcelize @@ -36,6 +41,7 @@ data class RoomProfileArgs( class RoomProfileFragment @Inject constructor( private val roomProfileController: RoomProfileController, + private val avatarRenderer: AvatarRenderer, val roomProfileViewModelFactory: RoomProfileViewModel.Factory ) : VectorBaseFragment() { @@ -46,10 +52,34 @@ class RoomProfileFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setupToolbar(roomProfileToolbar) + setupRecyclerView() } - override fun invalidate() = withState(roomProfileViewModel) { - roomProfileController.setData(it) + private fun setupRecyclerView() { + roomProfileRecyclerView.setHasFixedSize(true) + roomProfileRecyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) + roomProfileRecyclerView.adapter = roomProfileController.adapter + } + + override fun onDestroyView() { + super.onDestroyView() + roomProfileRecyclerView.adapter = null + } + + override fun invalidate() = withState(roomProfileViewModel) { state -> + state.roomSummary()?.let { + if (it.membership.isLeft()) { + Timber.w("The room has been left") + activity?.finish() + } else { + roomProfileNameView.text = it.displayName + roomProfileIdView.text = it.roomId + roomProfileTopicView.setTextOrHide(it.topic) + avatarRenderer.render(it, roomProfileAvatarView) + } + } + roomProfileController.setData(state) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index ca796d1e2e..d81c748bdd 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -22,9 +22,13 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.rx.rx +import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel -class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState) +class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, + private val session: Session) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -41,6 +45,20 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } } + private val room = session.getRoom(initialState.roomId)!! + + init { + observeRoomSummary() + } + + private fun observeRoomSummary() { + room.rx().liveRoomSummary() + .unwrap() + .execute { + copy(roomSummary = it) + } + } + override fun handle(action: RoomProfileAction) { } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt index a54fc2c8fb..aed1488b07 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt @@ -17,10 +17,14 @@ package im.vector.riotx.features.roomprofile +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.RoomSummary data class RoomProfileViewState( - val roomId: String + val roomId: String, + val roomSummary: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index 2fe7ce2b2f..3e58c2eeb9 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -1,14 +1,98 @@ - + android:layout_height="match_parent" + android:background="?riotx_header_panel_background"> - + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_bottom_sheet_divider.xml b/vector/src/main/res/layout/item_divider.xml similarity index 100% rename from vector/src/main/res/layout/item_bottom_sheet_divider.xml rename to vector/src/main/res/layout/item_divider.xml diff --git a/vector/src/main/res/layout/item_profile_action.xml b/vector/src/main/res/layout/item_profile_action.xml new file mode 100644 index 0000000000..947d505f35 --- /dev/null +++ b/vector/src/main/res/layout/item_profile_action.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/item_profile_section.xml b/vector/src/main/res/layout/item_profile_section.xml new file mode 100644 index 0000000000..e365733d79 --- /dev/null +++ b/vector/src/main/res/layout/item_profile_section.xml @@ -0,0 +1,17 @@ + + + + From 8aab46804bbe5fb70fc0e156c10d7d368cc275f3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 15 Nov 2019 20:37:36 +0100 Subject: [PATCH 03/19] Profile room: continue working on it (try to get a nice animation) [WIP] --- .../behavior/PercentViewBehavior.kt | 223 ++++++++++++++++++ .../core/epoxy/profiles/ProfileItemAction.kt | 4 + .../riotx/core/platform/VectorBaseActivity.kt | 10 +- .../roomprofile/RoomProfileController.kt | 50 ++-- .../roomprofile/RoomProfileFragment.kt | 19 +- .../roomprofile/RoomProfileViewModel.kt | 5 +- .../roomprofile/RoomProfileViewState.kt | 3 +- .../main/res/layout/fragment_room_profile.xml | 82 ++++--- .../main/res/layout/item_profile_action.xml | 8 +- vector/src/main/res/values/attr_behavior.xml | 23 ++ vector/src/main/res/values/strings_riotX.xml | 4 + 11 files changed, 374 insertions(+), 57 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt create mode 100644 vector/src/main/res/values/attr_behavior.xml diff --git a/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt b/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt new file mode 100644 index 0000000000..d3ec1c1ed5 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt @@ -0,0 +1,223 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.core.animations.behavior + +import android.animation.ArgbEvaluator +import android.content.Context +import android.graphics.drawable.ColorDrawable +import android.util.AttributeSet +import android.view.View +import androidx.coordinatorlayout.widget.CoordinatorLayout + +import im.vector.riotx.R +import kotlin.math.abs + +private const val UNSPECIFIED_INT = Integer.MAX_VALUE +private val UNSPECIFIED_FLOAT = Float.MAX_VALUE +private const val DEPEND_TYPE_HEIGHT = 0 +private const val DEPEND_TYPE_WIDTH = 1 +private const val DEPEND_TYPE_X = 2 +private const val DEPEND_TYPE_Y = 3 + +class PercentViewBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior(context, attrs) { + + private var dependType: Int = 0 + private var dependViewId: Int = 0 + private var dependTarget: Int = 0 + private var dependStartX: Int = 0 + private var dependStartY: Int = 0 + private var dependStartWidth: Int = 0 + private var dependStartHeight: Int = 0 + + private var startX: Int = 0 + private var startY: Int = 0 + private var startWidth: Int = 0 + private var startHeight: Int = 0 + private var startBackgroundColor: Int = 0 + private var startAlpha: Float = 0f + private var startRotateX: Float = 0f + private var startRotateY: Float = 0f + + private var targetX: Int = 0 + private var targetY: Int = 0 + private var targetWidth: Int = 0 + private var targetHeight: Int = 0 + private var targetBackgroundColor: Int = 0 + private var targetAlpha: Float = 0f + private var targetRotateX: Float = 0f + private var targetRotateY: Float = 0f + + /** + * Is the values prepared to be use + */ + private var isPrepared: Boolean = false + + init { + val a = context.obtainStyledAttributes(attrs, R.styleable.PercentViewBehavior) + dependViewId = a.getResourceId(R.styleable.PercentViewBehavior_behavior_dependsOn, 0) + dependType = a.getInt(R.styleable.PercentViewBehavior_behavior_dependType, DEPEND_TYPE_WIDTH) + dependTarget = a.getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_dependTarget, UNSPECIFIED_INT) + targetX = a.getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetX, UNSPECIFIED_INT) + targetY = a.getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetY, UNSPECIFIED_INT) + targetWidth = a.getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetWidth, UNSPECIFIED_INT) + targetHeight = a.getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetHeight, UNSPECIFIED_INT) + targetBackgroundColor = a.getColor(R.styleable.PercentViewBehavior_behavior_targetBackgroundColor, UNSPECIFIED_INT) + targetAlpha = a.getFloat(R.styleable.PercentViewBehavior_behavior_targetAlpha, UNSPECIFIED_FLOAT) + targetRotateX = a.getFloat(R.styleable.PercentViewBehavior_behavior_targetRotateX, UNSPECIFIED_FLOAT) + targetRotateY = a.getFloat(R.styleable.PercentViewBehavior_behavior_targetRotateY, UNSPECIFIED_FLOAT) + a.recycle() + } + + private fun prepare(parent: CoordinatorLayout, child: View, dependency: View) { + dependStartX = dependency.x.toInt() + dependStartY = dependency.y.toInt() + dependStartWidth = dependency.width + dependStartHeight = dependency.height + startX = child.x.toInt() + startY = child.y.toInt() + startWidth = child.width + startHeight = child.height + startAlpha = child.alpha + startRotateX = child.rotationX + startRotateY = child.rotationY + + // only set the start background color when the background is color drawable + val background = child.background + if (background is ColorDrawable) { + startBackgroundColor = background.color + } + + // if parent fitsSystemWindows is true, add status bar height to target y if specified + if (parent.fitsSystemWindows && targetY != UNSPECIFIED_INT) { + var result = 0 + val resources = parent.context.resources + val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = resources.getDimensionPixelSize(resourceId) + } + targetY += result + } + isPrepared = true + } + + override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean { + return dependency.id == dependViewId + } + + override fun onDependentViewChanged(parent: CoordinatorLayout, child: V, dependency: View): Boolean { + // first time, prepare values before continue + if (!isPrepared) { + prepare(parent, child, dependency) + } + updateView(child, dependency) + return false + } + + override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean { + val bool = super.onLayoutChild(parent, child, layoutDirection) + if (isPrepared) { + updateView(child, parent.getDependencies(child)[0]) + } + return bool + } + + /** + * Update the child view from the dependency states + * + * @param child child view + * @param dependency dependency view + */ + private fun updateView(child: V, dependency: View) { + var percent = 0f + var start = 0f + var current = 0f + var end = UNSPECIFIED_INT.toFloat() + when (dependType) { + DEPEND_TYPE_WIDTH -> { + start = dependStartWidth.toFloat() + current = dependency.width.toFloat() + end = dependTarget.toFloat() + } + DEPEND_TYPE_HEIGHT -> { + start = dependStartHeight.toFloat() + current = dependency.height.toFloat() + end = dependTarget.toFloat() + } + DEPEND_TYPE_X -> { + start = dependStartX.toFloat() + current = dependency.x + end = dependTarget.toFloat() + } + DEPEND_TYPE_Y -> { + start = dependStartY.toFloat() + current = dependency.y + end = dependTarget.toFloat() + } + } + + // need to define target value according to the depend type, if not then skip + if (end != UNSPECIFIED_INT.toFloat()) { + percent = abs(current - start) / abs(end - start) + } + updateViewWithPercent(child, if (percent > 1f) 1f else percent) + } + + private fun updateViewWithPercent(child: View, percent: Float) { + var newX = if (targetX == UNSPECIFIED_INT) 0f else (targetX - startX) * percent + var newY = if (targetY == UNSPECIFIED_INT) 0f else (targetY - startY) * percent + + // set scale + if (targetWidth != UNSPECIFIED_INT) { + val newWidth = startWidth + (targetWidth - startWidth) * percent + child.scaleX = newWidth / startWidth + newX -= (startWidth - newWidth) / 2 + } + if (targetHeight != UNSPECIFIED_INT) { + val newHeight = startHeight + (targetHeight - startHeight) * percent + child.scaleY = newHeight / startHeight + newY -= (startHeight - newHeight) / 2 + } + + // set new position + child.translationX = newX + child.translationY = newY + + // set alpha + if (targetAlpha != UNSPECIFIED_FLOAT) { + child.alpha = startAlpha + (targetAlpha - startAlpha) * percent + } + + // set background color + if (targetBackgroundColor != UNSPECIFIED_INT && startBackgroundColor != 0) { + val evaluator = ArgbEvaluator() + val color = evaluator.evaluate(percent, startBackgroundColor, targetBackgroundColor) as Int + child.setBackgroundColor(color) + } + + // set rotation + if (targetRotateX != UNSPECIFIED_FLOAT) { + child.rotationX = startRotateX + (targetRotateX - startRotateX) * percent + } + if (targetRotateY != UNSPECIFIED_FLOAT) { + child.rotationY = startRotateY + (targetRotateY - startRotateY) * percent + } + + child.requestLayout() + } + + +} diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt index a69cf96243..eb34834f27 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt @@ -16,6 +16,7 @@ package im.vector.riotx.core.epoxy.profiles +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible @@ -37,9 +38,12 @@ abstract class ProfileItemAction : VectorEpoxyModel() var iconRes: Int = 0 @EpoxyAttribute var editable: Boolean = true + @EpoxyAttribute + lateinit var listener: View.OnClickListener override fun bind(holder: Holder) { super.bind(holder) + holder.view.setOnClickListener(listener) holder.editable.isVisible = editable holder.title.text = title holder.subtitle.setTextOrHide(subtitle) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index 4a3056657f..cca3505d1c 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -332,12 +332,10 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { */ protected fun configureToolbar(toolbar: Toolbar, displayBack: Boolean = true) { setSupportActionBar(toolbar) - - if (displayBack) { - supportActionBar?.let { - it.setDisplayShowHomeEnabled(true) - it.setDisplayHomeAsUpEnabled(true) - } + supportActionBar?.let { + it.setDisplayShowHomeEnabled(displayBack) + it.setDisplayHomeAsUpEnabled(displayBack) + it.title = null } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index a5f0682d11..f321d54274 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -22,29 +22,49 @@ import im.vector.riotx.R import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.epoxy.profiles.profileItemAction import im.vector.riotx.core.epoxy.profiles.profileItemSection +import im.vector.riotx.core.resources.StringProvider import javax.inject.Inject -class RoomProfileController @Inject constructor() +class RoomProfileController @Inject constructor(private val stringProvider: StringProvider) : TypedEpoxyController() { + var callback: Callback? = null + + interface Callback { + fun onLearnMoreClicked() + fun onMemberListClicked() + fun onSettingsClicked() + } + override fun buildModels(data: RoomProfileViewState?) { if (data == null) { return } + val roomSummary = data.roomSummary() + profileItemSection { id("section_security") title("Security") } + + val learnMoreSubtitle = if (data.isEncrypted) { + R.string.room_profile_encrypted_subtitle + } else { + R.string.room_profile_not_encrypted_subtitle + } profileItemAction { id("action_learn_more") title("Learn more") editable(true) - subtitle("Messages in this room are not end-to-end encrypted.") + subtitle(stringProvider.getString(learnMoreSubtitle)) + listener { _ -> + callback?.onLearnMoreClicked() + } } - dividerItem{ + dividerItem { id("action_learn_more_divider") } @@ -53,33 +73,29 @@ class RoomProfileController @Inject constructor() title("Options") } + val numberOfMembers = (roomSummary?.otherMemberIds?.size ?: 0) + 1 profileItemAction { iconRes(R.drawable.ic_person_outline_black) id("action_member_list") - title("88 people") + title(stringProvider.getString(R.string.room_profile_member_list_title, numberOfMembers)) editable(true) + listener { _ -> + callback?.onMemberListClicked() + } } - dividerItem{ + dividerItem { id("action_member_list_divider") } profileItemAction { - iconRes(R.drawable.ic_attachment) - id("action_files") - title("12 files") - editable(true) - } - - dividerItem{ - id("action_files_divider") - } - - profileItemAction { - iconRes(R.drawable.ic_settings_x) + iconRes(R.drawable.ic_room_actions_settings) id("action_settings") title("Room settings") editable(true) + listener { _ -> + callback?.onSettingsClicked() + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index ff0b8be634..2523f8f6ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -43,7 +43,7 @@ class RoomProfileFragment @Inject constructor( private val roomProfileController: RoomProfileController, private val avatarRenderer: AvatarRenderer, val roomProfileViewModelFactory: RoomProfileViewModel.Factory -) : VectorBaseFragment() { +) : VectorBaseFragment(), RoomProfileController.Callback { private val roomProfileArgs: RoomProfileArgs by args() private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() @@ -57,9 +57,11 @@ class RoomProfileFragment @Inject constructor( } private fun setupRecyclerView() { + roomProfileController.callback = this roomProfileRecyclerView.setHasFixedSize(true) roomProfileRecyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) roomProfileRecyclerView.adapter = roomProfileController.adapter + } override fun onDestroyView() { @@ -74,6 +76,7 @@ class RoomProfileFragment @Inject constructor( activity?.finish() } else { roomProfileNameView.text = it.displayName + roomProfileNameView2.text = it.displayName roomProfileIdView.text = it.roomId roomProfileTopicView.setTextOrHide(it.topic) avatarRenderer.render(it, roomProfileAvatarView) @@ -82,5 +85,19 @@ class RoomProfileFragment @Inject constructor( roomProfileController.setData(state) } + // RoomProfileController.Callback + + override fun onLearnMoreClicked() { + vectorBaseActivity.notImplemented() + } + + override fun onMemberListClicked() { + vectorBaseActivity.notImplemented("Room member list") + } + + override fun onSettingsClicked() { + vectorBaseActivity.notImplemented("Room settings") + } + } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index d81c748bdd..52a48d101d 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -55,7 +55,10 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R room.rx().liveRoomSummary() .unwrap() .execute { - copy(roomSummary = it) + copy( + roomSummary = it, + isEncrypted = room.isEncrypted() + ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt index aed1488b07..7a5e74b10e 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt @@ -24,7 +24,8 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary data class RoomProfileViewState( val roomId: String, - val roomSummary: Async = Uninitialized + val roomSummary: Async = Uninitialized, + val isEncrypted: Boolean = false ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index 3e58c2eeb9..d7e3ca2555 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -7,49 +7,40 @@ android:background="?riotx_header_panel_background"> - - + app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" + app:titleEnabled="false" + app:toolbarId="@+id/roomProfileToolbar"> - - + android:paddingTop="160dp" + android:minHeight="280dp" + app:layout_collapseMode="parallax" + app:layout_collapseParallaxMultiplier="0.9"> - - + + @@ -92,7 +85,42 @@ android:id="@+id/roomProfileRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:listitem="@layout/item_profile_action" /> + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_profile_action.xml b/vector/src/main/res/layout/item_profile_action.xml index 947d505f35..2d6e1d0612 100644 --- a/vector/src/main/res/layout/item_profile_action.xml +++ b/vector/src/main/res/layout/item_profile_action.xml @@ -25,8 +25,8 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp" - app:layout_constraintTop_toTopOf="parent" - tools:src="@drawable/ic_room_actions_notifications_all" /> + android:visibility="gone" + app:layout_constraintTop_toTopOf="parent" /> + tools:text="Learn more" /> + tools:text="Messages in this room are not end-to-end encrypted" /> + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 6e5b5da92e..1cd27b39b4 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -19,4 +19,8 @@ Long click on a room to see more options + Messages in this room are not end-to-end encrypted. + Messages in this room are end-to-end encrypted. + "%1$d people" + From ab4cab05cf680b34bddc146504b6461de990611b Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 26 Nov 2019 13:22:07 +0100 Subject: [PATCH 04/19] Room profile: continue working on UI --- .../api/session/room/model/RoomSummary.kt | 2 + .../database/mapper/RoomSummaryMapper.kt | 2 + .../core/epoxy/profiles/ProfileItemAction.kt | 12 ++ .../roomprofile/RoomProfileController.kt | 132 +++++++++++------- .../roomprofile/RoomProfileFragment.kt | 19 ++- .../drawable/ic_room_profile_member_list.xml | 51 +++++++ .../drawable/ic_room_profile_notification.xml | 25 ++++ .../res/drawable/ic_room_profile_settings.xml | 30 ++++ .../res/drawable/ic_room_profile_uploads.xml | 37 +++++ .../main/res/layout/fragment_room_profile.xml | 2 +- .../main/res/layout/item_profile_action.xml | 18 +-- vector/src/main/res/values/strings_riotX.xml | 9 +- 12 files changed, 276 insertions(+), 63 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_room_profile_member_list.xml create mode 100644 vector/src/main/res/drawable/ic_room_profile_notification.xml create mode 100644 vector/src/main/res/drawable/ic_room_profile_settings.xml create mode 100644 vector/src/main/res/drawable/ic_room_profile_uploads.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index 447ba563de..53268e5805 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -30,6 +30,8 @@ data class RoomSummary( val topic: String = "", val avatarUrl: String = "", val isDirect: Boolean = false, + val joinedMembersCount: Int? = 0, + val invitedMembersCount: Int? = 0, val latestPreviewableEvent: TimelineEvent? = null, val otherMemberIds: List = emptyList(), val notificationCount: Int = 0, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index 2577bec581..80056c2b2f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -60,6 +60,8 @@ internal class RoomSummaryMapper @Inject constructor( avatarUrl = roomSummaryEntity.avatarUrl ?: "", isDirect = roomSummaryEntity.isDirect, latestPreviewableEvent = latestEvent, + joinedMembersCount = roomSummaryEntity.joinedMembersCount, + invitedMembersCount = roomSummaryEntity.invitedMembersCount, otherMemberIds = roomSummaryEntity.otherMemberIds.toList(), highlightCount = roomSummaryEntity.highlightCount, notificationCount = roomSummaryEntity.notificationCount, diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt index eb34834f27..5de1958783 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt @@ -16,16 +16,20 @@ package im.vector.riotx.core.epoxy.profiles +import android.content.res.ColorStateList import android.view.View import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.features.themes.ThemeUtils @EpoxyModelClass(layout = R.layout.item_profile_action) abstract class ProfileItemAction : VectorEpoxyModel() { @@ -39,6 +43,8 @@ abstract class ProfileItemAction : VectorEpoxyModel() @EpoxyAttribute var editable: Boolean = true @EpoxyAttribute + var destructive: Boolean = false + @EpoxyAttribute lateinit var listener: View.OnClickListener override fun bind(holder: Holder) { @@ -46,6 +52,12 @@ abstract class ProfileItemAction : VectorEpoxyModel() holder.view.setOnClickListener(listener) holder.editable.isVisible = editable holder.title.text = title + val tintColor = if (destructive) { + ContextCompat.getColor(holder.view.context, R.color.riotx_notice) + } else { + ThemeUtils.getColor(holder.view.context, R.attr.riotx_text_primary) + } + holder.title.setTextColor(tintColor) holder.subtitle.setTextOrHide(subtitle) if (iconRes != 0) { holder.icon.setImageResource(iconRes) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index f321d54274..390686567b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -17,8 +17,11 @@ package im.vector.riotx.features.roomprofile +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController import im.vector.riotx.R +import im.vector.riotx.core.epoxy.DividerItem_ import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.epoxy.profiles.profileItemAction import im.vector.riotx.core.epoxy.profiles.profileItemSection @@ -33,7 +36,10 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri interface Callback { fun onLearnMoreClicked() fun onMemberListClicked() + fun onNotificationsClicked() + fun onUploadsClicked() fun onSettingsClicked() + fun onLeaveRoomClicked() } override fun buildModels(data: RoomProfileViewState?) { @@ -43,60 +49,88 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri val roomSummary = data.roomSummary() - profileItemSection { - id("section_security") - title("Security") - } - - + // Security + buildSection(stringProvider.getString(R.string.room_profile_section_security)) val learnMoreSubtitle = if (data.isEncrypted) { R.string.room_profile_encrypted_subtitle } else { R.string.room_profile_not_encrypted_subtitle } - profileItemAction { - id("action_learn_more") - title("Learn more") - editable(true) - subtitle(stringProvider.getString(learnMoreSubtitle)) - listener { _ -> - callback?.onLearnMoreClicked() - } - } - - dividerItem { - id("action_learn_more_divider") - } - - profileItemSection { - id("section_options") - title("Options") - } - - val numberOfMembers = (roomSummary?.otherMemberIds?.size ?: 0) + 1 - profileItemAction { - iconRes(R.drawable.ic_person_outline_black) - id("action_member_list") - title(stringProvider.getString(R.string.room_profile_member_list_title, numberOfMembers)) - editable(true) - listener { _ -> - callback?.onMemberListClicked() - } - } - - dividerItem { - id("action_member_list_divider") - } - - profileItemAction { - iconRes(R.drawable.ic_room_actions_settings) - id("action_settings") - title("Room settings") - editable(true) - listener { _ -> - callback?.onSettingsClicked() - } - } + buildAction( + id = "learn_more", + title = stringProvider.getString(R.string.room_profile_section_security_learn_more), + subtitle = stringProvider.getString(learnMoreSubtitle), + action = { callback?.onLearnMoreClicked() } + ) + // More + buildSection(stringProvider.getString(R.string.room_profile_section_more)) + buildAction( + id = "settings", + title = stringProvider.getString(R.string.room_profile_section_more_settings), + icon = R.drawable.ic_room_profile_settings, + action = { callback?.onSettingsClicked() } + ) + buildAction( + id = "notifications", + title = stringProvider.getString(R.string.room_profile_section_more_notifications), + icon = R.drawable.ic_room_profile_notification, + action = { callback?.onNotificationsClicked() } + ) + val numberOfMembers = roomSummary?.joinedMembersCount?.toString() ?: "-" + buildAction( + id = "member_list", + title = stringProvider.getString(R.string.room_profile_section_more_member_list, numberOfMembers), + icon = R.drawable.ic_room_profile_member_list, + action = { callback?.onMemberListClicked() } + ) + buildAction( + id = "uploads", + title = stringProvider.getString(R.string.room_profile_section_more_uploads), + icon = R.drawable.ic_room_profile_uploads, + action = { callback?.onUploadsClicked() } + ) + buildAction( + id = "leave", + title = stringProvider.getString(R.string.room_profile_section_more_leave), + divider = false, + destructive = true, + action = { callback?.onLeaveRoomClicked() } + ) } + + private fun buildSection(title: String) { + profileItemSection { + id("section_$title") + title(title) + } + } + + private fun buildAction( + id: String, + title: String, + subtitle: String? = null, + @DrawableRes icon: Int = 0, + destructive: Boolean = false, + divider: Boolean = true, + action: () -> Unit + ) { + + profileItemAction { + iconRes(icon) + id("action_$id") + subtitle(subtitle) + destructive(destructive) + title(title) + listener { _ -> + action() + } + } + + DividerItem_() + .id("divider_$title") + .addIf(divider, this) + } + + } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 2523f8f6ff..a659ba4c00 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -61,7 +61,6 @@ class RoomProfileFragment @Inject constructor( roomProfileRecyclerView.setHasFixedSize(true) roomProfileRecyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) roomProfileRecyclerView.adapter = roomProfileController.adapter - } override fun onDestroyView() { @@ -77,7 +76,8 @@ class RoomProfileFragment @Inject constructor( } else { roomProfileNameView.text = it.displayName roomProfileNameView2.text = it.displayName - roomProfileIdView.text = it.roomId + // Use canonical alias when PR with alias management will be merged + roomProfileAliasView.text = it.roomId roomProfileTopicView.setTextOrHide(it.topic) avatarRenderer.render(it, roomProfileAvatarView) } @@ -92,12 +92,23 @@ class RoomProfileFragment @Inject constructor( } override fun onMemberListClicked() { - vectorBaseActivity.notImplemented("Room member list") + vectorBaseActivity.notImplemented("See room member list") } override fun onSettingsClicked() { - vectorBaseActivity.notImplemented("Room settings") + vectorBaseActivity.notImplemented("See Room settings") } + override fun onNotificationsClicked() { + vectorBaseActivity.notImplemented("See notifications") + } + + override fun onUploadsClicked() { + vectorBaseActivity.notImplemented("See uploads") + } + + override fun onLeaveRoomClicked() { + vectorBaseActivity.notImplemented("Leave room") + } } diff --git a/vector/src/main/res/drawable/ic_room_profile_member_list.xml b/vector/src/main/res/drawable/ic_room_profile_member_list.xml new file mode 100644 index 0000000000..a80a0d4811 --- /dev/null +++ b/vector/src/main/res/drawable/ic_room_profile_member_list.xml @@ -0,0 +1,51 @@ + + + + + + + + diff --git a/vector/src/main/res/drawable/ic_room_profile_notification.xml b/vector/src/main/res/drawable/ic_room_profile_notification.xml new file mode 100644 index 0000000000..f2b500815d --- /dev/null +++ b/vector/src/main/res/drawable/ic_room_profile_notification.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/vector/src/main/res/drawable/ic_room_profile_settings.xml b/vector/src/main/res/drawable/ic_room_profile_settings.xml new file mode 100644 index 0000000000..494ba69f14 --- /dev/null +++ b/vector/src/main/res/drawable/ic_room_profile_settings.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/vector/src/main/res/drawable/ic_room_profile_uploads.xml b/vector/src/main/res/drawable/ic_room_profile_uploads.xml new file mode 100644 index 0000000000..914a70f7fb --- /dev/null +++ b/vector/src/main/res/drawable/ic_room_profile_uploads.xml @@ -0,0 +1,37 @@ + + + + + + diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index d7e3ca2555..8b3ac8f608 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -44,7 +44,7 @@ tools:text="Random" /> + app:layout_constraintTop_toTopOf="parent" + tools:src="@drawable/ic_room_profile_notification" /> @@ -72,8 +74,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:visibility="gone" android:tint="?riotx_text_secondary" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 1cd27b39b4..feaa9f5db5 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -21,6 +21,13 @@ Messages in this room are not end-to-end encrypted. Messages in this room are end-to-end encrypted. - "%1$d people" + Security + Learn more + More + Room settings + Notifications + "%1$s people" + Uploads + Leave Room From fd18bcb97f84f92c538a2b579979010e6f571f72 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 26 Nov 2019 17:25:26 +0100 Subject: [PATCH 05/19] Room profile: branch leave and notifications actions --- .../home/room/list/RoomListFragment.kt | 8 +- .../RoomListQuickActionsBottomSheet.kt | 15 +++- .../RoomListQuickActionsEpoxyController.kt | 36 +++++---- .../list/actions/RoomListQuickActionsState.kt | 3 +- .../features/roomprofile/RoomProfileAction.kt | 4 + .../roomprofile/RoomProfileFragment.kt | 78 ++++++++++++++++++- .../roomprofile/RoomProfileViewEvents.kt | 26 +++++++ .../roomprofile/RoomProfileViewModel.kt | 33 +++++++- .../src/main/res/layout/activity_simple.xml | 14 +++- vector/src/main/res/values/strings_riotX.xml | 1 + 10 files changed, 185 insertions(+), 33 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 9e6d5533f6..54fadf6461 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -40,6 +40,7 @@ import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.RoomListDisplayMode +import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel @@ -132,10 +133,7 @@ class RoomListFragment @Inject constructor( } private fun showError(event: RoomListViewEvents.Failure) { - vectorBaseActivity.coordinatorLayout?.let { - Snackbar.make(it, errorFormatter.toHumanReadable(event.throwable), Snackbar.LENGTH_SHORT) - .show() - } + vectorBaseActivity.showSnackbar(errorFormatter.toHumanReadable(event.throwable)) } private fun setupCreateRoomButton() { @@ -347,7 +345,7 @@ class RoomListFragment @Inject constructor( roomController.onRoomLongClicked() RoomListQuickActionsBottomSheet - .newInstance(room.roomId) + .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") return true } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 3a85cf26fa..c797a7dc45 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -36,8 +36,15 @@ import javax.inject.Inject @Parcelize data class RoomListActionsArgs( - val roomId: String -) : Parcelable + val roomId: String, + val mode: Mode +) : Parcelable { + + enum class Mode { + FULL, + NOTIFICATIONS + } +} /** * Bottom sheet fragment that shows room information with list of contextual actions @@ -87,9 +94,9 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R } companion object { - fun newInstance(roomId: String): RoomListQuickActionsBottomSheet { + fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet { return RoomListQuickActionsBottomSheet().apply { - setArguments(RoomListActionsArgs(roomId)) + setArguments(RoomListActionsArgs(roomId, mode)) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 8562ec4153..c7fa354961 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -35,19 +35,21 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar override fun buildModels(state: RoomListQuickActionsState) { val roomSummary = state.roomSummary() ?: return - // Preview - bottomSheetItemRoomPreview { - id("preview") - avatarRenderer(avatarRenderer) - roomName(roomSummary.displayName) - avatarUrl(roomSummary.avatarUrl) - roomId(roomSummary.roomId) - settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }) - } + if (state.mode == RoomListActionsArgs.Mode.FULL) { + // Preview + bottomSheetItemRoomPreview { + id("preview") + avatarRenderer(avatarRenderer) + roomName(roomSummary.displayName) + avatarUrl(roomSummary.avatarUrl) + roomId(roomSummary.roomId) + settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }) + } - // Notifications - dividerItem { - id("notifications_separator") + // Notifications + dividerItem { + id("notifications_separator") + } } val selectedRoomState = state.roomNotificationState() @@ -56,11 +58,13 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState) RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) - // Leave - dividerItem { - id("leave_separator") + if (state.mode == RoomListActionsArgs.Mode.FULL) { + // Leave + dividerItem { + id("leave_separator") + } + RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) } - RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) } private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsState.kt index a943db1804..6e134871a7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsState.kt @@ -24,9 +24,10 @@ import im.vector.matrix.android.api.session.room.notification.RoomNotificationSt data class RoomListQuickActionsState( val roomId: String, + val mode: RoomListActionsArgs.Mode, val roomSummary: Async = Uninitialized, val roomNotificationState: Async = Uninitialized ) : MvRxState { - constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) + constructor(args: RoomListActionsArgs) : this(roomId = args.roomId, mode = args.mode) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt index 979a9d2a57..985eee72a4 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt @@ -17,7 +17,11 @@ package im.vector.riotx.features.roomprofile +import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.core.platform.VectorViewModelAction +import im.vector.riotx.features.home.room.list.RoomListAction sealed class RoomProfileAction: VectorViewModelAction { + object LeaveRoom: RoomProfileAction() + data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index a659ba4c00..b1c5855aac 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -15,20 +15,32 @@ * */ +@file:Suppress("DEPRECATION") + package im.vector.riotx.features.roomprofile +import android.app.ProgressDialog import android.os.Bundle import android.os.Parcelable import android.view.View +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import com.google.android.material.snackbar.Snackbar +import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.R +import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.AvatarRenderer +import im.vector.riotx.features.home.room.list.RoomListAction +import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs +import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet +import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction +import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_profile.* import timber.log.Timber @@ -42,18 +54,70 @@ data class RoomProfileArgs( class RoomProfileFragment @Inject constructor( private val roomProfileController: RoomProfileController, private val avatarRenderer: AvatarRenderer, + private val errorFormatter: ErrorFormatter, val roomProfileViewModelFactory: RoomProfileViewModel.Factory ) : VectorBaseFragment(), RoomProfileController.Callback { + private var progress: ProgressDialog? = null private val roomProfileArgs: RoomProfileArgs by args() + private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() override fun getLayoutResId() = R.layout.fragment_room_profile override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) setupToolbar(roomProfileToolbar) setupRecyclerView() + roomProfileViewModel.viewEvents + .observe() + .subscribe { + progress?.dismiss() + when (it) { + RoomProfileViewEvents.Loading -> showLoading() + RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom() + is RoomProfileViewEvents.Failure -> showError(it.throwable) + } + } + .disposeOnDestroyView() + + sharedActionViewModel + .observe() + .subscribe { handleQuickActions(it) } + .disposeOnDestroyView() + } + + private fun handleQuickActions(action: RoomListQuickActionsSharedAction) = when (action) { + is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { + roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.ALL_MESSAGES_NOISY)) + } + is RoomListQuickActionsSharedAction.NotificationsAll -> { + roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.ALL_MESSAGES)) + } + is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> { + roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.MENTIONS_ONLY)) + } + is RoomListQuickActionsSharedAction.NotificationsMute -> { + roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.MUTE)) + } + else -> Timber.v("$action not handled") + } + + private fun onLeaveRoom() { + vectorBaseActivity.finish() + } + + private fun showError(throwable: Throwable) { + vectorBaseActivity.showSnackbar(errorFormatter.toHumanReadable(throwable)) + } + + private fun showLoading() { + progress = ProgressDialog(requireContext()).apply { + setMessage(getString(R.string.room_profile_leaving_room)) + setProgressStyle(ProgressDialog.STYLE_SPINNER) + show() + } } private fun setupRecyclerView() { @@ -100,8 +164,9 @@ class RoomProfileFragment @Inject constructor( } override fun onNotificationsClicked() { - vectorBaseActivity.notImplemented("See notifications") - + RoomListQuickActionsBottomSheet + .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) + .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") } override fun onUploadsClicked() { @@ -109,6 +174,13 @@ class RoomProfileFragment @Inject constructor( } override fun onLeaveRoomClicked() { - vectorBaseActivity.notImplemented("Leave room") + AlertDialog.Builder(requireContext()) + .setTitle(R.string.room_participants_leave_prompt_title) + .setMessage(R.string.room_participants_leave_prompt_msg) + .setPositiveButton(R.string.leave) { _, _ -> + roomProfileViewModel.handle(RoomProfileAction.LeaveRoom) + } + .setNegativeButton(R.string.cancel, null) + .show() } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt new file mode 100644 index 0000000000..4b483d51a4 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile +/** + * Transient events for RoomProfile + */ +sealed class RoomProfileViewEvents { + object Loading: RoomProfileViewEvents() + object OnLeaveRoomSuccess: RoomProfileViewEvents() + data class Failure(val throwable: Throwable) : RoomProfileViewEvents() + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index 52a48d101d..72b87b7716 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -22,10 +22,15 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.utils.DataSource +import im.vector.riotx.core.utils.PublishDataSource +import im.vector.riotx.features.home.room.list.RoomListAction +import im.vector.riotx.features.home.room.list.RoomListViewEvents class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, private val session: Session) @@ -45,6 +50,9 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } } + private val _viewEvents = PublishDataSource() + val viewEvents: DataSource = _viewEvents + private val room = session.getRoom(initialState.roomId)!! init { @@ -62,7 +70,30 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } } - override fun handle(action: RoomProfileAction) { + override fun handle(action: RoomProfileAction) = when (action) { + RoomProfileAction.LeaveRoom -> handleLeaveRoom() + is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) + } + + private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { + room.setRoomNotificationState(action.notificationState, object : MatrixCallback { + override fun onFailure(failure: Throwable) { + _viewEvents.post(RoomProfileViewEvents.Failure(failure)) + } + }) + } + + private fun handleLeaveRoom() { + _viewEvents.post(RoomProfileViewEvents.Loading) + room.leave(object : MatrixCallback { + override fun onSuccess(data: Unit) { + _viewEvents.post(RoomProfileViewEvents.OnLeaveRoomSuccess) + } + + override fun onFailure(failure: Throwable) { + _viewEvents.post(RoomProfileViewEvents.Failure(failure)) + } + }) } } diff --git a/vector/src/main/res/layout/activity_simple.xml b/vector/src/main/res/layout/activity_simple.xml index 8dc41487fe..0eda46a67d 100644 --- a/vector/src/main/res/layout/activity_simple.xml +++ b/vector/src/main/res/layout/activity_simple.xml @@ -1,5 +1,13 @@ - + android:layout_height="match_parent"> + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index feaa9f5db5..9f38134022 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -29,5 +29,6 @@ "%1$s people" Uploads Leave Room + "Leaving the room..." From dfd8181754e8987319ae4a4db1023eccb2a1a225 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 20 Dec 2019 20:28:27 +0100 Subject: [PATCH 06/19] Room profile: start creating some classes and use shared action. --- .../im/vector/riotx/core/di/FragmentModule.kt | 6 +++ .../vector/riotx/core/di/ViewModelModule.kt | 6 +++ .../roomprofile/RoomProfileActivity.kt | 31 ++++++++++- .../roomprofile/RoomProfileFragment.kt | 14 ++--- .../roomprofile/RoomProfileSharedAction.kt | 28 ++++++++++ .../RoomProfileSharedActionViewModel.kt | 22 ++++++++ .../members/RoomMemberListAction.kt | 21 ++++++++ .../members/RoomMemberListFragment.kt | 53 +++++++++++++++++++ .../members/RoomMemberListViewModel.kt | 47 ++++++++++++++++ .../members/RoomMemberListViewState.kt | 23 ++++++++ .../members/RoomMembersEpoxyController.kt | 27 ++++++++++ .../res/layout/fragment_room_member_list.xml | 19 +++++++ 12 files changed, 289 insertions(+), 8 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt create mode 100644 vector/src/main/res/layout/fragment_room_member_list.xml diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 9eaf89b70e..dcca33a645 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -45,6 +45,7 @@ import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment import im.vector.riotx.features.roomprofile.RoomProfileFragment +import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.settings.* import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment import im.vector.riotx.features.settings.push.PushGatewaysFragment @@ -259,6 +260,11 @@ interface FragmentModule { @FragmentKey(RoomProfileFragment::class) fun bindRoomProfileFragment(fragment: RoomProfileFragment): Fragment + @Binds + @IntoMap + @FragmentKey(RoomMemberListFragment::class) + fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment + @Binds @IntoMap @FragmentKey(BreadcrumbsFragment::class) diff --git a/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt index bfd0124391..5d4288f4b8 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt @@ -35,6 +35,7 @@ import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsShare import im.vector.riotx.features.login.LoginSharedActionViewModel import im.vector.riotx.features.reactions.EmojiChooserViewModel import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel +import im.vector.riotx.features.roomprofile.RoomProfileSharedActionViewModel import im.vector.riotx.features.workers.signout.SignOutViewModel @Module @@ -124,4 +125,9 @@ interface ViewModelModule { @IntoMap @ViewModelKey(RoomDetailSharedActionViewModel::class) fun bindRoomDetailSharedActionViewModel(viewModel: RoomDetailSharedActionViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(RoomProfileSharedActionViewModel::class) + fun bindRoomProfileSharedActionViewModel(viewModel: RoomProfileSharedActionViewModel): ViewModel } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index e57debee7f..14c3421e7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -22,8 +22,10 @@ import android.content.Intent import androidx.appcompat.widget.Toolbar import im.vector.riotx.R import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.extensions.addFragmentToBackstack import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity +import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { @@ -39,14 +41,39 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { } } + private lateinit var sharedActionViewModel: RoomProfileSharedActionViewModel + private lateinit var roomProfileArgs: RoomProfileArgs + override fun getLayoutRes() = R.layout.activity_simple override fun initUiAndData() { + sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) + roomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) ?: return if (isFirstCreation()) { - val roomProfileArgs: RoomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) - ?: return addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) } + sharedActionViewModel + .observe() + .subscribe { sharedAction -> + when (sharedAction) { + is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() + is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + } + } + .disposeOnDestroy() + } + + private fun openRoomUploads() { + notImplemented("Open room uploads") + } + + private fun openRoomSettings() { + notImplemented("Open room settings") + } + + private fun openRoomMembers() { + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) } override fun configure(toolbar: Toolbar) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index c4fec612ac..0ebbbcde6b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -60,14 +60,16 @@ class RoomProfileFragment @Inject constructor( private var progress: ProgressDialog? = null private val roomProfileArgs: RoomProfileArgs by args() - private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel + private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel + private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() override fun getLayoutResId() = R.layout.fragment_room_profile override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) + roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) + roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) setupToolbar(roomProfileToolbar) setupRecyclerView() roomProfileViewModel.viewEvents @@ -82,7 +84,7 @@ class RoomProfileFragment @Inject constructor( } .disposeOnDestroyView() - sharedActionViewModel + roomListQuickActionsSharedActionViewModel .observe() .subscribe { handleQuickActions(it) } .disposeOnDestroyView() @@ -156,11 +158,11 @@ class RoomProfileFragment @Inject constructor( } override fun onMemberListClicked() { - vectorBaseActivity.notImplemented("See room member list") + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomMembers) } override fun onSettingsClicked() { - vectorBaseActivity.notImplemented("See Room settings") + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomSettings) } override fun onNotificationsClicked() { @@ -170,7 +172,7 @@ class RoomProfileFragment @Inject constructor( } override fun onUploadsClicked() { - vectorBaseActivity.notImplemented("See uploads") + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomUploads) } override fun onLeaveRoomClicked() { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt new file mode 100644 index 0000000000..71142c0aae --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile + +import im.vector.riotx.core.platform.VectorSharedAction + +/** + * Supported navigation actions for [RoomProfileActivity] + */ +sealed class RoomProfileSharedAction : VectorSharedAction { + object OpenRoomSettings : RoomProfileSharedAction() + object OpenRoomUploads : RoomProfileSharedAction() + object OpenRoomMembers : RoomProfileSharedAction() +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedActionViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedActionViewModel.kt new file mode 100644 index 0000000000..e1921b0c23 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedActionViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile + +import im.vector.riotx.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +class RoomProfileSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListAction.kt new file mode 100644 index 0000000000..01a35b84d3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListAction.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class RoomMemberListAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt new file mode 100644 index 0000000000..ae0e990c4a --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import android.os.Bundle +import android.view.View +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.features.roomprofile.RoomProfileArgs +import javax.inject.Inject + + +class RoomMemberListFragment @Inject constructor( + val viewModelFactory: RoomMemberListViewModel.Factory +) : VectorBaseFragment() { + + private val viewModel: RoomMemberListViewModel by fragmentViewModel() + private val roomProfileArgs: RoomProfileArgs by args() + + override fun getLayoutResId() = R.layout.fragment_room_member_list + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // Initialize your view, subscribe to viewModel... + } + + override fun onDestroyView() { + super.onDestroyView() + // Clear your view, unsubscribe... + } + + override fun invalidate() = withState(viewModel) { _ -> + + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt new file mode 100644 index 0000000000..d1a4592965 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.riotx.core.platform.VectorViewModel + +class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? { + val fragment: RoomMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: RoomMemberListAction) { + //TODO + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt new file mode 100644 index 0000000000..2e432c59f5 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import com.airbnb.mvrx.MvRxState + +data class RoomMemberListViewState( + val noValue: Boolean = false +) : MvRxState diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt new file mode 100644 index 0000000000..9fb6c400c3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import com.airbnb.epoxy.TypedEpoxyController + +class RoomMembersEpoxyController : TypedEpoxyController() { + + override fun buildModels(data: RoomMemberListViewState?) { + + } + +} diff --git a/vector/src/main/res/layout/fragment_room_member_list.xml b/vector/src/main/res/layout/fragment_room_member_list.xml new file mode 100644 index 0000000000..eb453ac2e8 --- /dev/null +++ b/vector/src/main/res/layout/fragment_room_member_list.xml @@ -0,0 +1,19 @@ + + + + + + + From 15639b45cf1efecb4eccc619162105d5ca48ecb8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Jan 2020 12:10:49 +0100 Subject: [PATCH 07/19] Introduce RoomMemberProfile files --- vector/src/main/AndroidManifest.xml | 1 + .../im/vector/riotx/core/di/FragmentModule.kt | 6 ++ .../home/room/detail/RoomDetailFragment.kt | 2 +- .../features/navigation/DefaultNavigator.kt | 12 ++-- .../riotx/features/navigation/Navigator.kt | 3 +- .../features/permalink/PermalinkHandler.kt | 2 +- .../RoomMemberProfileAction.kt | 22 ++++++++ .../RoomMemberProfileActivity.kt | 55 ++++++++++++++++++ .../RoomMemberProfileFragment.kt | 56 +++++++++++++++++++ .../RoomMemberProfileViewModel.kt | 51 +++++++++++++++++ .../RoomMemberProfileViewState.kt | 29 ++++++++++ .../layout/fragment_room_member_profile.xml | 36 ++++++++++++ 12 files changed, 265 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt create mode 100644 vector/src/main/res/layout/fragment_room_member_profile.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index fe03efecd5..febdefdb16 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -125,6 +125,7 @@ + diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 9c340fbcb2..d86271761a 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -38,6 +38,7 @@ import im.vector.riotx.features.home.room.detail.RoomDetailFragment import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.login.* import im.vector.riotx.features.login.terms.LoginTermsFragment +import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment import im.vector.riotx.features.reactions.EmojiChooserFragment import im.vector.riotx.features.reactions.EmojiSearchResultFragment import im.vector.riotx.features.roomdirectory.PublicRoomsFragment @@ -271,6 +272,11 @@ interface FragmentModule { @FragmentKey(RoomMemberListFragment::class) fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment + @Binds + @IntoMap + @FragmentKey(RoomMemberProfileFragment::class) + fun bindRoomMemberProfileFragment(fragment: RoomMemberProfileFragment): Fragment + @Binds @IntoMap @FragmentKey(BreadcrumbsFragment::class) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index b5560129ae..4998cad700 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -912,7 +912,7 @@ class RoomDetailFragment @Inject constructor( } override fun onAvatarClicked(informationData: MessageInformationData) { - vectorBaseActivity.notImplemented("Click on user avatar") + navigator.openRoomMemberProfile(userId = informationData.senderId, context = requireActivity()) } override fun onMemberNameClicked(informationData: MessageInformationData) { diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 8f7505928a..76925d9af8 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -33,13 +33,14 @@ import im.vector.riotx.features.createdirect.CreateDirectRoomActivity import im.vector.riotx.features.home.room.detail.RoomDetailActivity import im.vector.riotx.features.home.room.detail.RoomDetailArgs import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity +import im.vector.riotx.features.roommemberprofile.RoomMemberProfileActivity +import im.vector.riotx.features.roommemberprofile.RoomMemberProfileArgs import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity import im.vector.riotx.features.roomprofile.RoomProfileActivity import im.vector.riotx.features.settings.VectorSettingsActivity import im.vector.riotx.features.share.SharedData -import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -81,12 +82,9 @@ class DefaultNavigator @Inject constructor( } } - override fun openUserDetail(userId: String, context: Context, buildTask: Boolean) { - if (context is VectorBaseActivity) { - context.notImplemented("Open user detail") - } else { - context.toast(R.string.not_implemented) - } + override fun openRoomMemberProfile(userId: String, context: Context, buildTask: Boolean) { + val args = RoomMemberProfileArgs(userId = userId) + context.startActivity(RoomMemberProfileActivity.newIntent(context, args)) } override fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData) { diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index 10eff8b2b3..0e093910f0 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -50,7 +50,8 @@ interface Navigator { fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean = false) - fun openUserDetail(userId: String, context: Context, buildTask: Boolean = false) + fun openRoomMemberProfile(userId: String, context: Context, buildTask: Boolean = false) fun openRoomProfile(context: Context, roomId: String) + } diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt index e46adc53fc..8386a8e45d 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt @@ -68,7 +68,7 @@ class PermalinkHandler @Inject constructor(private val session: Session, Single.just(true) } is PermalinkData.UserLink -> { - navigator.openUserDetail(permalinkData.userId, context, buildTask) + navigator.openRoomMemberProfile(permalinkData.userId, context, buildTask) Single.just(true) } is PermalinkData.FallbackLink -> { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt new file mode 100644 index 0000000000..d61dbffcba --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class RoomMemberProfileAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt new file mode 100644 index 0000000000..a17a969b41 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import android.content.Context +import android.content.Intent +import androidx.appcompat.widget.Toolbar +import im.vector.riotx.R +import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.platform.ToolbarConfigurable +import im.vector.riotx.core.platform.VectorBaseActivity + +class RoomMemberProfileActivity : VectorBaseActivity(), ToolbarConfigurable { + + companion object { + + private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" + + fun newIntent(context: Context, args: RoomMemberProfileArgs): Intent { + return Intent(context, RoomMemberProfileActivity::class.java).apply { + putExtra(EXTRA_FRAGMENT_ARGS, args) + } + } + } + + override fun getLayoutRes() = R.layout.activity_simple + + override fun initUiAndData() { + if (isFirstCreation()) { + val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) + ?: return + addFragment(R.id.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs) + } + } + + override fun configure(toolbar: Toolbar) { + configureToolbar(toolbar) + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt new file mode 100644 index 0000000000..cc5c228058 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import android.os.Bundle +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize +import com.airbnb.mvrx.args +import android.view.View +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorBaseFragment +import timber.log.Timber +import javax.inject.Inject + +@Parcelize +data class RoomMemberProfileArgs( + val userId: String, + val roomId: String? = null +) : Parcelable + +class RoomMemberProfileFragment @Inject constructor( + val viewModelFactory: RoomMemberProfileViewModel.Factory +) : VectorBaseFragment() { + + private val fragmentArgs: RoomMemberProfileArgs by args() + private val viewModel: RoomMemberProfileViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_room_member_profile + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // Initialize your view, subscribe to viewModel... + } + + override fun invalidate() = withState(viewModel) { state -> + Timber.v("Invalidate: $state") + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt new file mode 100644 index 0000000000..d563ec37d2 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.session.Session +import im.vector.riotx.core.platform.VectorViewModel +import timber.log.Timber + +class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberProfileViewState, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomMemberProfileViewState): RoomMemberProfileViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomMemberProfileViewState): RoomMemberProfileViewModel? { + val fragment: RoomMemberProfileFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: RoomMemberProfileAction) { + Timber.v("Handle $action") + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt new file mode 100644 index 0000000000..032bf853f4 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import com.airbnb.mvrx.MvRxState + +data class RoomMemberProfileViewState( + val userId: String, + val roomId: String? +) : MvRxState { + + constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId, userId = args.userId) + +} diff --git a/vector/src/main/res/layout/fragment_room_member_profile.xml b/vector/src/main/res/layout/fragment_room_member_profile.xml new file mode 100644 index 0000000000..3f6001fdab --- /dev/null +++ b/vector/src/main/res/layout/fragment_room_member_profile.xml @@ -0,0 +1,36 @@ + + + + + + + + From 289951ea4abc3525e9633c8e8db189a35c3d8fa3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Jan 2020 15:54:36 +0100 Subject: [PATCH 08/19] RoomMemberList : start showing items --- .../database/model/RoomMemberEntity.kt | 4 +- .../membership/RoomMemberEntityFactory.kt | 4 +- .../core/epoxy/profiles/ProfileMatrixItem.kt | 53 +++++++++++++ .../home/room/detail/RoomDetailFragment.kt | 2 +- .../RoomListQuickActionsEpoxyController.kt | 29 ++++--- .../features/navigation/DefaultNavigator.kt | 2 +- .../riotx/features/navigation/Navigator.kt | 2 +- .../features/permalink/PermalinkHandler.kt | 2 +- .../members/RoomMemberListController.kt | 49 ++++++++++++ .../members/RoomMemberListFragment.kt | 34 ++++++-- .../members/RoomMemberListViewModel.kt | 41 +++++++++- .../members/RoomMemberListViewState.kt | 15 +++- .../members/RoomMembersEpoxyController.kt | 27 ------- .../res/layout/fragment_room_member_list.xml | 64 +++++++++++++-- .../res/layout/item_profile_matrix_item.xml | 79 +++++++++++++++++++ 15 files changed, 343 insertions(+), 64 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt delete mode 100644 vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt create mode 100644 vector/src/main/res/layout/item_profile_matrix_item.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt index c532857fe1..d6e41f7705 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt @@ -24,8 +24,8 @@ import io.realm.annotations.PrimaryKey internal open class RoomMemberEntity(@PrimaryKey var primaryKey: String = "", @Index var userId: String = "", @Index var roomId: String = "", - var displayName: String = "", - var avatarUrl: String = "", + var displayName: String? = null, + var avatarUrl: String? = null , var reason: String? = null, var isDirect: Boolean = false ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt index 51df244401..bc970668bf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt @@ -27,8 +27,8 @@ internal object RoomMemberEntityFactory { primaryKey = primaryKey, userId = userId, roomId = roomId, - displayName = roomMember.displayName ?: "", - avatarUrl = roomMember.avatarUrl ?: "" + displayName = roomMember.displayName, + avatarUrl = roomMember.avatarUrl ).apply { membership = roomMember.membership } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt new file mode 100644 index 0000000000..4fe65748ce --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.core.epoxy.profiles + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.matrix.android.api.util.MatrixItem +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.epoxy.VectorEpoxyModel +import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.features.home.AvatarRenderer + +@EpoxyModelClass(layout = R.layout.item_profile_matrix_item) +abstract class ProfileMatrixItem : VectorEpoxyModel() { + + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute lateinit var matrixItem: MatrixItem + @EpoxyAttribute var clickListener: View.OnClickListener? = null + + override fun bind(holder: Holder) { + val bestName = matrixItem.getBestName() + val matrixId = matrixItem.id.takeIf { it != bestName } + holder.view.setOnClickListener(clickListener) + holder.titleView.text = bestName + holder.subtitleView.setTextOrHide(matrixId) + avatarRenderer.render(matrixItem, holder.avatarImageView) + } + + class Holder : VectorEpoxyHolder() { + val titleView by bind(R.id.matrixItemTitle) + val subtitleView by bind(R.id.matrixItemSubtitle) + val avatarImageView by bind(R.id.matrixItemAvatar) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 4998cad700..ee203b5b72 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -912,7 +912,7 @@ class RoomDetailFragment @Inject constructor( } override fun onAvatarClicked(informationData: MessageInformationData) { - navigator.openRoomMemberProfile(userId = informationData.senderId, context = requireActivity()) + navigator.openRoomMemberProfile(userId = informationData.senderId, roomId = roomDetailArgs.roomId, context = requireActivity()) } override fun onMemberNameClicked(informationData: MessageInformationData) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 05a93848b3..0cc409d53d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -19,6 +19,9 @@ import android.view.View import com.airbnb.epoxy.TypedEpoxyController import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.riotx.core.epoxy.DividerItem_ +import im.vector.riotx.core.epoxy.bottomsheet.BottomSheetRoomPreviewItem +import im.vector.riotx.core.epoxy.bottomsheet.BottomSheetRoomPreviewItem_ import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetActionItem import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem import im.vector.riotx.core.epoxy.dividerItem @@ -35,18 +38,21 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar override fun buildModels(state: RoomListQuickActionsState) { val roomSummary = state.roomSummary() ?: return + val showAll = state.mode == RoomListActionsArgs.Mode.FULL - // Preview - bottomSheetRoomPreviewItem { - id("preview") - avatarRenderer(avatarRenderer) - matrixItem(roomSummary.toMatrixItem()) - settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }) - } + if (showAll) { + // Preview + bottomSheetRoomPreviewItem { + id("room_preview") + avatarRenderer(avatarRenderer) + matrixItem(roomSummary.toMatrixItem()) + settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }) + } - // Notifications - dividerItem { - id("notifications_separator") + // Notifications + dividerItem { + id("notifications_separator") + } } val selectedRoomState = state.roomNotificationState() @@ -55,8 +61,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState) RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) - - if (state.mode == RoomListActionsArgs.Mode.FULL) { + if (showAll) { // Leave dividerItem { id("leave_separator") diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 76925d9af8..df9a142a9f 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -82,7 +82,7 @@ class DefaultNavigator @Inject constructor( } } - override fun openRoomMemberProfile(userId: String, context: Context, buildTask: Boolean) { + override fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean) { val args = RoomMemberProfileArgs(userId = userId) context.startActivity(RoomMemberProfileActivity.newIntent(context, args)) } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index 0e093910f0..4049c2e2c5 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -50,7 +50,7 @@ interface Navigator { fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean = false) - fun openRoomMemberProfile(userId: String, context: Context, buildTask: Boolean = false) + fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean = false) fun openRoomProfile(context: Context, roomId: String) diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt index 8386a8e45d..6b5f28860d 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt @@ -68,7 +68,7 @@ class PermalinkHandler @Inject constructor(private val session: Session, Single.just(true) } is PermalinkData.UserLink -> { - navigator.openRoomMemberProfile(permalinkData.userId, context, buildTask) + navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask) Single.just(true) } is PermalinkData.FallbackLink -> { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt new file mode 100644 index 0000000000..f9a9b1537d --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.members + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.riotx.core.epoxy.profiles.profileItemAction +import im.vector.riotx.core.epoxy.profiles.profileMatrixItem +import im.vector.riotx.features.autocomplete.autocompleteMatrixItem +import im.vector.riotx.features.home.AvatarRenderer +import javax.inject.Inject + +class RoomMemberListController @Inject constructor(private val avatarRenderer: AvatarRenderer) : TypedEpoxyController() { + + interface Callback { + fun onRoomMemberClicked(roomMember: RoomMember) + } + + var callback: Callback? = null + + override fun buildModels(data: RoomMemberListViewState?) { + data?.roomMembers?.invoke()?.forEach { roomMember -> + profileMatrixItem { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + clickListener { _ -> + callback?.onRoomMemberClicked(roomMember) + } + } + } + } + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index ae0e990c4a..12dec3783a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -21,33 +21,57 @@ import android.view.View import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.roomprofile.RoomProfileArgs +import kotlinx.android.synthetic.main.fragment_room_member_list.* +import kotlinx.android.synthetic.main.fragment_room_member_list.recyclerView import javax.inject.Inject class RoomMemberListFragment @Inject constructor( - val viewModelFactory: RoomMemberListViewModel.Factory -) : VectorBaseFragment() { + val viewModelFactory: RoomMemberListViewModel.Factory, + private val roomMemberListController: RoomMemberListController, + private val avatarRenderer: AvatarRenderer +) : VectorBaseFragment(), RoomMemberListController.Callback { private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val roomProfileArgs: RoomProfileArgs by args() + override fun getLayoutResId() = R.layout.fragment_room_member_list override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - // Initialize your view, subscribe to viewModel... + setupToolbar(roomMemberListToolbar) + roomMemberListController.callback = this + recyclerView.configureWith(roomMemberListController, hasFixedSize = true) } override fun onDestroyView() { + recyclerView.cleanup() super.onDestroyView() - // Clear your view, unsubscribe... } - override fun invalidate() = withState(viewModel) { _ -> + override fun invalidate() = withState(viewModel) { viewState -> + roomMemberListController.setData(viewState) + renderRoomSummary(viewState) + } + override fun onRoomMemberClicked(roomMember: RoomMember) { + navigator.openRoomMemberProfile(roomMember.userId, roomId = roomProfileArgs.roomId, context = requireActivity()) + } + + private fun renderRoomSummary(state: RoomMemberListViewState) { + state.roomSummary()?.let { + roomMemberListToolbarTitleView.text = it.displayName + avatarRenderer.render(it.toMatrixItem(), roomMemberListToolbarAvatarImageView) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index d1a4592965..38cb09a099 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -21,9 +21,19 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.query.QueryStringValue +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams +import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.rx.rx +import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.utils.DataSource +import im.vector.riotx.core.utils.PublishDataSource +import im.vector.riotx.features.roomprofile.RoomProfileViewEvents -class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState) +class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, + private val session: Session) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -40,8 +50,35 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } } + private val room = session.getRoom(initialState.roomId)!! + + init { + observeRoomMembers() + observeRoomSummary() + } + + private fun observeRoomSummary() { + room.rx().liveRoomSummary() + .unwrap() + .execute { async -> + copy(roomSummary = async) + } + } + + private fun observeRoomMembers() { + val queryParams = roomMemberQueryParams { + displayName = QueryStringValue.IsNotEmpty + memberships = Membership.activeMemberships() + } + room.rx() + .liveRoomMembers(queryParams) + .execute { + copy(roomMembers = it) + } + } + override fun handle(action: RoomMemberListAction) { - //TODO + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt index 2e432c59f5..223d2c5af6 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt @@ -16,8 +16,19 @@ package im.vector.riotx.features.roomprofile.members +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.features.roomprofile.RoomProfileArgs data class RoomMemberListViewState( - val noValue: Boolean = false -) : MvRxState + val roomId: String, + val roomSummary: Async = Uninitialized, + val roomMembers: Async> = Uninitialized +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt deleted file mode 100644 index 9fb6c400c3..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMembersEpoxyController.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.riotx.features.roomprofile.members - -import com.airbnb.epoxy.TypedEpoxyController - -class RoomMembersEpoxyController : TypedEpoxyController() { - - override fun buildModels(data: RoomMemberListViewState?) { - - } - -} diff --git a/vector/src/main/res/layout/fragment_room_member_list.xml b/vector/src/main/res/layout/fragment_room_member_list.xml index eb453ac2e8..ec38848f32 100644 --- a/vector/src/main/res/layout/fragment_room_member_list.xml +++ b/vector/src/main/res/layout/fragment_room_member_list.xml @@ -1,19 +1,67 @@ - - + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml new file mode 100644 index 0000000000..aa5d9e44cf --- /dev/null +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + From 81712ae7362813b1e8e6283c8ae2cba5aa7732a6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Jan 2020 19:47:54 +0100 Subject: [PATCH 09/19] RoomProfile: fix some rendering + change anim --- .../animations/AppBarStateChangeListener.kt | 53 +++++++++++ .../core/epoxy/profiles/ProfileItemSection.kt | 2 - .../roomprofile/RoomProfileFragment.kt | 28 ++++-- .../res/layout/fragment_room_member_list.xml | 2 - .../main/res/layout/fragment_room_profile.xml | 91 +++++++++++-------- .../main/res/layout/item_profile_section.xml | 2 +- 6 files changed, 126 insertions(+), 52 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt diff --git a/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt b/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt new file mode 100644 index 0000000000..a16b3b52d9 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.core.animations + +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener +import kotlin.math.abs + + +abstract class AppBarStateChangeListener : OnOffsetChangedListener { + + enum class State { + EXPANDED, COLLAPSED, IDLE + } + + private var currentState = State.IDLE + + override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) { + currentState = if (i == 0) { + if (currentState != State.EXPANDED) { + onStateChanged(appBarLayout, State.EXPANDED) + } + State.EXPANDED + } else if (abs(i) >= appBarLayout.totalScrollRange) { + if (currentState != State.COLLAPSED) { + onStateChanged(appBarLayout, State.COLLAPSED) + } + State.COLLAPSED + } else { + if (currentState != State.IDLE) { + onStateChanged(appBarLayout, State.IDLE) + } + State.IDLE + } + } + + abstract fun onStateChanged(appBarLayout: AppBarLayout, state: State) +} diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt index 24a9d1a57b..1ad6c12606 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt @@ -34,8 +34,6 @@ abstract class ProfileItemSection: VectorEpoxyModel() holder.sectionView.text = title } - - class Holder : VectorEpoxyHolder() { val sectionView by bind(R.id.itemProfileSectionView) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 0ebbbcde6b..50be4a2d87 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -29,15 +29,14 @@ import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.google.android.material.snackbar.Snackbar +import com.google.android.material.appbar.AppBarLayout import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R -import im.vector.riotx.core.error.ErrorFormatter +import im.vector.riotx.core.animations.AppBarStateChangeListener import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.AvatarRenderer -import im.vector.riotx.features.home.room.list.RoomListAction import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction @@ -72,6 +71,18 @@ class RoomProfileFragment @Inject constructor( roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) setupToolbar(roomProfileToolbar) setupRecyclerView() + roomProfileAppBarLayout.addOnOffsetChangedListener(object : AppBarStateChangeListener() { + override fun onStateChanged(appBarLayout: AppBarLayout, state: State) { + val animationDuration = roomProfileCollapsingToolbarLayout.scrimAnimationDuration + if (state == State.COLLAPSED) { + roomProfileToolbarAvatarImageView.animate().alpha(1f).duration = animationDuration + 100 + roomProfileToolbarTitleView.animate().alpha(1f).duration = animationDuration + 100 + } else { + roomProfileToolbarAvatarImageView.animate().alpha(0f).duration = animationDuration - 100 + roomProfileToolbarTitleView.animate().alpha(0f).duration = animationDuration - 100 + } + } + }) roomProfileViewModel.viewEvents .observe() .subscribe { @@ -135,17 +146,18 @@ class RoomProfileFragment @Inject constructor( } override fun invalidate() = withState(roomProfileViewModel) { state -> - state.roomSummary()?.let { + state.roomSummary()?.also { if (it.membership.isLeft()) { Timber.w("The room has been left") activity?.finish() } else { roomProfileNameView.text = it.displayName - roomProfileNameView2.text = it.displayName - // Use canonical alias when PR with alias management will be merged - roomProfileAliasView.text = it.roomId + roomProfileToolbarTitleView.text = it.displayName + roomProfileAliasView.setTextOrHide(it.canonicalAlias) roomProfileTopicView.setTextOrHide(it.topic) - avatarRenderer.render(it.toMatrixItem(), roomProfileAvatarView) + val matrixItem = it.toMatrixItem() + avatarRenderer.render(matrixItem, roomProfileAvatarView) + avatarRenderer.render(matrixItem, roomProfileToolbarAvatarImageView) } } roomProfileController.setData(state) diff --git a/vector/src/main/res/layout/fragment_room_member_list.xml b/vector/src/main/res/layout/fragment_room_member_list.xml index ec38848f32..16bb53a2aa 100644 --- a/vector/src/main/res/layout/fragment_room_member_list.xml +++ b/vector/src/main/res/layout/fragment_room_member_list.xml @@ -11,8 +11,6 @@ style="@style/VectorToolbarStyle" android:layout_width="0dp" android:layout_height="?actionBarSize" - android:elevation="4dp" - android:transitionName="toolbar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index 8b3ac8f608..78e54f76e2 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -13,8 +13,11 @@ android:layout_height="wrap_content"> + + @@ -60,7 +71,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginStart="40dp" - android:layout_marginTop="16dp" + android:layout_marginBottom="16dp" android:layout_marginEnd="40dp" android:fontFamily="sans-serif" android:gravity="center" @@ -74,7 +85,44 @@ android:id="@+id/roomProfileToolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - app:layout_collapseMode="pin" /> + android:background="@android:color/transparent" + app:layout_collapseMode="pin"> + + + + + + + + + + + @@ -88,39 +136,4 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:listitem="@layout/item_profile_action" /> - - - - \ No newline at end of file diff --git a/vector/src/main/res/layout/item_profile_section.xml b/vector/src/main/res/layout/item_profile_section.xml index e365733d79..89efade46b 100644 --- a/vector/src/main/res/layout/item_profile_section.xml +++ b/vector/src/main/res/layout/item_profile_section.xml @@ -13,5 +13,5 @@ android:textStyle="bold" android:textColor="?riotx_text_primary" android:background="?riotx_header_panel_background" - android:layout_height="68dp"/> + android:layout_height="56dp"/> From 171ec4fbdc7081a4e5ad2550071eeb37c395be1e Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 10 Jan 2020 17:03:11 +0100 Subject: [PATCH 10/19] Room member list: group by power level --- .../java/im/vector/matrix/rx/OptionalRx.kt | 6 + .../main/java/im/vector/matrix/rx/RxRoom.kt | 10 +- .../session/room/timeline/RoomDataHelper.kt | 4 +- .../SenderNotificationPermissionCondition.kt | 8 +- .../session/room/members/MembershipService.kt | 8 +- .../api/session/room/model/PowerLevels.kt | 121 ------------------ .../session/room/model/PowerLevelsContent.kt | 39 ++++++ .../{RoomMember.kt => RoomMemberSummary.kt} | 2 +- .../room/model/create/CreateRoomParams.kt | 4 +- .../room/powerlevers/PowerLevelsConstants.kt | 26 ++++ .../room/powerlevers/PowerLevelsHelper.kt | 77 +++++++++++ .../api/session/room/state/StateService.kt | 4 + .../matrix/android/api/util/MatrixItem.kt | 4 +- .../matrix/android/api/util/Optional.kt | 8 ++ .../internal/crypto/DefaultCryptoService.kt | 10 +- .../database/helper/RoomEntityHelper.kt | 4 +- .../helper/TimelineEventSenderVisitor.kt | 6 +- ...erMapper.kt => RoomMemberSummaryMapper.kt} | 22 ++-- ...erEntity.kt => RoomMemberSummaryEntity.kt} | 14 +- .../database/model/SessionRealmModule.kt | 2 +- .../database/query/EventEntityQueries.kt | 38 +++--- .../database/query/RoomMemberEntityQueries.kt | 12 +- .../session/room/RoomAvatarResolver.kt | 8 +- .../session/room/RoomSummaryUpdater.kt | 6 +- .../membership/DefaultMembershipService.kt | 24 ++-- .../membership/RoomDisplayNameResolver.kt | 22 ++-- .../membership/RoomMemberEntityFactory.kt | 6 +- .../{RoomMembers.kt => RoomMemberHelper.kt} | 28 ++-- .../session/room/state/DefaultStateService.kt | 25 +++- .../sync/UserAccountDataSyncHandler.kt | 4 +- .../epoxy/profiles/ProfileItemExtensions.kt | 55 ++++++++ .../member/AutocompleteMemberController.kt | 8 +- .../member/AutocompleteMemberPresenter.kt | 6 +- .../home/room/detail/AutoCompleter.kt | 8 +- .../home/room/detail/RoomDetailViewModel.kt | 4 +- .../home/room/detail/RoomDetailViewState.kt | 1 + .../roomprofile/RoomProfileController.kt | 50 ++------ .../members/RoomMemberListController.kt | 39 ++++-- .../members/RoomMemberListFragment.kt | 4 +- .../members/RoomMemberListViewModel.kt | 71 ++++++++-- .../members/RoomMemberListViewState.kt | 16 ++- .../main/res/layout/fragment_room_profile.xml | 2 +- vector/src/main/res/values/strings_riotX.xml | 6 + 43 files changed, 498 insertions(+), 324 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/{RoomMember.kt => RoomMemberSummary.kt} (96%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/{RoomMemberMapper.kt => RoomMemberSummaryMapper.kt} (59%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/{RoomMemberEntity.kt => RoomMemberSummaryEntity.kt} (67%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/{RoomMembers.kt => RoomMemberHelper.kt} (78%) create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt index d608837d4a..a8503ba428 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt @@ -22,3 +22,9 @@ import io.reactivex.Observable fun Observable>.unwrap(): Observable { return filter { it.hasValue() }.map { it.get() } } + +fun Observable>.mapOptional(fn: (T) -> U?): Observable> { + return map { + it.map(fn) + } +} diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index bbf0e76823..9491a69ef1 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -16,11 +16,12 @@ package im.vector.matrix.rx +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.ReadReceipt -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.session.room.send.UserDraft @@ -37,7 +38,7 @@ class RxRoom(private val room: Room) { .startWith(room.roomSummary().toOptional()) } - fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable> { + fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable> { return room.getRoomMembersLive(queryParams).asObservable() .startWith(room.getRoomMembers(queryParams)) } @@ -52,6 +53,11 @@ class RxRoom(private val room: Room) { .startWith(room.getTimeLineEvent(eventId).toOptional()) } + fun liveStateEvent(eventType: String): Observable> { + return room.getStateEventLive(eventType).asObservable() + .startWith(room.getStateEvent(eventType).toOptional()) + } + fun liveReadMarker(): Observable> { return room.getReadMarkerLive().asObservable() } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt index dd4daee9cd..102d9b65d3 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt @@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import kotlin.random.Random @@ -63,7 +63,7 @@ object RoomDataHelper { } fun createFakeRoomMemberEvent(): Event { - val roomMember = RoomMember(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() + val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt index 46fc9a5c6d..657ad29c87 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt @@ -16,7 +16,8 @@ package im.vector.matrix.android.api.pushrules import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.room.model.PowerLevels +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) { @@ -28,7 +29,8 @@ class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.se return "User power level <$key>" } - fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean { - return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key) + fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { + val powerLevelsHelper = PowerLevelsHelper(powerLevels) + return event.senderId != null && powerLevelsHelper.getUserPowerLevel(event.senderId) >= powerLevelsHelper.notificationLevel(key) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt index 6c117d3be7..98bce9476b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.api.session.room.members import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.Cancelable /** @@ -38,21 +38,21 @@ interface MembershipService { * * @return the roomMember with userId or null */ - fun getRoomMember(userId: String): RoomMember? + fun getRoomMember(userId: String): RoomMemberSummary? /** * Return all the roomMembers of the room with params * @param queryParams the params to query for * @return a roomMember list. */ - fun getRoomMembers(queryParams: RoomMemberQueryParams): List + fun getRoomMembers(queryParams: RoomMemberQueryParams): List /** * Return all the roomMembers of the room filtered by memberships * @param queryParams the params to query for * @return a [LiveData] of roomMember list. */ - fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> + fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> fun getNumberOfJoinedMembers(): Int diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt deleted file mode 100644 index 27f7820156..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.matrix.android.api.session.room.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.session.events.model.EventType - -/** - * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. - */ -@JsonClass(generateAdapter = true) -data class PowerLevels( - @Json(name = "ban") val ban: Int = 50, - @Json(name = "kick") val kick: Int = 50, - @Json(name = "invite") val invite: Int = 50, - @Json(name = "redact") val redact: Int = 50, - @Json(name = "events_default") val eventsDefault: Int = 0, - @Json(name = "events") val events: MutableMap = HashMap(), - @Json(name = "users_default") val usersDefault: Int = 0, - @Json(name = "users") val users: MutableMap = HashMap(), - @Json(name = "state_default") val stateDefault: Int = 50, - @Json(name = "notifications") val notifications: Map = HashMap() -) { - - /** - * Returns the user power level of a dedicated user Id - * - * @param userId the user id - * @return the power level - */ - fun getUserPowerLevel(userId: String): Int { - return users.getOrElse(userId) { usersDefault } - } - - /** - * Updates the user power levels of a dedicated user id - * - * @param userId the user - * @param powerLevel the new power level - */ - fun setUserPowerLevel(userId: String, powerLevel: Int) { - users[userId] = powerLevel - } - - /** - * Tell if an user can send an event of type 'eventTypeString'. - * - * @param eventTypeString the event type (in Event.EVENT_TYPE_XXX values) - * @param userId the user id - * @return true if the user can send the event - */ - fun maySendEventOfType(eventTypeString: String, userId: String): Boolean { - return if (eventTypeString.isNotEmpty() && userId.isNotEmpty()) { - getUserPowerLevel(userId) >= minimumPowerLevelForSendingEventAsMessage(eventTypeString) - } else false - } - - /** - * Tells if an user can send a room message. - * - * @param userId the user id - * @return true if the user can send a room message - */ - fun maySendMessage(userId: String): Boolean { - return maySendEventOfType(EventType.MESSAGE, userId) - } - - /** - * Helper to get the minimum power level the user must have to send an event of the given type - * as a message. - * - * @param eventTypeString the type of event (in Event.EVENT_TYPE_XXX values) - * @return the required minimum power level. - */ - fun minimumPowerLevelForSendingEventAsMessage(eventTypeString: String?): Int { - return events[eventTypeString] ?: eventsDefault - } - - /** - * Helper to get the minimum power level the user must have to send an event of the given type - * as a state event. - * - * @param eventTypeString the type of event (in Event.EVENT_TYPE_STATE_ values). - * @return the required minimum power level. - */ - fun minimumPowerLevelForSendingEventAsStateEvent(eventTypeString: String?): Int { - return events[eventTypeString] ?: stateDefault - } - - /** - * Get the notification level for a dedicated key. - * - * @param key the notification key - * @return the level - */ - fun notificationLevel(key: String): Int { - val valAsVoid = notifications[key] ?: return 50 - - // the first implementation was a string value - return if (valAsVoid is String) { - valAsVoid.toInt() - } else { - valAsVoid as Int - } - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt new file mode 100644 index 0000000000..3cbb435d76 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.api.session.room.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants + +/** + * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. + */ +@JsonClass(generateAdapter = true) +data class PowerLevelsContent( + @Json(name = "ban") val ban: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "kick") val kick: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "invite") val invite: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "redact") val redact: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "events_default") val eventsDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL, + @Json(name = "events") val events: MutableMap = HashMap(), + @Json(name = "users_default") val usersDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL, + @Json(name = "users") val users: MutableMap = HashMap(), + @Json(name = "state_default") val stateDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "notifications") val notifications: Map = HashMap() +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberSummary.kt similarity index 96% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberSummary.kt index 994c27be4d..17768362b2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberSummary.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.room.model /** * Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content */ -data class RoomMember( +data class RoomMemberSummary( val membership: Membership, val userId: String, val displayName: String? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index bc1e941698..74298b42d5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toContent -import im.vector.matrix.android.api.session.room.model.PowerLevels +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility import im.vector.matrix.android.internal.auth.data.ThreePidMedium @@ -113,7 +113,7 @@ class CreateRoomParams { * The power level content to override in the default power level event */ @Json(name = "power_level_content_override") - var powerLevelContentOverride: PowerLevels? = null + var powerLevelContentOverride: PowerLevelsContent? = null /** * Add the crypto algorithm to the room creation parameters. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt new file mode 100644 index 0000000000..f1610ead88 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.api.session.room.powerlevers + +object PowerLevelsConstants { + + const val DEFAULT_ROOM_ADMIN_LEVEL = 100 + const val DEFAULT_ROOM_MODERATOR_LEVEL = 50 + const val DEFAULT_ROOM_USER_LEVEL = 0 + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt new file mode 100644 index 0000000000..60bc5b3fc5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.api.session.room.powerlevers + +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent + +/** + * This class is an helper around PowerLevelsContent. + */ +class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { + + /** + * Returns the user power level of a dedicated user Id + * + * @param userId the user id + * @return the power level + */ + fun getUserPowerLevel(userId: String): Int { + return powerLevelsContent.users.getOrElse(userId) { + powerLevelsContent.usersDefault + } + } + + /** + * Tell if an user can send an event of a certain type + * + * @param eventType the event type to check for + * @param userId the user id + * @return true if the user can send this type of event + */ + fun isAllowedToSend(eventType: String, userId: String): Boolean { + return if (eventType.isNotEmpty() && userId.isNotEmpty()) { + val powerLevel = getUserPowerLevel(userId) + val minimumPowerLevel = powerLevelsContent.events[eventType] + ?: if (EventType.isStateEvent(eventType)) { + powerLevelsContent.stateDefault + } else { + powerLevelsContent.eventsDefault + } + powerLevel >= minimumPowerLevel + } else false + } + + + /** + * Get the notification level for a dedicated key. + * + * @param key the notification key + * @return the level + */ + fun notificationLevel(key: String): Int { + val value = powerLevelsContent.notifications[key] + ?: return PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL + return when (value) { + // the first implementation was a string value + is String -> value.toInt() + is Int -> value + else -> PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt index 06f4a9c7ee..09c77e9860 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt @@ -16,8 +16,10 @@ package im.vector.matrix.android.api.session.room.state +import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.util.Optional interface StateService { @@ -27,4 +29,6 @@ interface StateService { fun updateTopic(topic: String, callback: MatrixCallback) fun getStateEvent(eventType: String): Event? + + fun getStateEventLive(eventType: String): LiveData> } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt index d6ef522f41..5b3ca234ac 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.api.util import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.user.model.User @@ -147,4 +147,4 @@ fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, ava fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl) fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl) fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl) -fun RoomMember.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) +fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt index 2f602d0b84..a0f677d96d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt @@ -27,6 +27,14 @@ data class Optional constructor(private val value: T?) { return value } + fun map(fn: (T) -> U?): Optional { + return if (value == null) { + from(null) + } else { + from(fn(value)) + } + } + fun getOrElse(fn: () -> T): T { return value ?: fn() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index be8918dac7..793e19fc51 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -38,7 +38,7 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting @@ -64,7 +64,7 @@ import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -697,9 +697,9 @@ internal class DefaultCryptoService @Inject constructor( && shouldEncryptForInvitedMembers(roomId) userIds = if (encryptForInvitedMembers) { - RoomMembers(realm, roomId).getActiveRoomMemberIds() + RoomMemberHelper(realm, roomId).getActiveRoomMemberIds() } else { - RoomMembers(realm, roomId).getJoinedRoomMemberIds() + RoomMemberHelper(realm, roomId).getJoinedRoomMemberIds() } } return userIds @@ -722,7 +722,7 @@ internal class DefaultCryptoService @Inject constructor( return } event.stateKey?.let { userId -> - val roomMember: RoomMember? = event.content.toModel() + val roomMember: RoomMemberSummary? = event.content.toModel() val membership = roomMember?.membership if (membership == Membership.JOIN) { // make sure we are tracking the deviceList for this user. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt index 19c4715faa..b6c6c6c1ac 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.fastContains import im.vector.matrix.android.internal.extensions.assertIsManaged -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { chunks.remove(chunkEntity) @@ -59,7 +59,7 @@ internal fun RoomEntity.addSendingEvent(event: Event) { val eventEntity = event.toEntity(roomId).apply { this.sendState = SendState.UNSENT } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) val myUser = roomMembers.getLastRoomMember(senderId) val localId = TimelineEventEntity.nextId(realm) val timelineEventEntity = TimelineEventEntity(localId).also { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt index 983de3a50f..17103ce337 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt @@ -26,7 +26,7 @@ import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.session.SessionScope -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import io.realm.RealmList import io.realm.RealmQuery import javax.inject.Inject @@ -128,7 +128,7 @@ internal class TimelineEventSenderVisitor @Inject constructor() { ContentMapper.map(senderRoomMemberContent).toModel()?.also { result.senderAvatar = it.avatarUrl result.senderName = it.displayName - result.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName) + result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName) } // We try to fallback on prev content if we got a room member state events with null fields if (root?.type == EventType.STATE_ROOM_MEMBER) { @@ -138,7 +138,7 @@ internal class TimelineEventSenderVisitor @Inject constructor() { } if (result.senderName == null && it.displayName != null) { result.senderName = it.displayName - result.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName) + result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberSummaryMapper.kt similarity index 59% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberSummaryMapper.kt index a458c5e506..470772a40e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberSummaryMapper.kt @@ -16,21 +16,21 @@ package im.vector.matrix.android.internal.database.mapper -import im.vector.matrix.android.api.session.room.model.RoomMember -import im.vector.matrix.android.internal.database.model.RoomMemberEntity +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity -internal object RoomMemberMapper { +internal object RoomMemberSummaryMapper { - fun map(roomMemberEntity: RoomMemberEntity): RoomMember { - return RoomMember( - userId = roomMemberEntity.userId, - avatarUrl = roomMemberEntity.avatarUrl, - displayName = roomMemberEntity.displayName, - membership = roomMemberEntity.membership + fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary { + return RoomMemberSummary( + userId = roomMemberSummaryEntity.userId, + avatarUrl = roomMemberSummaryEntity.avatarUrl, + displayName = roomMemberSummaryEntity.displayName, + membership = roomMemberSummaryEntity.membership ) } } -internal fun RoomMemberEntity.asDomain(): RoomMember { - return RoomMemberMapper.map(this) +internal fun RoomMemberSummaryEntity.asDomain(): RoomMemberSummary { + return RoomMemberSummaryMapper.map(this) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberSummaryEntity.kt similarity index 67% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberSummaryEntity.kt index d6e41f7705..45bf1b3a22 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberSummaryEntity.kt @@ -21,13 +21,13 @@ import io.realm.RealmObject import io.realm.annotations.Index import io.realm.annotations.PrimaryKey -internal open class RoomMemberEntity(@PrimaryKey var primaryKey: String = "", - @Index var userId: String = "", - @Index var roomId: String = "", - var displayName: String? = null, - var avatarUrl: String? = null , - var reason: String? = null, - var isDirect: Boolean = false +internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "", + @Index var userId: String = "", + @Index var roomId: String = "", + var displayName: String? = null, + var avatarUrl: String? = null, + var reason: String? = null, + var isDirect: Boolean = false ) : RealmObject() { private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt index 07ff1df005..a29e7e89fb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt @@ -50,6 +50,6 @@ import io.realm.annotations.RealmModule UserDraftsEntity::class, DraftEntity::class, HomeServerCapabilitiesEntity::class, - RoomMemberEntity::class + RoomMemberSummaryEntity::class ]) internal class SessionRealmModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 62f53c3d7b..f3f8db0ea0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -60,20 +60,7 @@ internal fun EventEntity.Companion.types(realm: Realm, return query } -internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { - if (from != null) { - if (strict) { - this.greaterThan(EventEntityFields.STATE_INDEX, from) - } else { - this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from) - } - } - return this - .sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING) - .findFirst() -} - -internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = false): EventEntity? { +internal fun RealmQuery.descending(since: Int? = null, strict: Boolean = false): RealmQuery { if (since != null) { if (strict) { this.lessThan(EventEntityFields.STATE_INDEX, since) @@ -81,9 +68,26 @@ internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, since) } } - return this - .sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) - .findFirst() + return this.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) +} + +internal fun RealmQuery.ascending(from: Int? = null, strict: Boolean = true): RealmQuery { + if (from != null) { + if (strict) { + this.greaterThan(EventEntityFields.STATE_INDEX, from) + } else { + this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from) + } + } + return this.sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING) +} + +internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { + return this.ascending(from, strict).findFirst() +} + +internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = false): EventEntity? { + return descending(since, strict).findFirst() } internal fun RealmList.find(eventId: String): EventEntity? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt index 2ddade0048..e2dd2a7d8c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt @@ -16,19 +16,19 @@ package im.vector.matrix.android.internal.database.query -import im.vector.matrix.android.internal.database.model.RoomMemberEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where -internal fun RoomMemberEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery { +internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery { val query = realm - .where() - .equalTo(RoomMemberEntityFields.ROOM_ID, roomId) + .where() + .equalTo(RoomMemberSummaryEntityFields.ROOM_ID, roomId) if (userId != null) { - query.equalTo(RoomMemberEntityFields.USER_ID, userId) + query.equalTo(RoomMemberSummaryEntityFields.USER_ID, userId) } return query } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt index 0bb2dc0f27..2893bf5126 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt @@ -22,11 +22,11 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.RoomAvatarContent import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.UserId -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import javax.inject.Inject internal class RoomAvatarResolver @Inject constructor(private val monarchy: Monarchy, @@ -45,13 +45,13 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona if (!res.isNullOrEmpty()) { return@doWithRealm } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) val members = roomMembers.queryActiveRoomMembersEvent().findAll() // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) if (members.size == 1) { res = members.firstOrNull()?.avatarUrl } else if (members.size == 2) { - val firstOtherMember = members.where().notEqualTo(RoomMemberEntityFields.USER_ID, userId).findFirst() + val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() res = firstOtherMember?.avatarUrl } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index ea5c2e858c..009ca682ed 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -35,7 +35,7 @@ import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications import io.realm.Realm @@ -114,9 +114,9 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId roomSummaryEntity.isEncrypted = encryptionEvent != null if (updateMembers) { - val otherRoomMembers = RoomMembers(realm, roomId) + val otherRoomMembers = RoomMemberHelper(realm, roomId) .queryRoomMembersEvent() - .notEqualTo(RoomMemberEntityFields.USER_ID, userId) + .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) .findAll() .asSequence() .map { it.userId } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index 679f4a050b..53e0ee729e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -24,11 +24,11 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.members.MembershipService import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.RoomMemberEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields import im.vector.matrix.android.internal.query.process import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask @@ -62,14 +62,14 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr .executeBy(taskExecutor) } - override fun getRoomMember(userId: String): RoomMember? { + override fun getRoomMember(userId: String): RoomMemberSummary? { val roomMemberEntity = monarchy.fetchCopied { - RoomMembers(it, roomId).getLastRoomMember(userId) + RoomMemberHelper(it, roomId).getLastRoomMember(userId) } return roomMemberEntity?.asDomain() } - override fun getRoomMembers(queryParams: RoomMemberQueryParams): List { + override fun getRoomMembers(queryParams: RoomMemberQueryParams): List { return monarchy.fetchAllMappedSync( { roomMembersQuery(it, queryParams) @@ -80,7 +80,7 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr ) } - override fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> { + override fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> { return monarchy.findAllMappedWithChanges( { roomMembersQuery(it, queryParams) @@ -91,15 +91,15 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr ) } - private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { - return RoomMembers(realm, roomId).queryRoomMembersEvent() - .process(RoomMemberEntityFields.MEMBERSHIP_STR, queryParams.memberships) - .process(RoomMemberEntityFields.DISPLAY_NAME, queryParams.displayName) + private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { + return RoomMemberHelper(realm, roomId).queryRoomMembersEvent() + .process(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + .process(RoomMemberSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) } override fun getNumberOfJoinedMembers(): Int { return Realm.getInstance(monarchy.realmConfiguration).use { - RoomMembers(it, roomId).getNumberOfJoinedMembers() + RoomMemberHelper(it, roomId).getNumberOfJoinedMembers() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt index 9382fbc54a..67b222ecb8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -26,7 +26,7 @@ import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.RoomEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where @@ -75,7 +75,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: return@doWithRealm } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) val activeMembers = roomMembers.queryActiveRoomMembersEvent().findAll() if (roomEntity?.membership == Membership.INVITE) { @@ -83,7 +83,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: val inviterId = inviteMeEvent?.sender name = if (inviterId != null) { activeMembers.where() - .equalTo(RoomMemberEntityFields.USER_ID, inviterId) + .equalTo(RoomMemberSummaryEntityFields.USER_ID, inviterId) .findFirst() ?.displayName } else { @@ -91,7 +91,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: } } else if (roomEntity?.membership == Membership.JOIN) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() - val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { + val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { roomSummary.heroes.mapNotNull { userId -> roomMembers.getLastRoomMember(userId)?.takeIf { it.membership == Membership.INVITE || it.membership == Membership.JOIN @@ -99,7 +99,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: } } else { activeMembers.where() - .notEqualTo(RoomMemberEntityFields.USER_ID, userId) + .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) .limit(3) .findAll() .createSnapshot() @@ -123,14 +123,14 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: return name ?: roomId } - private fun resolveRoomMemberName(roomMember: RoomMemberEntity?, - roomMembers: RoomMembers): String? { - if (roomMember == null) return null - val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName) + private fun resolveRoomMemberName(roomMemberSummary: RoomMemberSummaryEntity?, + roomMemberHelper: RoomMemberHelper): String? { + if (roomMemberSummary == null) return null + val isUnique = roomMemberHelper.isUniqueDisplayName(roomMemberSummary.displayName) return if (isUnique) { - roomMember.displayName + roomMemberSummary.displayName } else { - "${roomMember.displayName} (${roomMember.userId})" + "${roomMemberSummary.displayName} (${roomMemberSummary.userId})" } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt index bc970668bf..291d2af3a6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt @@ -17,13 +17,13 @@ package im.vector.matrix.android.internal.session.room.membership import im.vector.matrix.android.api.session.room.model.RoomMemberContent -import im.vector.matrix.android.internal.database.model.RoomMemberEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity internal object RoomMemberEntityFactory { - fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberEntity { + fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberSummaryEntity { val primaryKey = "${roomId}_$userId" - return RoomMemberEntity( + return RoomMemberSummaryEntity( primaryKey = primaryKey, userId = userId, roomId = roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberHelper.kt similarity index 78% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberHelper.kt index e3775f5ade..029afcbe40 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberHelper.kt @@ -20,7 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.query.where import io.realm.Realm @@ -32,8 +32,8 @@ import io.realm.Sort * It allows to get the live membership of a user. */ -internal class RoomMembers(private val realm: Realm, - private val roomId: String +internal class RoomMemberHelper(private val realm: Realm, + private val roomId: String ) { private val roomSummary: RoomSummaryEntity? by lazy { @@ -48,8 +48,8 @@ internal class RoomMembers(private val realm: Realm, .findFirst() } - fun getLastRoomMember(userId: String): RoomMemberEntity? { - return RoomMemberEntity + fun getLastRoomMember(userId: String): RoomMemberSummaryEntity? { + return RoomMemberSummaryEntity .where(realm, roomId, userId) .findFirst() } @@ -66,26 +66,26 @@ internal class RoomMembers(private val realm: Realm, .size == 1 } - fun queryRoomMembersEvent(): RealmQuery { - return RoomMemberEntity.where(realm, roomId) + fun queryRoomMembersEvent(): RealmQuery { + return RoomMemberSummaryEntity.where(realm, roomId) } - fun queryJoinedRoomMembersEvent(): RealmQuery { + fun queryJoinedRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) } - fun queryInvitedRoomMembersEvent(): RealmQuery { + fun queryInvitedRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) } - fun queryActiveRoomMembersEvent(): RealmQuery { + fun queryActiveRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() .beginGroup() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) .or() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) .endGroup() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index 785fd9ae71..d52e5272cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -16,25 +16,34 @@ package im.vector.matrix.android.internal.session.room.state +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.state.StateService +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields +import im.vector.matrix.android.internal.database.query.descending import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.fetchCopied +import im.vector.matrix.android.internal.util.fetchCopyMap import io.realm.Realm import io.realm.RealmConfiguration internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String, - @SessionDatabase - private val realmConfiguration: RealmConfiguration, + private val monarchy: Monarchy, private val taskExecutor: TaskExecutor, private val sendStateTask: SendStateTask ) : StateService { @@ -45,11 +54,21 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private } override fun getStateEvent(eventType: String): Event? { - return Realm.getInstance(realmConfiguration).use { realm -> + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> EventEntity.where(realm, roomId, eventType).prev()?.asDomain() } } + override fun getStateEventLive(eventType: String): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { realm -> EventEntity.where(realm, roomId, eventType).descending()}, + { it.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.firstOrNull().toOptional() + } + } + override fun updateTopic(topic: String, callback: MatrixCallback) { val params = SendStateTask.Params(roomId, EventType.STATE_ROOM_TOPIC, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt index 9bc8c86be5..d7d19802c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt @@ -28,7 +28,7 @@ import im.vector.matrix.android.internal.database.query.getDirectRooms import im.vector.matrix.android.internal.database.query.getOrCreate import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.UserId -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync import im.vector.matrix.android.internal.session.sync.model.accountdata.* import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper @@ -69,7 +69,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc var hasUpdate = false monarchy.doWithRealm { realm -> invites.forEach { (roomId, _) -> - val myUserStateEvent = RoomMembers(realm, roomId).getLastStateEvent(userId) + val myUserStateEvent = RoomMemberHelper(realm, roomId).getLastStateEvent(userId) val inviterId = myUserStateEvent?.sender val myUserRoomMember: RoomMemberContent? = myUserStateEvent?.let { it.asDomain().content?.toModel() } val isDirect = myUserRoomMember?.isDirect diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt new file mode 100644 index 0000000000..4314a5d97a --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.core.epoxy.profiles + +import androidx.annotation.DrawableRes +import com.airbnb.epoxy.EpoxyController +import im.vector.riotx.core.epoxy.DividerItem_ + +fun EpoxyController.buildProfileSection(title: String) { + profileItemSection { + id("section_$title") + title(title) + } +} + +fun EpoxyController.buildProfileAction( + id: String, + title: String, + subtitle: String? = null, + @DrawableRes icon: Int = 0, + destructive: Boolean = false, + divider: Boolean = true, + action: () -> Unit +) { + + profileItemAction { + iconRes(icon) + id("action_$id") + subtitle(subtitle) + destructive(destructive) + title(title) + listener { _ -> + action() + } + } + + DividerItem_() + .id("divider_$title") + .addIf(divider, this) +} diff --git a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt index 1c8dc99196..8102a5a45f 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt @@ -17,20 +17,20 @@ package im.vector.riotx.features.autocomplete.member import com.airbnb.epoxy.TypedEpoxyController -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.autocompleteMatrixItem import im.vector.riotx.features.home.AvatarRenderer import javax.inject.Inject -class AutocompleteMemberController @Inject constructor() : TypedEpoxyController>() { +class AutocompleteMemberController @Inject constructor() : TypedEpoxyController>() { - var listener: AutocompleteClickListener? = null + var listener: AutocompleteClickListener? = null @Inject lateinit var avatarRenderer: AvatarRenderer - override fun buildModels(data: List?) { + override fun buildModels(data: List?) { if (data.isNullOrEmpty()) { return } diff --git a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt index 84a33173b8..970df6c403 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -25,14 +25,14 @@ import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.riotx.features.autocomplete.AutocompleteClickListener class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, @Assisted val roomId: String, private val session: Session, private val controller: AutocompleteMemberController -) : RecyclerViewPresenter(context), AutocompleteClickListener { +) : RecyclerViewPresenter(context), AutocompleteClickListener { private val room = session.getRoom(roomId)!! @@ -51,7 +51,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, return controller.adapter } - override fun onItemClick(t: RoomMember) { + override fun onItemClick(t: RoomMemberSummary) { dispatchClick(t) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt index 7ca647ea3e..314baab011 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt @@ -27,7 +27,7 @@ import com.otaliastudios.autocomplete.CharPolicy import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem @@ -108,13 +108,13 @@ class AutoCompleter @AssistedInject constructor( private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) { val autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId) - Autocomplete.on(editText) + Autocomplete.on(editText) .with(CharPolicy('@', true)) .with(autocompleteMemberPresenter) .with(ELEVATION) .with(backgroundDrawable) - .with(object : AutocompleteCallback { - override fun onPopupItemClicked(editable: Editable, item: RoomMember): Boolean { + .with(object : AutocompleteCallback { + override fun onPopupItemClicked(editable: Editable, item: RoomMemberSummary): Boolean { insertMatrixItem(editText, editable, "@", item.toMatrixItem()) return true } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index c93358a04e..e780d36411 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -40,7 +40,7 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageType @@ -210,7 +210,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro invisibleEventsObservable.accept(action) } - fun getMember(userId: String): RoomMember? { + fun getMember(userId: String): RoomMemberSummary? { return room.getRoomMember(userId) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt index 165ef7b625..588d398e7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.sync.SyncState diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index 390686567b..f2f266bc5a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -23,6 +23,8 @@ import com.airbnb.epoxy.TypedEpoxyController import im.vector.riotx.R import im.vector.riotx.core.epoxy.DividerItem_ import im.vector.riotx.core.epoxy.dividerItem +import im.vector.riotx.core.epoxy.profiles.buildProfileAction +import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.epoxy.profiles.profileItemAction import im.vector.riotx.core.epoxy.profiles.profileItemSection import im.vector.riotx.core.resources.StringProvider @@ -50,13 +52,13 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri val roomSummary = data.roomSummary() // Security - buildSection(stringProvider.getString(R.string.room_profile_section_security)) + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) val learnMoreSubtitle = if (data.isEncrypted) { R.string.room_profile_encrypted_subtitle } else { R.string.room_profile_not_encrypted_subtitle } - buildAction( + buildProfileAction( id = "learn_more", title = stringProvider.getString(R.string.room_profile_section_security_learn_more), subtitle = stringProvider.getString(learnMoreSubtitle), @@ -64,33 +66,33 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri ) // More - buildSection(stringProvider.getString(R.string.room_profile_section_more)) - buildAction( + buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + buildProfileAction( id = "settings", title = stringProvider.getString(R.string.room_profile_section_more_settings), icon = R.drawable.ic_room_profile_settings, action = { callback?.onSettingsClicked() } ) - buildAction( + buildProfileAction( id = "notifications", title = stringProvider.getString(R.string.room_profile_section_more_notifications), icon = R.drawable.ic_room_profile_notification, action = { callback?.onNotificationsClicked() } ) val numberOfMembers = roomSummary?.joinedMembersCount?.toString() ?: "-" - buildAction( + buildProfileAction( id = "member_list", title = stringProvider.getString(R.string.room_profile_section_more_member_list, numberOfMembers), icon = R.drawable.ic_room_profile_member_list, action = { callback?.onMemberListClicked() } ) - buildAction( + buildProfileAction( id = "uploads", title = stringProvider.getString(R.string.room_profile_section_more_uploads), icon = R.drawable.ic_room_profile_uploads, action = { callback?.onUploadsClicked() } ) - buildAction( + buildProfileAction( id = "leave", title = stringProvider.getString(R.string.room_profile_section_more_leave), divider = false, @@ -99,38 +101,6 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri ) } - private fun buildSection(title: String) { - profileItemSection { - id("section_$title") - title(title) - } - } - - private fun buildAction( - id: String, - title: String, - subtitle: String? = null, - @DrawableRes icon: Int = 0, - destructive: Boolean = false, - divider: Boolean = true, - action: () -> Unit - ) { - - profileItemAction { - iconRes(icon) - id("action_$id") - subtitle(subtitle) - destructive(destructive) - title(title) - listener { _ -> - action() - } - } - - DividerItem_() - .id("divider_$title") - .addIf(divider, this) - } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt index f9a9b1537d..857b03b263 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt @@ -17,33 +17,48 @@ package im.vector.riotx.features.roomprofile.members import com.airbnb.epoxy.TypedEpoxyController -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem -import im.vector.riotx.core.epoxy.profiles.profileItemAction +import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.epoxy.profiles.profileMatrixItem -import im.vector.riotx.features.autocomplete.autocompleteMatrixItem +import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.features.home.AvatarRenderer import javax.inject.Inject -class RoomMemberListController @Inject constructor(private val avatarRenderer: AvatarRenderer) : TypedEpoxyController() { +class RoomMemberListController @Inject constructor(private val avatarRenderer: AvatarRenderer, + private val stringProvider: StringProvider) : TypedEpoxyController() { interface Callback { - fun onRoomMemberClicked(roomMember: RoomMember) + fun onRoomMemberClicked(roomMember: RoomMemberSummary) } var callback: Callback? = null + init { + setData(null) + } + override fun buildModels(data: RoomMemberListViewState?) { - data?.roomMembers?.invoke()?.forEach { roomMember -> - profileMatrixItem { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) - avatarRenderer(avatarRenderer) - clickListener { _ -> - callback?.onRoomMemberClicked(roomMember) + val roomMembersByPowerLevel = data?.roomMemberSummaries?.invoke() ?: return + for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { + if (roomMemberList.isEmpty()) { + continue + } + buildProfileSection( + stringProvider.getString(powerLevelCategory.titleRes) + ) + roomMemberList.forEach { roomMember -> + profileMatrixItem { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + clickListener { _ -> + callback?.onRoomMemberClicked(roomMember) + } } } } } + } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index 12dec3783a..4b9955bf2f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -21,7 +21,7 @@ import android.view.View import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup @@ -63,7 +63,7 @@ class RoomMemberListFragment @Inject constructor( renderRoomSummary(viewState) } - override fun onRoomMemberClicked(roomMember: RoomMember) { + override fun onRoomMemberClicked(roomMember: RoomMemberSummary) { navigator.openRoomMemberProfile(roomMember.userId, roomId = roomProfileArgs.roomId, context = requireActivity()) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 38cb09a099..0056d332a0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -23,14 +23,20 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel -import im.vector.riotx.core.utils.DataSource -import im.vector.riotx.core.utils.PublishDataSource -import im.vector.riotx.features.roomprofile.RoomProfileViewEvents +import io.reactivex.Observable +import io.reactivex.functions.BiFunction class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, private val session: Session) @@ -53,10 +59,32 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState private val room = session.getRoom(initialState.roomId)!! init { - observeRoomMembers() + observeRoomMemberSummaries() observeRoomSummary() } + private fun observeRoomMemberSummaries() { + val roomMemberQueryParams = roomMemberQueryParams { + displayName = QueryStringValue.IsNotEmpty + memberships = Membership.activeMemberships() + } + Observable + .combineLatest, PowerLevelsContent, RoomMemberSummaries>( + room.rx().liveRoomMembers(roomMemberQueryParams), + room.rx() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + .mapOptional { it.content.toModel() } + .unwrap(), + BiFunction { roomMembers, powerLevelsContent -> + buildRoomMemberSummaries(powerLevelsContent, roomMembers) + } + ) + .execute { async -> + copy(roomMemberSummaries = async) + } + + } + private fun observeRoomSummary() { room.rx().liveRoomSummary() .unwrap() @@ -65,16 +93,33 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } } - private fun observeRoomMembers() { - val queryParams = roomMemberQueryParams { - displayName = QueryStringValue.IsNotEmpty - memberships = Membership.activeMemberships() - } - room.rx() - .liveRoomMembers(queryParams) - .execute { - copy(roomMembers = it) + + private fun buildRoomMemberSummaries(powerLevelsContent: PowerLevelsContent, roomMembers: List): RoomMemberSummaries { + val admins = ArrayList() + val moderators = ArrayList() + val users = ArrayList(roomMembers.size) + val customs = ArrayList() + val invites = ArrayList() + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + roomMembers + .forEach { roomMember -> + val memberPowerLevel = powerLevelsHelper.getUserPowerLevel(roomMember.userId) + when { + roomMember.membership == Membership.INVITE -> invites.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL -> admins.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL -> moderators.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL -> users.add(roomMember) + else -> customs.add(roomMember) + } } + + return mapOf( + PowerLevelCategory.ADMIN to admins, + PowerLevelCategory.MODERATOR to moderators, + PowerLevelCategory.CUSTOM to customs, + PowerLevelCategory.INVITE to invites, + PowerLevelCategory.USER to users + ) } override fun handle(action: RoomMemberListAction) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt index 223d2c5af6..bc616994ba 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt @@ -16,19 +16,31 @@ package im.vector.riotx.features.roomprofile.members +import androidx.annotation.StringRes import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.R import im.vector.riotx.features.roomprofile.RoomProfileArgs data class RoomMemberListViewState( val roomId: String, val roomSummary: Async = Uninitialized, - val roomMembers: Async> = Uninitialized + val roomMemberSummaries: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } + +typealias RoomMemberSummaries = Map> + +enum class PowerLevelCategory(@StringRes val titleRes: Int) { + ADMIN(R.string.room_member_power_level_admins), + MODERATOR(R.string.room_member_power_level_moderators), + CUSTOM(R.string.room_member_power_level_custom), + INVITE(R.string.room_member_power_level_invites), + USER(R.string.room_member_power_level_users) +} diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index 78e54f76e2..c9f79caec7 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -17,7 +17,7 @@ style="@style/VectorAppBarLayoutStyle" android:layout_width="match_parent" app:contentScrim="?riotx_background" - app:scrimAnimationDuration="400" + app:scrimAnimationDuration="250" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:titleEnabled="false" diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index fdfd8f85ef..72f3113308 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -35,4 +35,10 @@ Leave Room "Leaving the room..." + Admins + Moderators + Custom + Invites + Users + From ae1a24e948a73c19866cdf05eabd4f64b0f469f4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 13 Jan 2020 16:49:14 +0100 Subject: [PATCH 11/19] Room member profile: branch the UI and fix some UI issues --- .../room/members/RoomMemberQueryParams.kt | 7 +- .../membership/DefaultMembershipService.kt | 1 + .../MatrixItemAppBarStateChangeListener.kt | 36 ++++++ .../epoxy/profiles/ProfileItemExtensions.kt | 2 + .../riotx/core/platform/VectorBaseActivity.kt | 39 +++++-- .../riotx/core/platform/VectorBaseFragment.kt | 2 + .../features/navigation/DefaultNavigator.kt | 2 +- .../RoomMemberProfileAction.kt | 11 +- .../RoomMemberProfileController.kt | 107 ++++++++++++++++++ .../RoomMemberProfileFragment.kt | 93 +++++++++++++-- .../RoomMemberProfileViewModel.kt | 76 ++++++++++++- .../RoomMemberProfileViewState.kt | 24 +++- .../roomprofile/RoomProfileActivity.kt | 26 ++++- .../roomprofile/RoomProfileController.kt | 1 + .../roomprofile/RoomProfileFragment.kt | 49 ++++---- .../members/RoomMemberListController.kt | 5 + .../members/RoomMemberListFragment.kt | 7 +- ...rofile.xml => fragment_matrix_profile.xml} | 79 +++---------- .../res/layout/fragment_room_member_list.xml | 1 + .../layout/fragment_room_member_profile.xml | 36 ------ .../view_stub_room_member_profile_header.xml | 69 +++++++++++ .../layout/view_stub_room_profile_header.xml | 57 ++++++++++ vector/src/main/res/values/strings_riotX.xml | 7 ++ 23 files changed, 586 insertions(+), 151 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt rename vector/src/main/res/layout/{fragment_room_profile.xml => fragment_matrix_profile.xml} (54%) delete mode 100644 vector/src/main/res/layout/fragment_room_member_profile.xml create mode 100644 vector/src/main/res/layout/view_stub_room_member_profile_header.xml create mode 100644 vector/src/main/res/layout/view_stub_room_profile_header.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMemberQueryParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMemberQueryParams.kt index 19003632ca..7a8518c5de 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMemberQueryParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/RoomMemberQueryParams.kt @@ -28,17 +28,20 @@ fun roomMemberQueryParams(init: (RoomMemberQueryParams.Builder.() -> Unit) = {}) */ data class RoomMemberQueryParams( val displayName: QueryStringValue, - val memberships: List + val memberships: List, + val userId: QueryStringValue ) { class Builder { + var userId: QueryStringValue = QueryStringValue.NoCondition var displayName: QueryStringValue = QueryStringValue.IsNotEmpty var memberships: List = Membership.all() fun build() = RoomMemberQueryParams( displayName = displayName, - memberships = memberships + memberships = memberships, + userId = userId ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index 53e0ee729e..01646e4c17 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -93,6 +93,7 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { return RoomMemberHelper(realm, roomId).queryRoomMembersEvent() + .process(RoomMemberSummaryEntityFields.USER_ID, queryParams.userId) .process(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) .process(RoomMemberSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) } diff --git a/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt b/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt new file mode 100644 index 0000000000..6e34983018 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.core.animations + +import android.view.View +import com.google.android.material.appbar.AppBarLayout + +class MatrixItemAppBarStateChangeListener(private val animationDuration: Long, private val views: List) : AppBarStateChangeListener() { + + override fun onStateChanged(appBarLayout: AppBarLayout, state: State) { + if (state == State.COLLAPSED) { + views.forEach { + it.animate().alpha(1f).duration = animationDuration + 100 + } + } else { + views.forEach { + it.animate().alpha(0f).duration = animationDuration - 100 + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt index 4314a5d97a..7b9e6973c6 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt @@ -32,6 +32,7 @@ fun EpoxyController.buildProfileAction( id: String, title: String, subtitle: String? = null, + editable: Boolean = true, @DrawableRes icon: Int = 0, destructive: Boolean = false, divider: Boolean = true, @@ -42,6 +43,7 @@ fun EpoxyController.buildProfileAction( iconRes(icon) id("action_$id") subtitle(subtitle) + editable(editable) destructive(destructive) title(title) listener { _ -> diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index 40ce65e3ef..5c73fc97da 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -23,11 +23,18 @@ import android.os.Parcelable import android.view.Menu import android.view.MenuItem import android.view.View -import androidx.annotation.* +import androidx.annotation.AttrRes +import androidx.annotation.LayoutRes +import androidx.annotation.MainThread +import androidx.annotation.MenuRes +import androidx.annotation.Nullable +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentManager import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider @@ -41,7 +48,12 @@ import com.google.android.material.snackbar.Snackbar import im.vector.matrix.android.api.failure.GlobalError import im.vector.riotx.BuildConfig import im.vector.riotx.R -import im.vector.riotx.core.di.* +import im.vector.riotx.core.di.ActiveSessionHolder +import im.vector.riotx.core.di.DaggerScreenComponent +import im.vector.riotx.core.di.HasScreenInjector +import im.vector.riotx.core.di.HasVectorInjector +import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.di.VectorComponent import im.vector.riotx.core.dialogs.DialogLocker import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.utils.toast @@ -92,6 +104,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { lateinit var rageShake: RageShake private set protected lateinit var navigator: Navigator + private lateinit var fragmentFactory: FragmentFactory private lateinit var activeSessionHolder: ActiveSessionHolder private lateinit var vectorPreferences: VectorPreferences @@ -145,7 +158,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { } Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms") ThemeUtils.setActivityTheme(this, getOtherThemes()) - supportFragmentManager.fragmentFactory = screenComponent.fragmentFactory() + fragmentFactory = screenComponent.fragmentFactory() + supportFragmentManager.fragmentFactory = fragmentFactory super.onCreate(savedInstanceState) viewModelFactory = screenComponent.viewModelFactory() configurationViewModel = viewModelProvider.get(ConfigurationViewModel::class.java) @@ -196,7 +210,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { handleInvalidToken(globalError) is GlobalError.ConsentNotGivenError -> consentNotGivenHelper.displayDialog(globalError.consentUri, - activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host ?: "") + activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host + ?: "") } } @@ -209,11 +224,11 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { mainActivityStarted = true MainActivity.restartApp(this, - MainActivityArgs( - clearCredentials = !globalError.softLogout, - isUserLoggedOut = true, - isSoftLogout = globalError.softLogout - ) + MainActivityArgs( + clearCredentials = !globalError.softLogout, + isUserLoggedOut = true, + isSoftLogout = globalError.softLogout + ) ) } @@ -276,6 +291,12 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { protected open fun injectWith(injector: ScreenComponent) = Unit + protected fun createFragment(fragmentClass: Class, args: Bundle?): Fragment { + return fragmentFactory.instantiate(classLoader, fragmentClass.name).apply { + arguments = args + } + } + /* ========================================================================================== * PRIVATE METHODS * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index 3bb667593c..91ecf188dd 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -96,6 +96,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { } final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + Timber.i("onCreateView Fragment ${this.javaClass.simpleName}") return inflater.inflate(getLayoutResId(), container, false) } @@ -117,6 +118,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { @CallSuper override fun onDestroyView() { super.onDestroyView() + Timber.i("onDestroyView Fragment ${this.javaClass.simpleName}") mUnBinder?.unbind() mUnBinder = null uiDisposables.clear() diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index df9a142a9f..018f8a0702 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -83,7 +83,7 @@ class DefaultNavigator @Inject constructor( } override fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean) { - val args = RoomMemberProfileArgs(userId = userId) + val args = RoomMemberProfileArgs(userId = userId, roomId = roomId) context.startActivity(RoomMemberProfileActivity.newIntent(context, args)) } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index d61dbffcba..f4e27b5bc0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -19,4 +19,13 @@ package im.vector.riotx.features.roommemberprofile import im.vector.riotx.core.platform.VectorViewModelAction -sealed class RoomMemberProfileAction : VectorViewModelAction +sealed class RoomMemberProfileAction : VectorViewModelAction { + + sealed class Displayable : RoomMemberProfileAction() { + object JumpToReadReceipt : Displayable() + object Ignore : Displayable() + object Mention : Displayable() + } + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt new file mode 100644 index 0000000000..58d2746dcb --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.features.roommemberprofile + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.profiles.buildProfileAction +import im.vector.riotx.core.epoxy.profiles.buildProfileSection +import im.vector.riotx.core.resources.StringProvider +import javax.inject.Inject + +class RoomMemberProfileController @Inject constructor(private val stringProvider: StringProvider) + : TypedEpoxyController() { + + var callback: Callback? = null + + interface Callback { + fun onIgnoreClicked() + fun onLearnMoreClicked() + fun onJumpToReadReceiptClicked() + fun onMentionClicked() + } + + override fun buildModels(data: RoomMemberProfileViewState?) { + if (data == null) { + return + } + if (data.roomId == null) { + buildUserActions() + } else { + buildRoomMemberActions(data) + } + } + + private fun buildUserActions() { + // More + buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + buildProfileAction( + id = "ignore", + title = stringProvider.getString(R.string.ignore), + destructive = true, + editable = false, + action = { callback?.onIgnoreClicked() } + ) + } + + private fun buildRoomMemberActions(data: RoomMemberProfileViewState) { + val roomSummaryEntity = data.roomSummary() ?: return + + // Security + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) + val learnMoreSubtitle = if (roomSummaryEntity.isEncrypted) { + R.string.room_profile_encrypted_subtitle + } else { + R.string.room_profile_not_encrypted_subtitle + } + buildProfileAction( + id = "learn_more", + title = stringProvider.getString(R.string.room_profile_section_security_learn_more), + editable = false, + subtitle = stringProvider.getString(learnMoreSubtitle), + action = { callback?.onLearnMoreClicked() } + ) + + // More + if (!data.isMine) { + buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + buildProfileAction( + id = "read_receipt", + editable = false, + title = stringProvider.getString(R.string.room_member_jump_to_read_receipt), + action = { callback?.onJumpToReadReceiptClicked() } + ) + buildProfileAction( + id = "mention", + title = stringProvider.getString(R.string.room_participants_action_mention), + editable = false, + action = { callback?.onMentionClicked() } + ) + buildProfileAction( + id = "ignore", + title = stringProvider.getString(R.string.ignore), + destructive = true, + editable = false, + action = { callback?.onIgnoreClicked() } + ) + } + + } + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index cc5c228058..eecfe65d88 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -19,14 +19,25 @@ package im.vector.riotx.features.roommemberprofile import android.os.Bundle import android.os.Parcelable -import kotlinx.android.parcel.Parcelize -import com.airbnb.mvrx.args import android.view.View +import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R +import im.vector.riotx.core.animations.AppBarStateChangeListener +import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment -import timber.log.Timber +import im.vector.riotx.features.home.AvatarRenderer +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_matrix_profile.* +import kotlinx.android.synthetic.main.fragment_matrix_profile.matrixProfileHeaderView +import kotlinx.android.synthetic.main.view_stub_room_member_profile_header.* import javax.inject.Inject @Parcelize @@ -36,21 +47,87 @@ data class RoomMemberProfileArgs( ) : Parcelable class RoomMemberProfileFragment @Inject constructor( - val viewModelFactory: RoomMemberProfileViewModel.Factory -) : VectorBaseFragment() { + val viewModelFactory: RoomMemberProfileViewModel.Factory, + private val roomMemberProfileController: RoomMemberProfileController, + private val avatarRenderer: AvatarRenderer +) : VectorBaseFragment(), RoomMemberProfileController.Callback { private val fragmentArgs: RoomMemberProfileArgs by args() private val viewModel: RoomMemberProfileViewModel by fragmentViewModel() - override fun getLayoutResId() = R.layout.fragment_room_member_profile + private lateinit var appBarStateChangeListener: AppBarStateChangeListener + + override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - // Initialize your view, subscribe to viewModel... + setupToolbar(matrixProfileToolbar) + matrixProfileHeaderView.apply { + layoutResource = R.layout.view_stub_room_member_profile_header + inflate() + } + matrixProfileRecyclerView.configureWith(roomMemberProfileController, hasFixedSize = true) + roomMemberProfileController.callback = this + appBarStateChangeListener = MatrixItemAppBarStateChangeListener(matrixProfileCollapsingToolbarLayout.scrimAnimationDuration, listOf(matrixProfileToolbarAvatarImageView, matrixProfileToolbarTitleView)) + matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) } + override fun onDestroyView() { + matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) + roomMemberProfileController.callback = null + matrixProfileRecyclerView.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { state -> - Timber.v("Invalidate: $state") + val memberMatrixItem = state.memberAsMatrixItem() ?: return@withState + memberProfileIdView.text = memberMatrixItem.id + val bestName = memberMatrixItem.getBestName() + memberProfileNameView.text = bestName + matrixProfileToolbarTitleView.text = bestName + avatarRenderer.render(memberMatrixItem, memberProfileAvatarView) + avatarRenderer.render(memberMatrixItem, matrixProfileToolbarAvatarImageView) + + val roomSummary = state.roomSummary() + val powerLevelsContent = state.powerLevelsContent() + if (powerLevelsContent == null || roomSummary == null) { + memberProfilePowerLevelView.visibility = View.GONE + } else { + val roomName = roomSummary.toMatrixItem().getBestName() + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + val userPowerLevel = powerLevelsHelper.getUserPowerLevel(state.userId) + val powerLevelText = if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL) { + getString(R.string.room_member_power_level_admin_in, roomName) + } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL) { + getString(R.string.room_member_power_level_moderator_in, roomName) + } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL) { + null + } else { + getString(R.string.room_member_power_level_custom_in, userPowerLevel, roomName) + } + memberProfilePowerLevelView.setTextOrHide(powerLevelText) + } + roomMemberProfileController.setData(state) } + // RoomMemberProfileController.Callback + + override fun onIgnoreClicked() { + vectorBaseActivity.notImplemented("Ignore") + } + + override fun onLearnMoreClicked() { + vectorBaseActivity.notImplemented("Learn more") + } + + override fun onJumpToReadReceiptClicked() { + vectorBaseActivity.notImplemented("Jump to read receipts") + } + + override fun onMentionClicked() { + vectorBaseActivity.notImplemented("Mention") + } + + } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index d563ec37d2..74c946b5eb 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -22,11 +22,20 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.rx.mapOptional +import im.vector.matrix.rx.rx +import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel import timber.log.Timber -class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberProfileViewState, +class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val session: Session) : VectorViewModel(initialState) { @@ -44,8 +53,73 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted initialSt } } + private val room = if (initialState.roomId != null) { + session.getRoom(initialState.roomId) + } else { + null + } + + init { + setState { copy(isMine = session.myUserId == this.userId) } + observeRoomSummary() + observeRoomMemberSummary() + observePowerLevel() + observeUserIfRequired() + } + + private fun observeUserIfRequired() { + if (initialState.roomId != null) { + return + } + session.rx().liveUser(initialState.userId) + .unwrap() + .execute { + copy(user = it) + } + } + override fun handle(action: RoomMemberProfileAction) { Timber.v("Handle $action") } + private fun observeRoomSummary() { + if (room == null) { + return + } + room.rx().liveRoomSummary() + .unwrap() + .execute { + copy(roomSummary = it) + } + } + + private fun observeRoomMemberSummary() { + if (room == null) { + return + } + val queryParams = roomMemberQueryParams { + this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) + } + room.rx().liveRoomMembers(queryParams) + .map { it.firstOrNull().toOptional() } + .unwrap() + .execute { + copy(roomMemberSummary = it) + } + } + + private fun observePowerLevel() { + if (room == null) { + return + } + room.rx() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + .mapOptional { it.content.toModel() } + .unwrap() + .execute { + copy(powerLevelsContent = it) + } + } + + } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index 032bf853f4..a1f099c5d3 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -17,13 +17,35 @@ package im.vector.riotx.features.roommemberprofile +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.user.model.User +import im.vector.matrix.android.api.util.MatrixItem +import im.vector.matrix.android.api.util.toMatrixItem data class RoomMemberProfileViewState( val userId: String, - val roomId: String? + val roomId: String?, + val isMine: Boolean = false, + val roomSummary: Async = Uninitialized, + val roomMemberSummary: Async = Uninitialized, + val user: Async = Uninitialized, + val powerLevelsContent: Async = Uninitialized ) : MvRxState { constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId, userId = args.userId) + + fun memberAsMatrixItem(): MatrixItem? { + return if (roomId == null) { + user.invoke()?.toMatrixItem() + } else { + roomMemberSummary.invoke()?.toMatrixItem() + } + } + } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index 14c3421e7f..bc8bc2a959 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -21,8 +21,8 @@ import android.content.Context import android.content.Intent import androidx.appcompat.widget.Toolbar import im.vector.riotx.R -import im.vector.riotx.core.extensions.addFragment -import im.vector.riotx.core.extensions.addFragmentToBackstack +import im.vector.riotx.core.extensions.commitTransaction +import im.vector.riotx.core.extensions.commitTransactionNow import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment @@ -32,6 +32,9 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { private const val EXTRA_ROOM_PROFILE_ARGS = "EXTRA_ROOM_PROFILE_ARGS" + private const val TAG_ROOM_PROFILE_FRAGMENT = "TAG_ROOM_PROFILE_FRAGMENT" + private const val TAG_ROOM_MEMBER_LIST_FRAGMENT = "TAG_ROOM_MEMBER_LIST_FRAGMENT" + fun newIntent(context: Context, roomId: String): Intent { val roomProfileArgs = RoomProfileArgs(roomId) @@ -50,7 +53,14 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) roomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) ?: return if (isFirstCreation()) { - addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) + val argsBundle = roomProfileArgs.toMvRxBundle() + val roomProfileFragment = createFragment(RoomProfileFragment::class.java, argsBundle) + val roomMemberListFragment = createFragment(RoomMemberListFragment::class.java, argsBundle) + supportFragmentManager.commitTransactionNow { + add(R.id.simpleFragmentContainer, roomProfileFragment, TAG_ROOM_PROFILE_FRAGMENT) + add(R.id.simpleFragmentContainer, roomMemberListFragment, TAG_ROOM_MEMBER_LIST_FRAGMENT) + detach(roomMemberListFragment) + } } sharedActionViewModel .observe() @@ -73,7 +83,15 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { } private fun openRoomMembers() { - addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) + val roomProfileFragment = supportFragmentManager.findFragmentByTag(TAG_ROOM_PROFILE_FRAGMENT) + ?: throw IllegalStateException("You should have a RoomProfileFragment") + val roomMemberListFragment = supportFragmentManager.findFragmentByTag(TAG_ROOM_MEMBER_LIST_FRAGMENT) + ?: throw IllegalStateException("You should have a RoomMemberListFragment") + supportFragmentManager.commitTransaction { + hide(roomProfileFragment) + attach(roomMemberListFragment) + addToBackStack(null) + } } override fun configure(toolbar: Toolbar) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index f2f266bc5a..814cdc7723 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -97,6 +97,7 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri title = stringProvider.getString(R.string.room_profile_section_more_leave), divider = false, destructive = true, + editable = false, action = { callback?.onLeaveRoomClicked() } ) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 50be4a2d87..b596c9725d 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -24,16 +24,16 @@ import android.os.Bundle import android.os.Parcelable import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.google.android.material.appbar.AppBarLayout import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R import im.vector.riotx.core.animations.AppBarStateChangeListener +import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.AvatarRenderer @@ -42,7 +42,8 @@ import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBotto import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import kotlinx.android.parcel.Parcelize -import kotlinx.android.synthetic.main.fragment_room_profile.* +import kotlinx.android.synthetic.main.fragment_matrix_profile.* +import kotlinx.android.synthetic.main.view_stub_room_profile_header.* import timber.log.Timber import javax.inject.Inject @@ -63,26 +64,22 @@ class RoomProfileFragment @Inject constructor( private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() - override fun getLayoutResId() = R.layout.fragment_room_profile + private lateinit var appBarStateChangeListener: AppBarStateChangeListener + + + override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) - setupToolbar(roomProfileToolbar) + matrixProfileHeaderView.apply { + layoutResource = R.layout.view_stub_room_profile_header + inflate() + } setupRecyclerView() - roomProfileAppBarLayout.addOnOffsetChangedListener(object : AppBarStateChangeListener() { - override fun onStateChanged(appBarLayout: AppBarLayout, state: State) { - val animationDuration = roomProfileCollapsingToolbarLayout.scrimAnimationDuration - if (state == State.COLLAPSED) { - roomProfileToolbarAvatarImageView.animate().alpha(1f).duration = animationDuration + 100 - roomProfileToolbarTitleView.animate().alpha(1f).duration = animationDuration + 100 - } else { - roomProfileToolbarAvatarImageView.animate().alpha(0f).duration = animationDuration - 100 - roomProfileToolbarTitleView.animate().alpha(0f).duration = animationDuration - 100 - } - } - }) + appBarStateChangeListener = MatrixItemAppBarStateChangeListener(matrixProfileCollapsingToolbarLayout.scrimAnimationDuration, listOf(matrixProfileToolbarAvatarImageView, matrixProfileToolbarTitleView)) + matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) roomProfileViewModel.viewEvents .observe() .subscribe { @@ -101,6 +98,11 @@ class RoomProfileFragment @Inject constructor( .disposeOnDestroyView() } + override fun onResume() { + super.onResume() + setupToolbar(matrixProfileToolbar) + } + private fun handleQuickActions(action: RoomListQuickActionsSharedAction) = when (action) { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.ALL_MESSAGES_NOISY)) @@ -135,14 +137,13 @@ class RoomProfileFragment @Inject constructor( private fun setupRecyclerView() { roomProfileController.callback = this - roomProfileRecyclerView.setHasFixedSize(true) - roomProfileRecyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - roomProfileRecyclerView.adapter = roomProfileController.adapter + matrixProfileRecyclerView.configureWith(roomProfileController, hasFixedSize = true) } override fun onDestroyView() { super.onDestroyView() - roomProfileRecyclerView.adapter = null + matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) + matrixProfileRecyclerView.cleanup() } override fun invalidate() = withState(roomProfileViewModel) { state -> @@ -152,12 +153,12 @@ class RoomProfileFragment @Inject constructor( activity?.finish() } else { roomProfileNameView.text = it.displayName - roomProfileToolbarTitleView.text = it.displayName + matrixProfileToolbarTitleView.text = it.displayName roomProfileAliasView.setTextOrHide(it.canonicalAlias) roomProfileTopicView.setTextOrHide(it.topic) val matrixItem = it.toMatrixItem() avatarRenderer.render(matrixItem, roomProfileAvatarView) - avatarRenderer.render(matrixItem, roomProfileToolbarAvatarImageView) + avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView) } } roomProfileController.setData(state) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt index 857b03b263..baf7c4d2e4 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt @@ -19,6 +19,7 @@ package im.vector.riotx.features.roomprofile.members import com.airbnb.epoxy.TypedEpoxyController import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.epoxy.profiles.profileMatrixItem import im.vector.riotx.core.resources.StringProvider @@ -56,6 +57,10 @@ class RoomMemberListController @Inject constructor(private val avatarRenderer: A callback?.onRoomMemberClicked(roomMember) } } + + dividerItem { + id("divider_${roomMember.userId}") + } } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index 4b9955bf2f..b2318259f9 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -30,7 +30,6 @@ import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_member_list.* -import kotlinx.android.synthetic.main.fragment_room_member_list.recyclerView import javax.inject.Inject @@ -48,11 +47,15 @@ class RoomMemberListFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupToolbar(roomMemberListToolbar) roomMemberListController.callback = this recyclerView.configureWith(roomMemberListController, hasFixedSize = true) } + override fun onResume() { + super.onResume() + setupToolbar(roomMemberListToolbar) + } + override fun onDestroyView() { recyclerView.cleanup() super.onDestroyView() diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_matrix_profile.xml similarity index 54% rename from vector/src/main/res/layout/fragment_room_profile.xml rename to vector/src/main/res/layout/fragment_matrix_profile.xml index c9f79caec7..384c87cccc 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_matrix_profile.xml @@ -7,82 +7,33 @@ android:background="?riotx_header_panel_background"> + app:toolbarId="@+id/matrixProfileToolbar"> - - - - - - - - - - - + app:layout_collapseParallaxMultiplier="0.9" /> @@ -130,7 +85,7 @@ - - - - - - - diff --git a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml new file mode 100644 index 0000000000..fc4836b80e --- /dev/null +++ b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/view_stub_room_profile_header.xml b/vector/src/main/res/layout/view_stub_room_profile_header.xml new file mode 100644 index 0000000000..6a0d0fc5de --- /dev/null +++ b/vector/src/main/res/layout/view_stub_room_profile_header.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 72f3113308..4fa6c5bbd4 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -41,4 +41,11 @@ Invites Users + Admin in %1$s + Moderator in %1$s + Custom (%1$d) in %2$s + + Jump to read receipt + + From 162f0949fa4f5693082d4b56c23cd9d0c52a9260 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 13 Jan 2020 18:44:01 +0100 Subject: [PATCH 12/19] Profile: Start fetching profile info from a user --- .../java/im/vector/matrix/rx/RxSession.kt | 10 +- .../matrix/android/api/session/Session.kt | 2 + .../api/session/profile/ProfileService.kt | 39 +++++ .../internal/session/DefaultSession.kt | 5 +- .../internal/session/SessionComponent.kt | 2 + .../session/profile/DefaultProfileService.kt | 77 +++++++++ .../session/profile/GetProfileInfoTask.kt | 40 +++++ .../internal/session/profile/ProfileAPI.kt | 37 +++++ .../internal/session/profile/ProfileModule.kt | 45 +++++ .../home/room/detail/RoomDetailFragment.kt | 155 +++++++++++------- .../features/permalink/PermalinkHandler.kt | 27 ++- .../RoomMemberProfileController.kt | 6 +- .../RoomMemberProfileFragment.kt | 16 +- .../RoomMemberProfileViewModel.kt | 12 +- .../RoomMemberProfileViewState.kt | 17 +- .../view_stub_room_member_profile_header.xml | 1 + 16 files changed, 404 insertions(+), 87 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index 084f497de5..65eae3924b 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -26,7 +26,9 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.user.model.User +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional import io.reactivex.Observable import io.reactivex.Single @@ -56,7 +58,8 @@ class RxSession(private val session: Session) { } fun liveUser(userId: String): Observable> { - return session.getUserLive(userId).asObservable().distinctUntilChanged() + return session.getUserLive(userId).asObservable() + .startWith(session.getUser(userId).toOptional()) } fun liveUsers(): Observable> { @@ -91,6 +94,11 @@ class RxSession(private val session: Session) { searchOnServer: Boolean): Single> = singleBuilder { session.getRoomIdByAlias(roomAlias, searchOnServer, it) } + + fun getProfileInfo(userId: String): Single = singleBuilder { + session.getProfile(userId, it) + } + } fun Session.rx(): RxSession { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 1c73d4c5d1..a0ac751a72 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService +import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.pushers.PushersService import im.vector.matrix.android.api.session.room.RoomDirectoryService import im.vector.matrix.android.api.session.room.RoomService @@ -51,6 +52,7 @@ interface Session : SignOutService, FilterService, FileService, + ProfileService, PushRuleService, PushersService, InitialSyncProgressService, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt new file mode 100644 index 0000000000..dc3fb941d1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.api.session.profile + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.api.util.Optional + + +interface ProfileService { + + companion object Constants { + const val DISPLAY_NAME_KEY = "displayname" + const val AVATAR_URL_KEY = "avatar_url" + } + + fun getDisplayName(userId: String, matrixCallback: MatrixCallback>): Cancelable + + fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback>): Cancelable + + fun getProfile(userId: String, matrixCallback: MatrixCallback): Cancelable + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index b0bf70eb70..483c7d7acf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -34,6 +34,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService +import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.pushers.PushersService import im.vector.matrix.android.api.session.room.RoomDirectoryService import im.vector.matrix.android.api.session.room.RoomService @@ -76,6 +77,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private val cryptoService: Lazy, private val fileService: Lazy, private val secureStorageService: Lazy, + private val profileService: Lazy, private val syncThreadProvider: Provider, private val contentUrlResolver: ContentUrlResolver, private val syncTokenStore: SyncTokenStore, @@ -97,7 +99,8 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se FileService by fileService.get(), InitialSyncProgressService by initialSyncProgressService.get(), SecureStorageService by secureStorageService.get(), - HomeServerCapabilitiesService by homeServerCapabilitiesService.get() { + HomeServerCapabilitiesService by homeServerCapabilitiesService.get(), + ProfileService by profileService.get() { private var isOpen = false diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt index a208a7a720..5b6aa57979 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.session.filter.FilterModule import im.vector.matrix.android.internal.session.group.GetGroupDataWorker import im.vector.matrix.android.internal.session.group.GroupModule import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule +import im.vector.matrix.android.internal.session.profile.ProfileModule import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker import im.vector.matrix.android.internal.session.pushers.PushersModule import im.vector.matrix.android.internal.session.room.RoomModule @@ -64,6 +65,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers CryptoModule::class, PushersModule::class, AccountDataModule::class, + ProfileModule::class, SessionAssistedInjectModule::class ] ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt new file mode 100644 index 0000000000..c10a486538 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.profile + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.profile.ProfileService +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject + +internal class DefaultProfileService @Inject constructor(private val taskExecutor: TaskExecutor, + private val getProfileInfoTask: GetProfileInfoTask) : ProfileService { + + override fun getDisplayName(userId: String, matrixCallback: MatrixCallback>): Cancelable { + val params = GetProfileInfoTask.Params(userId) + return getProfileInfoTask + .configureWith(params) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: JsonDict) { + val displayName = data[ProfileService.DISPLAY_NAME_KEY] as? String + matrixCallback.onSuccess(Optional.from(displayName)) + } + + override fun onFailure(failure: Throwable) { + matrixCallback.onFailure(failure) + } + } + } + .executeBy(taskExecutor) + } + + override fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback>): Cancelable { + val params = GetProfileInfoTask.Params(userId) + return getProfileInfoTask + .configureWith(params) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: JsonDict) { + val avatarUrl = data[ProfileService.AVATAR_URL_KEY] as? String + matrixCallback.onSuccess(Optional.from(avatarUrl)) + } + + override fun onFailure(failure: Throwable) { + matrixCallback.onFailure(failure) + } + } + } + .executeBy(taskExecutor) + } + + override fun getProfile(userId: String, matrixCallback: MatrixCallback): Cancelable { + val params = GetProfileInfoTask.Params(userId) + return getProfileInfoTask + .configureWith(params) { + this.callback = matrixCallback + } + .executeBy(taskExecutor) + } + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt new file mode 100644 index 0000000000..69db2d7b99 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.profile + +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject + +internal abstract class GetProfileInfoTask : Task { + data class Params( + val userId: String + ) +} + +internal class DefaultGetProfileInfoTask @Inject constructor(private val profileAPI: ProfileAPI) : GetProfileInfoTask() { + + override suspend fun execute(params: Params): JsonDict { + return executeRequest { + apiCall = profileAPI.getProfile(params.userId) + } + } +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt new file mode 100644 index 0000000000..34d1eb600a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.profile + +import im.vector.matrix.android.api.util.JsonDict +import im.vector.matrix.android.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path + +interface ProfileAPI { + + /** + * Get the combined profile information for this user. This API may be used to fetch the user's own profile information or other users; either locally or on remote homeservers. This API may return keys which are not limited to displayname or avatar_url. + * + * @param userId the user id to fetch profile info + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}") + fun getProfile(@Path("userId") roomId: String): Call + + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt new file mode 100644 index 0000000000..a3084033a3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.matrix.android.internal.session.profile + +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.profile.ProfileService +import im.vector.matrix.android.internal.session.SessionScope +import retrofit2.Retrofit + +@Module +internal abstract class ProfileModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesProfileAPI(retrofit: Retrofit): ProfileAPI { + return retrofit.create(ProfileAPI::class.java) + } + } + + @Binds + abstract fun bindProfileService(userService: DefaultProfileService): ProfileService + + @Binds + abstract fun bindGetProfileTask(getProfileInfoTask: DefaultGetProfileInfoTask): GetProfileInfoTask +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index ee203b5b72..e1832dd7d8 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -25,7 +25,12 @@ import android.os.Build import android.os.Bundle import android.os.Parcelable import android.text.Spannable -import android.view.* +import android.view.HapticFeedbackConstants +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.Window import android.widget.TextView import android.widget.Toast import androidx.annotation.DrawableRes @@ -45,7 +50,13 @@ import butterknife.BindView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.epoxy.OnModelBuildFinishedListener -import com.airbnb.mvrx.* +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import com.github.piasy.biv.BigImageViewer import com.github.piasy.biv.loader.ImageLoader import com.google.android.material.snackbar.Snackbar @@ -55,7 +66,13 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.message.* +import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent +import im.vector.matrix.android.api.session.room.model.message.MessageContent +import im.vector.matrix.android.api.session.room.model.message.MessageFileContent +import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent +import im.vector.matrix.android.api.session.room.model.message.MessageTextContent +import im.vector.matrix.android.api.session.room.model.message.MessageType +import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent @@ -65,13 +82,31 @@ import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R import im.vector.riotx.core.dialogs.withColoredButton import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer -import im.vector.riotx.core.extensions.* +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.hideKeyboard +import im.vector.riotx.core.extensions.observeEvent +import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.core.extensions.showKeyboard import im.vector.riotx.core.files.addEntryToDownloadManager import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.ui.views.JumpToReadMarkerView import im.vector.riotx.core.ui.views.NotificationAreaView -import im.vector.riotx.core.utils.* +import im.vector.riotx.core.utils.Debouncer +import im.vector.riotx.core.utils.KeyboardStateUtils +import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_DOWNLOAD_FILE +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_INCOMING_URI +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_PICK_ATTACHMENT +import im.vector.riotx.core.utils.TextUtils +import im.vector.riotx.core.utils.allGranted +import im.vector.riotx.core.utils.checkPermissions +import im.vector.riotx.core.utils.copyToClipboard +import im.vector.riotx.core.utils.createUIHandler +import im.vector.riotx.core.utils.getColorFromUserId +import im.vector.riotx.core.utils.openUrlInExternalBrowser +import im.vector.riotx.core.utils.shareMedia +import im.vector.riotx.core.utils.toast import im.vector.riotx.features.attachments.AttachmentTypeSelectorView import im.vector.riotx.features.attachments.AttachmentsHelper import im.vector.riotx.features.attachments.ContactAttachment @@ -84,7 +119,12 @@ import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedActi import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet -import im.vector.riotx.features.home.room.detail.timeline.item.* +import im.vector.riotx.features.home.room.detail.timeline.item.AbsMessageItem +import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem +import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem +import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem +import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.riotx.features.html.EventHtmlRenderer import im.vector.riotx.features.html.PillImageSpan @@ -94,7 +134,7 @@ import im.vector.riotx.features.media.ImageMediaViewerActivity import im.vector.riotx.features.media.VideoContentRenderer import im.vector.riotx.features.media.VideoMediaViewerActivity import im.vector.riotx.features.notifications.NotificationDrawerManager -import im.vector.riotx.features.permalink.NavigateToRoomInterceptor +import im.vector.riotx.features.permalink.NavigationInterceptor import im.vector.riotx.features.permalink.PermalinkHandler import im.vector.riotx.features.reactions.EmojiReactionPickerActivity import im.vector.riotx.features.settings.VectorPreferences @@ -247,9 +287,9 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode) { mode -> when (mode) { is SendMode.REGULAR -> renderRegularMode(mode.text) - is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text) - is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text) - is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text) + is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text) + is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text) + is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text) } } @@ -276,9 +316,9 @@ class RoomDetailFragment @Inject constructor( super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { when (val sharedData = roomDetailArgs.sharedData) { - is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false)) + is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false)) is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailAction.SendMedia(sharedData.attachmentData)) - null -> Timber.v("No share data to process") + null -> Timber.v("No share data to process") } } } @@ -415,7 +455,7 @@ class RoomDetailFragment @Inject constructor( avatarRenderer.render( MatrixItem.UserItem(event.root.senderId - ?: "", event.getDisambiguatedDisplayName(), event.senderAvatar), + ?: "", event.getDisambiguatedDisplayName(), event.senderAvatar), composerLayout.composerRelatedMessageAvatar ) @@ -504,7 +544,7 @@ class RoomDetailFragment @Inject constructor( is MessageTextItem -> { return (model as AbsMessageItem).attributes.informationData.sendState == SendState.SYNCED } - else -> false + else -> false } } } @@ -519,9 +559,9 @@ class RoomDetailFragment @Inject constructor( withState(roomDetailViewModel) { val showJumpToUnreadBanner = when (it.unreadState) { UnreadState.Unknown, - UnreadState.HasNoUnread -> false + UnreadState.HasNoUnread -> false is UnreadState.ReadMarkerNotLoaded -> true - is UnreadState.HasUnread -> { + is UnreadState.HasUnread -> { if (it.canShowJumpToReadMarker) { val lastVisibleItem = layoutManager.findLastVisibleItemPosition() val positionOfReadMarker = timelineEventController.getPositionOfReadMarker() @@ -649,7 +689,7 @@ class RoomDetailFragment @Inject constructor( navigator.openRoom(vectorBaseActivity, async()) vectorBaseActivity.finish() } - is Fail -> { + is Fail -> { vectorBaseActivity.hideWaitingView() vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error)) } @@ -658,23 +698,23 @@ class RoomDetailFragment @Inject constructor( private fun renderSendMessageResult(sendMessageResult: SendMessageResult) { when (sendMessageResult) { - is SendMessageResult.MessageSent -> { + is SendMessageResult.MessageSent -> { updateComposerText("") } - is SendMessageResult.SlashCommandHandled -> { + is SendMessageResult.SlashCommandHandled -> { sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) } updateComposerText("") } - is SendMessageResult.SlashCommandError -> { + is SendMessageResult.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) } - is SendMessageResult.SlashCommandUnknown -> { + is SendMessageResult.SlashCommandUnknown -> { displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) } - is SendMessageResult.SlashCommandResultOk -> { + is SendMessageResult.SlashCommandResultOk -> { updateComposerText("") } - is SendMessageResult.SlashCommandResultError -> { + is SendMessageResult.SlashCommandResultError -> { displayCommandError(sendMessageResult.throwable.localizedMessage) } is SendMessageResult.SlashCommandNotImplemented -> { @@ -712,7 +752,7 @@ class RoomDetailFragment @Inject constructor( private fun displayRoomDetailActionResult(result: Async) { when (result) { - is Fail -> { + is Fail -> { AlertDialog.Builder(requireActivity()) .setTitle(R.string.dialog_title_error) .setMessage(errorFormatter.toHumanReadable(result.error)) @@ -723,7 +763,7 @@ class RoomDetailFragment @Inject constructor( when (val data = result.invoke()) { is RoomDetailAction.ReportContent -> { when { - data.spam -> { + data.spam -> { AlertDialog.Builder(requireActivity()) .setTitle(R.string.content_reported_as_spam_title) .setMessage(R.string.content_reported_as_spam_content) @@ -745,7 +785,7 @@ class RoomDetailFragment @Inject constructor( .show() .withColoredButton(DialogInterface.BUTTON_NEGATIVE) } - else -> { + else -> { AlertDialog.Builder(requireActivity()) .setTitle(R.string.content_reported_title) .setMessage(R.string.content_reported_content) @@ -767,7 +807,7 @@ class RoomDetailFragment @Inject constructor( override fun onUrlClicked(url: String): Boolean { permalinkHandler - .launch(requireActivity(), url, object : NavigateToRoomInterceptor { + .launch(requireActivity(), url, object : NavigationInterceptor { override fun navToRoom(roomId: String?, eventId: String?): Boolean { // Same room? if (roomId == roomDetailArgs.roomId) { @@ -783,6 +823,11 @@ class RoomDetailFragment @Inject constructor( // Not handled return false } + + override fun navToMemberProfile(userId: String): Boolean { + navigator.openRoomMemberProfile(userId, roomDetailArgs.roomId, vectorBaseActivity) + return true + } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -858,14 +903,14 @@ class RoomDetailFragment @Inject constructor( override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { if (allGranted(grantResults)) { when (requestCode) { - PERMISSION_REQUEST_CODE_DOWNLOAD_FILE -> { + PERMISSION_REQUEST_CODE_DOWNLOAD_FILE -> { val action = roomDetailViewModel.pendingAction if (action != null) { roomDetailViewModel.pendingAction = null roomDetailViewModel.handle(action) } } - PERMISSION_REQUEST_CODE_INCOMING_URI -> { + PERMISSION_REQUEST_CODE_INCOMING_URI -> { val pendingUri = roomDetailViewModel.pendingUri if (pendingUri != null) { roomDetailViewModel.pendingUri = null @@ -941,7 +986,7 @@ class RoomDetailFragment @Inject constructor( override fun onRoomCreateLinkClicked(url: String) { permalinkHandler - .launch(requireContext(), url, object : NavigateToRoomInterceptor { + .launch(requireContext(), url, object : NavigationInterceptor { override fun navToRoom(roomId: String?, eventId: String?): Boolean { requireActivity().finish() return false @@ -963,23 +1008,23 @@ class RoomDetailFragment @Inject constructor( private fun handleActions(action: EventSharedAction) { when (action) { - is EventSharedAction.AddReaction -> { + is EventSharedAction.AddReaction -> { startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) } - is EventSharedAction.ViewReactions -> { + is EventSharedAction.ViewReactions -> { ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS") } - is EventSharedAction.Copy -> { + is EventSharedAction.Copy -> { // I need info about the current selected message :/ copyToClipboard(requireContext(), action.content, false) val msg = requireContext().getString(R.string.copied_to_clipboard) showSnackWithMessage(msg, Snackbar.LENGTH_SHORT) } - is EventSharedAction.Delete -> { + is EventSharedAction.Delete -> { roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) } - is EventSharedAction.Share -> { + is EventSharedAction.Share -> { // TODO current data communication is too limited // Need to now the media type // TODO bad, just POC @@ -1007,10 +1052,10 @@ class RoomDetailFragment @Inject constructor( } ) } - is EventSharedAction.ViewEditHistory -> { + is EventSharedAction.ViewEditHistory -> { onEditedDecorationClicked(action.messageInformationData) } - is EventSharedAction.ViewSource -> { + is EventSharedAction.ViewSource -> { val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) view.findViewById(R.id.event_content_text_view)?.let { it.text = action.content @@ -1021,7 +1066,7 @@ class RoomDetailFragment @Inject constructor( .setPositiveButton(R.string.ok, null) .show() } - is EventSharedAction.ViewDecryptedSource -> { + is EventSharedAction.ViewDecryptedSource -> { val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) view.findViewById(R.id.event_content_text_view)?.let { it.text = action.content @@ -1032,31 +1077,31 @@ class RoomDetailFragment @Inject constructor( .setPositiveButton(R.string.ok, null) .show() } - is EventSharedAction.QuickReact -> { + is EventSharedAction.QuickReact -> { // eventId,ClickedOn,Add roomDetailViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) } - is EventSharedAction.Edit -> { + is EventSharedAction.Edit -> { roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, composerLayout.text.toString())) } - is EventSharedAction.Quote -> { + is EventSharedAction.Quote -> { roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, composerLayout.text.toString())) } - is EventSharedAction.Reply -> { + is EventSharedAction.Reply -> { roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString())) } - is EventSharedAction.CopyPermalink -> { + is EventSharedAction.CopyPermalink -> { val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) copyToClipboard(requireContext(), permalink, false) showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } - is EventSharedAction.Resend -> { + is EventSharedAction.Resend -> { roomDetailViewModel.handle(RoomDetailAction.ResendMessage(action.eventId)) } - is EventSharedAction.Remove -> { + is EventSharedAction.Remove -> { roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId)) } - is EventSharedAction.ReportContentSpam -> { + is EventSharedAction.ReportContentSpam -> { roomDetailViewModel.handle(RoomDetailAction.ReportContent( action.eventId, action.senderId, "This message is spam", spam = true)) } @@ -1064,19 +1109,19 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.ReportContent( action.eventId, action.senderId, "This message is inappropriate", inappropriate = true)) } - is EventSharedAction.ReportContentCustom -> { + is EventSharedAction.ReportContentCustom -> { promptReasonToReportContent(action) } - is EventSharedAction.IgnoreUser -> { + is EventSharedAction.IgnoreUser -> { roomDetailViewModel.handle(RoomDetailAction.IgnoreUser(action.senderId)) } - is EventSharedAction.OnUrlClicked -> { + is EventSharedAction.OnUrlClicked -> { onUrlClicked(action.url) } - is EventSharedAction.OnUrlLongClicked -> { + is EventSharedAction.OnUrlLongClicked -> { onUrlLongClicked(action.url) } - else -> { + else -> { Toast.makeText(context, "Action $action is not implemented yet", Toast.LENGTH_LONG).show() } } @@ -1092,7 +1137,7 @@ class RoomDetailFragment @Inject constructor( val startToCompose = composerLayout.composerEditText.text.isNullOrBlank() if (startToCompose - && userId == session.myUserId) { + && userId == session.myUserId) { // Empty composer, current user: start an emote composerLayout.composerEditText.setText(Command.EMOTE.command + " ") composerLayout.composerEditText.setSelection(Command.EMOTE.length) @@ -1183,10 +1228,10 @@ class RoomDetailFragment @Inject constructor( private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { when (type) { - AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera() - AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile() + AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera() + AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile() AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery() - AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio() + AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio() AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact() AttachmentTypeSelectorView.Type.STICKER -> vectorBaseActivity.notImplemented("Adding stickers") } diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt index 6b5f28860d..107603e9d4 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt @@ -35,17 +35,17 @@ class PermalinkHandler @Inject constructor(private val session: Session, fun launch( context: Context, deepLink: String?, - navigateToRoomInterceptor: NavigateToRoomInterceptor? = null, + navigationInterceptor: NavigationInterceptor? = null, buildTask: Boolean = false ): Single { val uri = deepLink?.let { Uri.parse(it) } - return launch(context, uri, navigateToRoomInterceptor, buildTask) + return launch(context, uri, navigationInterceptor, buildTask) } fun launch( context: Context, deepLink: Uri?, - navigateToRoomInterceptor: NavigateToRoomInterceptor? = null, + navigationInterceptor: NavigationInterceptor? = null, buildTask: Boolean = false ): Single { if (deepLink == null) { @@ -57,7 +57,7 @@ class PermalinkHandler @Inject constructor(private val session: Session, .observeOn(AndroidSchedulers.mainThread()) .map { val roomId = it.getOrNull() - if (navigateToRoomInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) { + if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) { openRoom(context, roomId, permalinkData.eventId, buildTask) } true @@ -68,7 +68,9 @@ class PermalinkHandler @Inject constructor(private val session: Session, Single.just(true) } is PermalinkData.UserLink -> { - navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask) + if (navigationInterceptor?.navToMemberProfile(permalinkData.userId) != true) { + navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask) + } Single.just(true) } is PermalinkData.FallbackLink -> { @@ -98,10 +100,21 @@ class PermalinkHandler @Inject constructor(private val session: Session, } } -interface NavigateToRoomInterceptor { +interface NavigationInterceptor { /** * Return true if the navigation has been intercepted */ - fun navToRoom(roomId: String?, eventId: String? = null): Boolean + fun navToRoom(roomId: String?, eventId: String? = null): Boolean { + return false + } + + /** + * Return true if the navigation has been intercepted + */ + fun navToMemberProfile(userId: String): Boolean { + return false + } + + } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index 58d2746dcb..ee3a7ccebd 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -40,9 +40,11 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider if (data == null) { return } - if (data.roomId == null) { + val roomMemberSummary = data.roomMemberSummary() + val profileInfo = data.profileInfo() + if (roomMemberSummary == null && profileInfo != null) { buildUserActions() - } else { + } else if (roomMemberSummary != null) { buildRoomMemberActions(data) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index eecfe65d88..d27e35bb1f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -81,13 +81,15 @@ class RoomMemberProfileFragment @Inject constructor( override fun invalidate() = withState(viewModel) { state -> - val memberMatrixItem = state.memberAsMatrixItem() ?: return@withState - memberProfileIdView.text = memberMatrixItem.id - val bestName = memberMatrixItem.getBestName() - memberProfileNameView.text = bestName - matrixProfileToolbarTitleView.text = bestName - avatarRenderer.render(memberMatrixItem, memberProfileAvatarView) - avatarRenderer.render(memberMatrixItem, matrixProfileToolbarAvatarImageView) + val memberMatrixItem = state.memberAsMatrixItem() + if (memberMatrixItem != null) { + memberProfileIdView.text = memberMatrixItem.id + val bestName = memberMatrixItem.getBestName() + memberProfileNameView.text = bestName + matrixProfileToolbarTitleView.text = bestName + avatarRenderer.render(memberMatrixItem, memberProfileAvatarView) + avatarRenderer.render(memberMatrixItem, matrixProfileToolbarAvatarImageView) + } val roomSummary = state.roomSummary() val powerLevelsContent = state.powerLevelsContent() diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 74c946b5eb..e23db1a177 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -64,17 +64,17 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v observeRoomSummary() observeRoomMemberSummary() observePowerLevel() - observeUserIfRequired() + fetchProfileInfoIfRequired() } - private fun observeUserIfRequired() { - if (initialState.roomId != null) { + private fun fetchProfileInfoIfRequired() { + val roomMember = room?.getRoomMember(initialState.userId) + if (roomMember != null) { return } - session.rx().liveUser(initialState.userId) - .unwrap() + session.rx().getProfileInfo(initialState.userId) .execute { - copy(user = it) + copy(profileInfo = it) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index a1f099c5d3..f22751a27b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -20,32 +20,33 @@ package im.vector.riotx.features.roommemberprofile import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary -import im.vector.matrix.android.api.session.user.model.User +import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem +typealias ProfileInfo = JsonDict + data class RoomMemberProfileViewState( val userId: String, val roomId: String?, val isMine: Boolean = false, - val roomSummary: Async = Uninitialized, + val roomSummary: Async = Uninitialized, val roomMemberSummary: Async = Uninitialized, - val user: Async = Uninitialized, + val profileInfo: Async = Uninitialized, val powerLevelsContent: Async = Uninitialized ) : MvRxState { constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId, userId = args.userId) - fun memberAsMatrixItem(): MatrixItem? { - return if (roomId == null) { - user.invoke()?.toMatrixItem() - } else { - roomMemberSummary.invoke()?.toMatrixItem() + return roomMemberSummary()?.toMatrixItem() ?: profileInfo()?.let { + MatrixItem.UserItem(userId, it[ProfileService.DISPLAY_NAME_KEY] as? String, it[ProfileService.AVATAR_URL_KEY] as? String) } } } + diff --git a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml index fc4836b80e..af4eb42790 100644 --- a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml +++ b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml @@ -9,6 +9,7 @@ android:padding="16dp" android:orientation="vertical"> + Date: Tue, 14 Jan 2020 17:08:21 +0100 Subject: [PATCH 13/19] Profile: handle ignore/unignore action + adjust UI --- .../MatrixItemAppBarStateChangeListener.kt | 12 +- .../vector/riotx/core/platform/StateView.kt | 2 +- .../riotx/core/platform/VectorBaseFragment.kt | 19 +++ .../im/vector/riotx/core/utils/DataSource.kt | 5 +- .../RoomMemberProfileAction.kt | 8 +- .../RoomMemberProfileController.kt | 48 +++--- .../RoomMemberProfileFragment.kt | 89 ++++++----- .../RoomMemberProfileViewEvents.kt | 27 ++++ .../RoomMemberProfileViewModel.kt | 150 +++++++++++++----- .../RoomMemberProfileViewState.kt | 20 +-- .../roomprofile/RoomProfileActivity.kt | 26 +-- .../roomprofile/RoomProfileFragment.kt | 32 ++-- .../roomprofile/RoomProfileViewEvents.kt | 2 +- .../roomprofile/RoomProfileViewModel.kt | 5 +- .../members/RoomMemberListFragment.kt | 6 +- .../res/layout/fragment_matrix_profile.xml | 9 +- .../main/res/layout/item_profile_action.xml | 8 +- .../res/layout/item_profile_matrix_item.xml | 8 +- .../view_stub_room_member_profile_header.xml | 121 +++++++------- vector/src/main/res/values/strings_riotX.xml | 2 + 20 files changed, 352 insertions(+), 247 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt diff --git a/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt b/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt index 6e34983018..427f37ba49 100644 --- a/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt +++ b/vector/src/main/java/im/vector/riotx/core/animations/MatrixItemAppBarStateChangeListener.kt @@ -20,16 +20,18 @@ package im.vector.riotx.core.animations import android.view.View import com.google.android.material.appbar.AppBarLayout -class MatrixItemAppBarStateChangeListener(private val animationDuration: Long, private val views: List) : AppBarStateChangeListener() { +class MatrixItemAppBarStateChangeListener(private val headerView: View, private val toolbarViews: List) : AppBarStateChangeListener() { override fun onStateChanged(appBarLayout: AppBarLayout, state: State) { if (state == State.COLLAPSED) { - views.forEach { - it.animate().alpha(1f).duration = animationDuration + 100 + headerView.visibility = View.INVISIBLE + toolbarViews.forEach { + it.animate().alpha(1f).duration = 150 } } else { - views.forEach { - it.animate().alpha(0f).duration = animationDuration - 100 + headerView.visibility = View.VISIBLE + toolbarViews.forEach { + it.animate().alpha(0f).duration = 150 } } } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt b/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt index f674478724..4c5a987b4b 100755 --- a/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt @@ -34,7 +34,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? data class Error(val message: CharSequence? = null) : State() } - private var eventCallback: EventCallback? = null + var eventCallback: EventCallback? = null var contentView: View? = null diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index 91ecf188dd..a77361126a 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -14,8 +14,11 @@ * limitations under the License. */ +@file:Suppress("DEPRECATION") + package im.vector.riotx.core.platform +import android.app.ProgressDialog import android.content.Context import android.os.Bundle import android.os.Parcelable @@ -59,6 +62,9 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { protected lateinit var navigator: Navigator protected lateinit var errorFormatter: ErrorFormatter + private var progress: ProgressDialog? = null + + /* ========================================================================================== * View model * ========================================================================================== */ @@ -177,6 +183,19 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { } } + protected fun showLoadingDialog(message: CharSequence, cancelable: Boolean = false) { + progress = ProgressDialog(requireContext()).apply { + setCancelable(cancelable) + setMessage(message) + setProgressStyle(ProgressDialog.STYLE_SPINNER) + show() + } + } + + protected fun dismissLoadingDialog(){ + progress?.dismiss() + } + /* ========================================================================================== * Toolbar * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt index 726d2ea697..232a164b57 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt @@ -19,6 +19,7 @@ package im.vector.riotx.core.utils import com.jakewharton.rxrelay2.BehaviorRelay import com.jakewharton.rxrelay2.PublishRelay import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers interface DataSource { @@ -37,7 +38,7 @@ open class BehaviorDataSource(private val defaultValue: T? = null) : MutableD private val behaviorRelay = createRelay() override fun observe(): Observable { - return behaviorRelay.hide().observeOn(Schedulers.computation()) + return behaviorRelay.hide().observeOn(AndroidSchedulers.mainThread()) } override fun post(value: T) { @@ -61,7 +62,7 @@ open class PublishDataSource : MutableDataSource { private val publishRelay = PublishRelay.create() override fun observe(): Observable { - return publishRelay.hide() + return publishRelay.hide().observeOn(AndroidSchedulers.mainThread()) } override fun post(value: T) { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index f4e27b5bc0..a38d4967bc 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -21,11 +21,7 @@ import im.vector.riotx.core.platform.VectorViewModelAction sealed class RoomMemberProfileAction : VectorViewModelAction { - sealed class Displayable : RoomMemberProfileAction() { - object JumpToReadReceipt : Displayable() - object Ignore : Displayable() - object Mention : Displayable() - } - + object RetryFetchingInfo: RoomMemberProfileAction() + object IgnoreUser: RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index ee3a7ccebd..98e422cb0f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -37,36 +37,33 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider } override fun buildModels(data: RoomMemberProfileViewState?) { - if (data == null) { + if (data?.userMatrixItem?.invoke() == null) { return } - val roomMemberSummary = data.roomMemberSummary() - val profileInfo = data.profileInfo() - if (roomMemberSummary == null && profileInfo != null) { - buildUserActions() - } else if (roomMemberSummary != null) { + if (data.showAsMember) { buildRoomMemberActions(data) + } else { + buildUserActions(data) } } - private fun buildUserActions() { + private fun buildUserActions(state: RoomMemberProfileViewState) { + val ignoreActionTitle = state.buildIgnoreActionTitle() ?: return // More buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) buildProfileAction( id = "ignore", - title = stringProvider.getString(R.string.ignore), + title = ignoreActionTitle, destructive = true, editable = false, action = { callback?.onIgnoreClicked() } ) } - private fun buildRoomMemberActions(data: RoomMemberProfileViewState) { - val roomSummaryEntity = data.roomSummary() ?: return - + private fun buildRoomMemberActions(state: RoomMemberProfileViewState) { // Security buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) - val learnMoreSubtitle = if (roomSummaryEntity.isEncrypted) { + val learnMoreSubtitle = if (state.isRoomEncrypted) { R.string.room_profile_encrypted_subtitle } else { R.string.room_profile_not_encrypted_subtitle @@ -80,7 +77,7 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider ) // More - if (!data.isMine) { + if (!state.isMine) { buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) buildProfileAction( id = "read_receipt", @@ -94,15 +91,26 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider editable = false, action = { callback?.onMentionClicked() } ) - buildProfileAction( - id = "ignore", - title = stringProvider.getString(R.string.ignore), - destructive = true, - editable = false, - action = { callback?.onIgnoreClicked() } - ) + val ignoreActionTitle = state.buildIgnoreActionTitle() + if (ignoreActionTitle != null) { + buildProfileAction( + id = "ignore", + title = ignoreActionTitle, + destructive = true, + editable = false, + action = { callback?.onIgnoreClicked() } + ) + } } + } + private fun RoomMemberProfileViewState.buildIgnoreActionTitle(): String? { + val isIgnored = isIgnored() ?: return null + return if (isIgnored) { + stringProvider.getString(R.string.unignore) + } else { + stringProvider.getString(R.string.ignore) + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index d27e35bb1f..423cc86a62 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -15,28 +15,26 @@ * */ +@file:Suppress("DEPRECATION") + package im.vector.riotx.features.roommemberprofile import android.os.Bundle import android.os.Parcelable import android.view.View -import com.airbnb.mvrx.args -import com.airbnb.mvrx.fragmentViewModel -import com.airbnb.mvrx.withState -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper -import im.vector.matrix.android.api.util.toMatrixItem +import com.airbnb.mvrx.* +import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R import im.vector.riotx.core.animations.AppBarStateChangeListener import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.AvatarRenderer import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_matrix_profile.* -import kotlinx.android.synthetic.main.fragment_matrix_profile.matrixProfileHeaderView import kotlinx.android.synthetic.main.view_stub_room_member_profile_header.* import javax.inject.Inject @@ -62,14 +60,32 @@ class RoomMemberProfileFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar(matrixProfileToolbar) - matrixProfileHeaderView.apply { - layoutResource = R.layout.view_stub_room_member_profile_header - inflate() + val headerView = matrixProfileHeaderView.let { + it.layoutResource = R.layout.view_stub_room_member_profile_header + it.inflate() } + memberProfileStateView.eventCallback = object : StateView.EventCallback { + override fun onRetryClicked() { + viewModel.handle(RoomMemberProfileAction.RetryFetchingInfo) + } + } + memberProfileStateView.contentView = memberProfileInfoContainer matrixProfileRecyclerView.configureWith(roomMemberProfileController, hasFixedSize = true) roomMemberProfileController.callback = this - appBarStateChangeListener = MatrixItemAppBarStateChangeListener(matrixProfileCollapsingToolbarLayout.scrimAnimationDuration, listOf(matrixProfileToolbarAvatarImageView, matrixProfileToolbarTitleView)) + appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView, + matrixProfileToolbarTitleView)) matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) + viewModel.viewEvents + .observe() + .subscribe { + dismissLoadingDialog() + when (it) { + is RoomMemberProfileViewEvents.Loading -> showLoadingDialog(it.message) + is RoomMemberProfileViewEvents.Failure -> showErrorInSnackbar(it.throwable) + } + } + .disposeOnDestroyView() + } override fun onDestroyView() { @@ -81,42 +97,37 @@ class RoomMemberProfileFragment @Inject constructor( override fun invalidate() = withState(viewModel) { state -> - val memberMatrixItem = state.memberAsMatrixItem() - if (memberMatrixItem != null) { - memberProfileIdView.text = memberMatrixItem.id - val bestName = memberMatrixItem.getBestName() - memberProfileNameView.text = bestName - matrixProfileToolbarTitleView.text = bestName - avatarRenderer.render(memberMatrixItem, memberProfileAvatarView) - avatarRenderer.render(memberMatrixItem, matrixProfileToolbarAvatarImageView) - } - - val roomSummary = state.roomSummary() - val powerLevelsContent = state.powerLevelsContent() - if (powerLevelsContent == null || roomSummary == null) { - memberProfilePowerLevelView.visibility = View.GONE - } else { - val roomName = roomSummary.toMatrixItem().getBestName() - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - val userPowerLevel = powerLevelsHelper.getUserPowerLevel(state.userId) - val powerLevelText = if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL) { - getString(R.string.room_member_power_level_admin_in, roomName) - } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL) { - getString(R.string.room_member_power_level_moderator_in, roomName) - } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL) { - null - } else { - getString(R.string.room_member_power_level_custom_in, userPowerLevel, roomName) + when (val asyncUserMatrixItem = state.userMatrixItem) { + is Incomplete -> { + matrixProfileToolbarTitleView.text = state.userId + avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), matrixProfileToolbarAvatarImageView) + memberProfileStateView.state = StateView.State.Loading + } + is Fail -> { + avatarRenderer.render(MatrixItem.UserItem(state.userId, null, null), matrixProfileToolbarAvatarImageView) + matrixProfileToolbarTitleView.text = state.userId + val failureMessage = errorFormatter.toHumanReadable(asyncUserMatrixItem.error) + memberProfileStateView.state = StateView.State.Error(failureMessage) + } + is Success -> { + val userMatrixItem = asyncUserMatrixItem() + memberProfileStateView.state = StateView.State.Content + memberProfileIdView.text = userMatrixItem.id + val bestName = userMatrixItem.getBestName() + memberProfileNameView.text = bestName + matrixProfileToolbarTitleView.text = bestName + avatarRenderer.render(userMatrixItem, memberProfileAvatarView) + avatarRenderer.render(userMatrixItem, matrixProfileToolbarAvatarImageView) } - memberProfilePowerLevelView.setTextOrHide(powerLevelText) } + memberProfilePowerLevelView.setTextOrHide(state.userPowerLevelString()) roomMemberProfileController.setData(state) } // RoomMemberProfileController.Callback override fun onIgnoreClicked() { - vectorBaseActivity.notImplemented("Ignore") + viewModel.handle(RoomMemberProfileAction.IgnoreUser) } override fun onLearnMoreClicked() { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt new file mode 100644 index 0000000000..b4f526370b --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roommemberprofile + +/** + * Transient events for RoomProfile + */ +sealed class RoomMemberProfileViewEvents { + data class Loading(val message: CharSequence) : RoomMemberProfileViewEvents() + object OnIgnoreActionSuccess : RoomMemberProfileViewEvents() + data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() + +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index e23db1a177..7cb33a2d5a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -17,25 +17,43 @@ package im.vector.riotx.features.roommemberprofile +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.profile.ProfileService +import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.android.api.util.MatrixItem +import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap +import im.vector.riotx.R import im.vector.riotx.core.platform.VectorViewModel -import timber.log.Timber +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.core.utils.DataSource +import im.vector.riotx.core.utils.PublishDataSource +import io.reactivex.Observable +import io.reactivex.functions.BiFunction +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, + private val stringProvider: StringProvider, private val session: Session) : VectorViewModel(initialState) { @@ -53,6 +71,9 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } + private val _viewEvents = PublishDataSource() + val viewEvents: DataSource = _viewEvents + private val room = if (initialState.roomId != null) { session.getRoom(initialState.roomId) } else { @@ -61,65 +82,118 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v init { setState { copy(isMine = session.myUserId == this.userId) } - observeRoomSummary() - observeRoomMemberSummary() - observePowerLevel() - fetchProfileInfoIfRequired() + observeIgnoredState() + viewModelScope.launch(Dispatchers.Main) { + // Do we have a room member for this id. + val roomMember = withContext(Dispatchers.Default) { + room?.getRoomMember(initialState.userId) + } + // If not, we look for profile info on the server + if (room == null || roomMember == null) { + fetchProfileInfo() + } else { + // otherwise we just start listening to db + setState { copy(showAsMember = true) } + observeRoomMemberSummary(room) + observeRoomSummaryAndPowerLevels(room) + } + } } - private fun fetchProfileInfoIfRequired() { - val roomMember = room?.getRoomMember(initialState.userId) - if (roomMember != null) { - return - } - session.rx().getProfileInfo(initialState.userId) + private fun observeIgnoredState() { + session.rx().liveIgnoredUsers() + .map { ignored -> + ignored.find { + it.userId == initialState.userId + } != null + } .execute { - copy(profileInfo = it) + copy(isIgnored = it) } } override fun handle(action: RoomMemberProfileAction) { - Timber.v("Handle $action") + when (action) { + RoomMemberProfileAction.RetryFetchingInfo -> fetchProfileInfo() + is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() + } } - private fun observeRoomSummary() { - if (room == null) { - return - } - room.rx().liveRoomSummary() - .unwrap() - .execute { - copy(roomSummary = it) - } - } - - private fun observeRoomMemberSummary() { - if (room == null) { - return - } + private fun observeRoomMemberSummary(room: Room) { val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) } room.rx().liveRoomMembers(queryParams) - .map { it.firstOrNull().toOptional() } + .map { it.firstOrNull()?.toMatrixItem().toOptional() } .unwrap() .execute { - copy(roomMemberSummary = it) + copy(userMatrixItem = it) } } - private fun observePowerLevel() { - if (room == null) { - return - } - room.rx() - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + private fun fetchProfileInfo() { + session.rx().getProfileInfo(initialState.userId) + .map { + MatrixItem.UserItem( + id = initialState.userId, + displayName = it[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = it[ProfileService.AVATAR_URL_KEY] as? String + ) + } + .execute { + copy(userMatrixItem = it) + } + } + + private fun observeRoomSummaryAndPowerLevels(room: Room) { + val roomSummaryLive = room.rx().liveRoomSummary().unwrap() + val powerLevelsContentLive = room.rx().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS) .mapOptional { it.content.toModel() } .unwrap() - .execute { - copy(powerLevelsContent = it) + + roomSummaryLive.execute { + copy(isRoomEncrypted = it.invoke()?.isEncrypted == true) + } + Observable + .combineLatest( + roomSummaryLive, + powerLevelsContentLive, + BiFunction { roomSummary, powerLevelsContent -> + val roomName = roomSummary.toMatrixItem().getBestName() + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + val userPowerLevel = powerLevelsHelper.getUserPowerLevel(initialState.userId) + if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL) { + stringProvider.getString(R.string.room_member_power_level_admin_in, roomName) + } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL) { + stringProvider.getString(R.string.room_member_power_level_moderator_in, roomName) + } else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL) { + "" + } else { + stringProvider.getString(R.string.room_member_power_level_custom_in, userPowerLevel, roomName) + } + } + ).execute { + copy(userPowerLevelString = it) } } + private fun handleIgnoreAction() = withState { state -> + val isIgnored = state.isIgnored() ?: return@withState + _viewEvents.post(RoomMemberProfileViewEvents.Loading(stringProvider.getString(R.string.please_wait))) + val ignoreActionCallback = object : MatrixCallback { + override fun onSuccess(data: Unit) { + _viewEvents.post(RoomMemberProfileViewEvents.OnIgnoreActionSuccess) + } + + override fun onFailure(failure: Throwable) { + _viewEvents.post(RoomMemberProfileViewEvents.Failure(failure)) + } + } + if (isIgnored) { + session.unIgnoreUserIds(listOf(state.userId), ignoreActionCallback) + } else { + session.ignoreUserIds(listOf(initialState.userId), ignoreActionCallback) + } + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index f22751a27b..aa95f58fc1 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -20,33 +20,23 @@ package im.vector.riotx.features.roommemberprofile import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.session.profile.ProfileService import im.vector.matrix.android.api.session.room.model.PowerLevelsContent -import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.MatrixItem -import im.vector.matrix.android.api.util.toMatrixItem - -typealias ProfileInfo = JsonDict data class RoomMemberProfileViewState( val userId: String, val roomId: String?, + val showAsMember: Boolean = false, val isMine: Boolean = false, - val roomSummary: Async = Uninitialized, - val roomMemberSummary: Async = Uninitialized, - val profileInfo: Async = Uninitialized, - val powerLevelsContent: Async = Uninitialized + val isIgnored: Async = Uninitialized, + val isRoomEncrypted: Boolean = false, + val userPowerLevelString: Async = Uninitialized, + val userMatrixItem: Async = Uninitialized ) : MvRxState { constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId, userId = args.userId) - fun memberAsMatrixItem(): MatrixItem? { - return roomMemberSummary()?.toMatrixItem() ?: profileInfo()?.let { - MatrixItem.UserItem(userId, it[ProfileService.DISPLAY_NAME_KEY] as? String, it[ProfileService.AVATAR_URL_KEY] as? String) - } - } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index bc8bc2a959..14c3421e7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -21,8 +21,8 @@ import android.content.Context import android.content.Intent import androidx.appcompat.widget.Toolbar import im.vector.riotx.R -import im.vector.riotx.core.extensions.commitTransaction -import im.vector.riotx.core.extensions.commitTransactionNow +import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.extensions.addFragmentToBackstack import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment @@ -32,9 +32,6 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { private const val EXTRA_ROOM_PROFILE_ARGS = "EXTRA_ROOM_PROFILE_ARGS" - private const val TAG_ROOM_PROFILE_FRAGMENT = "TAG_ROOM_PROFILE_FRAGMENT" - private const val TAG_ROOM_MEMBER_LIST_FRAGMENT = "TAG_ROOM_MEMBER_LIST_FRAGMENT" - fun newIntent(context: Context, roomId: String): Intent { val roomProfileArgs = RoomProfileArgs(roomId) @@ -53,14 +50,7 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) roomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) ?: return if (isFirstCreation()) { - val argsBundle = roomProfileArgs.toMvRxBundle() - val roomProfileFragment = createFragment(RoomProfileFragment::class.java, argsBundle) - val roomMemberListFragment = createFragment(RoomMemberListFragment::class.java, argsBundle) - supportFragmentManager.commitTransactionNow { - add(R.id.simpleFragmentContainer, roomProfileFragment, TAG_ROOM_PROFILE_FRAGMENT) - add(R.id.simpleFragmentContainer, roomMemberListFragment, TAG_ROOM_MEMBER_LIST_FRAGMENT) - detach(roomMemberListFragment) - } + addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) } sharedActionViewModel .observe() @@ -83,15 +73,7 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { } private fun openRoomMembers() { - val roomProfileFragment = supportFragmentManager.findFragmentByTag(TAG_ROOM_PROFILE_FRAGMENT) - ?: throw IllegalStateException("You should have a RoomProfileFragment") - val roomMemberListFragment = supportFragmentManager.findFragmentByTag(TAG_ROOM_MEMBER_LIST_FRAGMENT) - ?: throw IllegalStateException("You should have a RoomMemberListFragment") - supportFragmentManager.commitTransaction { - hide(roomProfileFragment) - attach(roomMemberListFragment) - addToBackStack(null) - } + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) } override fun configure(toolbar: Toolbar) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index b596c9725d..852b0f1f37 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -19,7 +19,6 @@ package im.vector.riotx.features.roomprofile -import android.app.ProgressDialog import android.os.Bundle import android.os.Parcelable import android.view.View @@ -58,7 +57,6 @@ class RoomProfileFragment @Inject constructor( val roomProfileViewModelFactory: RoomProfileViewModel.Factory ) : VectorBaseFragment(), RoomProfileController.Callback { - private var progress: ProgressDialog? = null private val roomProfileArgs: RoomProfileArgs by args() private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel @@ -73,36 +71,32 @@ class RoomProfileFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) - matrixProfileHeaderView.apply { - layoutResource = R.layout.view_stub_room_profile_header - inflate() + val headerView = matrixProfileHeaderView.let { + it.layoutResource = R.layout.view_stub_room_profile_header + it.inflate() } + setupToolbar(matrixProfileToolbar) setupRecyclerView() - appBarStateChangeListener = MatrixItemAppBarStateChangeListener(matrixProfileCollapsingToolbarLayout.scrimAnimationDuration, listOf(matrixProfileToolbarAvatarImageView, matrixProfileToolbarTitleView)) + appBarStateChangeListener = MatrixItemAppBarStateChangeListener(headerView, listOf(matrixProfileToolbarAvatarImageView, + matrixProfileToolbarTitleView)) matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) roomProfileViewModel.viewEvents .observe() .subscribe { - progress?.dismiss() + dismissLoadingDialog() when (it) { - RoomProfileViewEvents.Loading -> showLoading() + is RoomProfileViewEvents.Loading -> showLoadingDialog(it.message) RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom() is RoomProfileViewEvents.Failure -> showError(it.throwable) } } .disposeOnDestroyView() - roomListQuickActionsSharedActionViewModel .observe() .subscribe { handleQuickActions(it) } .disposeOnDestroyView() } - override fun onResume() { - super.onResume() - setupToolbar(matrixProfileToolbar) - } - private fun handleQuickActions(action: RoomListQuickActionsSharedAction) = when (action) { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.ALL_MESSAGES_NOISY)) @@ -124,15 +118,7 @@ class RoomProfileFragment @Inject constructor( } private fun showError(throwable: Throwable) { - vectorBaseActivity.showSnackbar(errorFormatter.toHumanReadable(throwable)) - } - - private fun showLoading() { - progress = ProgressDialog(requireContext()).apply { - setMessage(getString(R.string.room_profile_leaving_room)) - setProgressStyle(ProgressDialog.STYLE_SPINNER) - show() - } + showErrorInSnackbar(throwable) } private fun setupRecyclerView() { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt index 4b483d51a4..e100f77b17 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt @@ -19,7 +19,7 @@ package im.vector.riotx.features.roomprofile * Transient events for RoomProfile */ sealed class RoomProfileViewEvents { - object Loading: RoomProfileViewEvents() + data class Loading(val message: CharSequence): RoomProfileViewEvents() object OnLeaveRoomSuccess: RoomProfileViewEvents() data class Failure(val throwable: Throwable) : RoomProfileViewEvents() diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index 43ed7a5576..6d8db33ea0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -26,13 +26,16 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap +import im.vector.riotx.R import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.DataSource import im.vector.riotx.core.utils.PublishDataSource import im.vector.riotx.features.home.room.list.RoomListAction import im.vector.riotx.features.home.room.list.RoomListViewEvents class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, + private val stringProvider: StringProvider, private val session: Session) : VectorViewModel(initialState) { @@ -84,7 +87,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } private fun handleLeaveRoom() { - _viewEvents.post(RoomProfileViewEvents.Loading) + _viewEvents.post(RoomProfileViewEvents.Loading(stringProvider.getString(R.string.room_profile_leaving_room))) room.leave(null, object : MatrixCallback { override fun onSuccess(data: Unit) { _viewEvents.post(RoomProfileViewEvents.OnLeaveRoomSuccess) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index b2318259f9..0c18509af0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -48,12 +48,8 @@ class RoomMemberListFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomMemberListController.callback = this - recyclerView.configureWith(roomMemberListController, hasFixedSize = true) - } - - override fun onResume() { - super.onResume() setupToolbar(roomMemberListToolbar) + recyclerView.configureWith(roomMemberListController, hasFixedSize = true) } override fun onDestroyView() { diff --git a/vector/src/main/res/layout/fragment_matrix_profile.xml b/vector/src/main/res/layout/fragment_matrix_profile.xml index 384c87cccc..7bd6d8bfb0 100644 --- a/vector/src/main/res/layout/fragment_matrix_profile.xml +++ b/vector/src/main/res/layout/fragment_matrix_profile.xml @@ -2,6 +2,7 @@ @@ -19,6 +20,7 @@ android:layout_height="match_parent" app:contentScrim="?riotx_background" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" + app:scrimVisibleHeightTrigger="80dp" app:scrimAnimationDuration="250" app:titleEnabled="false" app:toolbarId="@+id/matrixProfileToolbar"> @@ -40,6 +42,7 @@ app:layout_collapseMode="pin"> @@ -50,10 +53,10 @@ android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:alpha="0" - tools:alpha="1" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + tools:alpha="1" tools:src="@tools:sample/avatars" /> diff --git a/vector/src/main/res/layout/item_profile_action.xml b/vector/src/main/res/layout/item_profile_action.xml index 98a9d15ba3..d063025379 100644 --- a/vector/src/main/res/layout/item_profile_action.xml +++ b/vector/src/main/res/layout/item_profile_action.xml @@ -30,7 +30,7 @@ diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index aa5d9e44cf..cdca2341e8 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -27,7 +27,7 @@ diff --git a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml index af4eb42790..82bf0e7d7b 100644 --- a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml +++ b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml @@ -1,70 +1,75 @@ - + android:padding="16dp"> - - - - + android:orientation="vertical"> - + - + - + - + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 4fa6c5bbd4..e8cc1c7360 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -47,5 +47,7 @@ Jump to read receipt + Unignore + From 9671a77e5d8723a44aab6ac62cbc74b4db14ef41 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 14 Jan 2020 18:48:13 +0100 Subject: [PATCH 14/19] Clean code and update CHANGES --- CHANGES.md | 2 ++ .../src/main/java/im/vector/matrix/rx/RxSession.kt | 1 - .../matrix/android/api/session/profile/ProfileService.kt | 2 -- .../android/api/session/room/model/PowerLevelsContent.kt | 1 - .../api/session/room/powerlevers/PowerLevelsConstants.kt | 1 - .../api/session/room/powerlevers/PowerLevelsHelper.kt | 1 - .../internal/session/profile/DefaultProfileService.kt | 1 - .../internal/session/profile/GetProfileInfoTask.kt | 2 -- .../android/internal/session/profile/ProfileAPI.kt | 7 +++---- .../session/room/membership/DefaultMembershipService.kt | 1 - .../internal/session/room/state/DefaultStateService.kt | 2 +- .../riotx/core/animations/AppBarStateChangeListener.kt | 1 - .../core/animations/behavior/PercentViewBehavior.kt | 2 -- .../main/java/im/vector/riotx/core/di/FragmentModule.kt | 1 - .../riotx/core/epoxy/profiles/ProfileItemAction.kt | 4 ---- .../riotx/core/epoxy/profiles/ProfileItemExtensions.kt | 1 - .../riotx/core/epoxy/profiles/ProfileItemSection.kt | 3 --- .../im/vector/riotx/core/platform/VectorBaseFragment.kt | 3 +-- .../main/java/im/vector/riotx/core/utils/DataSource.kt | 1 - .../features/home/room/detail/RoomDetailFragment.kt | 1 - .../features/home/room/detail/RoomDetailViewState.kt | 1 - .../list/actions/RoomListQuickActionsEpoxyController.kt | 3 --- .../vector/riotx/features/navigation/DefaultNavigator.kt | 1 - .../im/vector/riotx/features/navigation/Navigator.kt | 1 - .../vector/riotx/features/permalink/PermalinkHandler.kt | 2 -- .../roommemberprofile/RoomMemberProfileAction.kt | 1 - .../roommemberprofile/RoomMemberProfileActivity.kt | 1 - .../roommemberprofile/RoomMemberProfileController.kt | 2 -- .../roommemberprofile/RoomMemberProfileFragment.kt | 4 ---- .../roommemberprofile/RoomMemberProfileViewEvents.kt | 1 - .../roommemberprofile/RoomMemberProfileViewModel.kt | 1 - .../roommemberprofile/RoomMemberProfileViewState.kt | 5 ----- .../riotx/features/roomprofile/RoomProfileAction.kt | 1 - .../riotx/features/roomprofile/RoomProfileActivity.kt | 1 - .../riotx/features/roomprofile/RoomProfileController.kt | 9 --------- .../riotx/features/roomprofile/RoomProfileFragment.kt | 1 - .../riotx/features/roomprofile/RoomProfileViewEvents.kt | 1 - .../riotx/features/roomprofile/RoomProfileViewModel.kt | 3 --- .../roomprofile/members/RoomMemberListController.kt | 2 -- .../roomprofile/members/RoomMemberListFragment.kt | 3 --- .../roomprofile/members/RoomMemberListViewModel.kt | 4 ---- .../roomprofile/members/RoomMemberListViewState.kt | 1 - 42 files changed, 7 insertions(+), 80 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 628d9759b1..7db26cd8aa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,8 @@ Changes in RiotX 0.13.0 (2020-XX-XX) Features ✨: - Send and render typing events (#564) + - Create Room Profile screen (#54) + - Create Room Member Profile screen (#59) Improvements 🙌: - Render events m.room.encryption and m.room.guest_access in the timeline diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index 65eae3924b..406e274258 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -98,7 +98,6 @@ class RxSession(private val session: Session) { fun getProfileInfo(userId: String): Single = singleBuilder { session.getProfile(userId, it) } - } fun Session.rx(): RxSession { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt index dc3fb941d1..2718ff742a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.Optional - interface ProfileService { companion object Constants { @@ -35,5 +34,4 @@ interface ProfileService { fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback>): Cancelable fun getProfile(userId: String, matrixCallback: MatrixCallback): Cancelable - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt index 3cbb435d76..9b4a1f23d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.api.session.room.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt index f1610ead88..a039626d6e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt @@ -22,5 +22,4 @@ object PowerLevelsConstants { const val DEFAULT_ROOM_ADMIN_LEVEL = 100 const val DEFAULT_ROOM_MODERATOR_LEVEL = 50 const val DEFAULT_ROOM_USER_LEVEL = 0 - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt index 60bc5b3fc5..ec285f0e1f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt @@ -57,7 +57,6 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { } else false } - /** * Get the notification level for a dedicated key. * diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt index c10a486538..e2c18e41d6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/DefaultProfileService.kt @@ -73,5 +73,4 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto } .executeBy(taskExecutor) } - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt index 2309628a8a..f51092434e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/GetProfileInfoTask.kt @@ -38,5 +38,3 @@ internal class DefaultGetProfileInfoTask @Inject constructor(private val profile } } } - - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt index 34d1eb600a..eb016280d8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt @@ -26,12 +26,11 @@ import retrofit2.http.Path interface ProfileAPI { /** - * Get the combined profile information for this user. This API may be used to fetch the user's own profile information or other users; either locally or on remote homeservers. This API may return keys which are not limited to displayname or avatar_url. - * + * Get the combined profile information for this user. + * This API may be used to fetch the user's own profile information or other users; either locally or on remote homeservers. + * This API may return keys which are not limited to displayname or avatar_url. * @param userId the user id to fetch profile info */ @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}") fun getProfile(@Path("userId") roomId: String): Call - - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index 0195497798..d17614ca4e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -95,7 +95,6 @@ internal class DefaultMembershipService @AssistedInject constructor( ) } - private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { return RoomMemberHelper(realm, roomId).queryRoomMembersEvent() .process(RoomMemberSummaryEntityFields.USER_ID, queryParams.userId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index 9c6f1f4ae1..eb7208ea0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -57,7 +57,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private override fun getStateEventLive(eventType: String): LiveData> { val liveData = monarchy.findAllMappedWithChanges( - { realm -> EventEntity.where(realm, roomId, eventType).descending()}, + { realm -> EventEntity.where(realm, roomId, eventType).descending() }, { it.asDomain() } ) return Transformations.map(liveData) { results -> diff --git a/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt b/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt index a16b3b52d9..b739f52bf3 100644 --- a/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt +++ b/vector/src/main/java/im/vector/riotx/core/animations/AppBarStateChangeListener.kt @@ -21,7 +21,6 @@ import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener import kotlin.math.abs - abstract class AppBarStateChangeListener : OnOffsetChangedListener { enum class State { diff --git a/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt b/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt index d3ec1c1ed5..967d7d638d 100644 --- a/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt +++ b/vector/src/main/java/im/vector/riotx/core/animations/behavior/PercentViewBehavior.kt @@ -218,6 +218,4 @@ class PercentViewBehavior(context: Context, attrs: AttributeSet) : Coo child.requestLayout() } - - } diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index d86271761a..b99268d6d4 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -291,5 +291,4 @@ interface FragmentModule { @IntoMap @FragmentKey(SoftLogoutFragment::class) fun bindSoftLogoutFragment(fragment: SoftLogoutFragment): Fragment - } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt index 5de1958783..6d80a095d4 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt @@ -16,13 +16,11 @@ package im.vector.riotx.core.epoxy.profiles -import android.content.res.ColorStateList import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.view.isVisible -import androidx.core.widget.ImageViewCompat import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R @@ -67,12 +65,10 @@ abstract class ProfileItemAction : VectorEpoxyModel() } } - class Holder : VectorEpoxyHolder() { val icon by bind(R.id.actionIcon) val title by bind(R.id.actionTitle) val subtitle by bind(R.id.actionSubtitle) val editable by bind(R.id.actionEditable) } - } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt index 7b9e6973c6..78a7bea014 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt @@ -38,7 +38,6 @@ fun EpoxyController.buildProfileAction( divider: Boolean = true, action: () -> Unit ) { - profileItemAction { iconRes(icon) id("action_$id") diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt index 1ad6c12606..f24fd96ebd 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt @@ -37,7 +37,4 @@ abstract class ProfileItemSection: VectorEpoxyModel() class Holder : VectorEpoxyHolder() { val sectionView by bind(R.id.itemProfileSectionView) } - - - } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index a77361126a..91c166a96c 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -64,7 +64,6 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { private var progress: ProgressDialog? = null - /* ========================================================================================== * View model * ========================================================================================== */ @@ -192,7 +191,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { } } - protected fun dismissLoadingDialog(){ + protected fun dismissLoadingDialog() { progress?.dismiss() } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt index 232a164b57..4c4a553e5c 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/DataSource.kt @@ -20,7 +20,6 @@ import com.jakewharton.rxrelay2.BehaviorRelay import com.jakewharton.rxrelay2.PublishRelay import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers interface DataSource { fun observe(): Observable diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 77a4abc1bd..0cae97b562 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -456,7 +456,6 @@ class RoomDetailFragment @Inject constructor( composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes)) composerLayout.sendButton.setContentDescription(getString(descriptionRes)) - avatarRenderer.render( MatrixItem.UserItem(event.root.senderId ?: "", event.getDisambiguatedDisplayName(), event.senderAvatar), diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt index 23b0dc16ca..43a454d32e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt @@ -20,7 +20,6 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.sync.SyncState diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 0cc409d53d..2b7d87267e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -19,9 +19,6 @@ import android.view.View import com.airbnb.epoxy.TypedEpoxyController import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem -import im.vector.riotx.core.epoxy.DividerItem_ -import im.vector.riotx.core.epoxy.bottomsheet.BottomSheetRoomPreviewItem -import im.vector.riotx.core.epoxy.bottomsheet.BottomSheetRoomPreviewItem_ import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetActionItem import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem import im.vector.riotx.core.epoxy.dividerItem diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 9268cc8390..4551b179a5 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -141,5 +141,4 @@ class DefaultNavigator @Inject constructor( override fun openRoomProfile(context: Context, roomId: String) { context.startActivity(RoomProfileActivity.newIntent(context, roomId)) } - } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt index 4049c2e2c5..e2bca2453f 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/Navigator.kt @@ -53,5 +53,4 @@ interface Navigator { fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean = false) fun openRoomProfile(context: Context, roomId: String) - } diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt index 107603e9d4..84a25060cc 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt @@ -115,6 +115,4 @@ interface NavigationInterceptor { fun navToMemberProfile(userId: String): Boolean { return false } - - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index a38d4967bc..8ff209b443 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -23,5 +23,4 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object RetryFetchingInfo: RoomMemberProfileAction() object IgnoreUser: RoomMemberProfileAction() - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt index a17a969b41..25efecf541 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -51,5 +51,4 @@ class RoomMemberProfileActivity : VectorBaseActivity(), ToolbarConfigurable { override fun configure(toolbar: Toolbar) { configureToolbar(toolbar) } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index 98e422cb0f..1de8c29f89 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -112,6 +112,4 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider stringProvider.getString(R.string.ignore) } } - - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 423cc86a62..b8a6f247b2 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -85,7 +85,6 @@ class RoomMemberProfileFragment @Inject constructor( } } .disposeOnDestroyView() - } override fun onDestroyView() { @@ -95,7 +94,6 @@ class RoomMemberProfileFragment @Inject constructor( super.onDestroyView() } - override fun invalidate() = withState(viewModel) { state -> when (val asyncUserMatrixItem = state.userMatrixItem) { is Incomplete -> { @@ -141,6 +139,4 @@ class RoomMemberProfileFragment @Inject constructor( override fun onMentionClicked() { vectorBaseActivity.notImplemented("Mention") } - - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt index b4f526370b..2a72051491 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -23,5 +23,4 @@ sealed class RoomMemberProfileViewEvents { data class Loading(val message: CharSequence) : RoomMemberProfileViewEvents() object OnIgnoreActionSuccess : RoomMemberProfileViewEvents() data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 7cb33a2d5a..757318b1d2 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -195,5 +195,4 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v session.ignoreUserIds(listOf(initialState.userId), ignoreActionCallback) } } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index aa95f58fc1..ff59eb0f03 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -20,9 +20,6 @@ package im.vector.riotx.features.roommemberprofile import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.session.room.model.PowerLevelsContent -import im.vector.matrix.android.api.session.room.model.RoomSummary -import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.MatrixItem data class RoomMemberProfileViewState( @@ -37,6 +34,4 @@ data class RoomMemberProfileViewState( ) : MvRxState { constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId, userId = args.userId) - } - diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt index 985eee72a4..d3852e34c8 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt @@ -19,7 +19,6 @@ package im.vector.riotx.features.roomprofile import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.core.platform.VectorViewModelAction -import im.vector.riotx.features.home.room.list.RoomListAction sealed class RoomProfileAction: VectorViewModelAction { object LeaveRoom: RoomProfileAction() diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index 14c3421e7f..160d2341ed 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -79,5 +79,4 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable { override fun configure(toolbar: Toolbar) { configureToolbar(toolbar) } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index 814cdc7723..dfd534badd 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -17,16 +17,10 @@ package im.vector.riotx.features.roomprofile -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController import im.vector.riotx.R -import im.vector.riotx.core.epoxy.DividerItem_ -import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.epoxy.profiles.buildProfileAction import im.vector.riotx.core.epoxy.profiles.buildProfileSection -import im.vector.riotx.core.epoxy.profiles.profileItemAction -import im.vector.riotx.core.epoxy.profiles.profileItemSection import im.vector.riotx.core.resources.StringProvider import javax.inject.Inject @@ -101,7 +95,4 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri action = { callback?.onLeaveRoomClicked() } ) } - - - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 852b0f1f37..f8994b895b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -64,7 +64,6 @@ class RoomProfileFragment @Inject constructor( private lateinit var appBarStateChangeListener: AppBarStateChangeListener - override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt index e100f77b17..3834247b52 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt @@ -22,5 +22,4 @@ sealed class RoomProfileViewEvents { data class Loading(val message: CharSequence): RoomProfileViewEvents() object OnLeaveRoomSuccess: RoomProfileViewEvents() data class Failure(val throwable: Throwable) : RoomProfileViewEvents() - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index 6d8db33ea0..d7e86130f9 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -31,8 +31,6 @@ import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.DataSource import im.vector.riotx.core.utils.PublishDataSource -import im.vector.riotx.features.home.room.list.RoomListAction -import im.vector.riotx.features.home.room.list.RoomListViewEvents class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, private val stringProvider: StringProvider, @@ -98,5 +96,4 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } }) } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt index baf7c4d2e4..6b7ef74806 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt @@ -64,6 +64,4 @@ class RoomMemberListController @Inject constructor(private val avatarRenderer: A } } } - - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index 0c18509af0..4919fa39e3 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -32,7 +32,6 @@ import im.vector.riotx.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_member_list.* import javax.inject.Inject - class RoomMemberListFragment @Inject constructor( val viewModelFactory: RoomMemberListViewModel.Factory, private val roomMemberListController: RoomMemberListController, @@ -42,7 +41,6 @@ class RoomMemberListFragment @Inject constructor( private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val roomProfileArgs: RoomProfileArgs by args() - override fun getLayoutResId() = R.layout.fragment_room_member_list override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -72,5 +70,4 @@ class RoomMemberListFragment @Inject constructor( avatarRenderer.render(it.toMatrixItem(), roomMemberListToolbarAvatarImageView) } } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 0056d332a0..3ba457bfb8 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -82,7 +82,6 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState .execute { async -> copy(roomMemberSummaries = async) } - } private fun observeRoomSummary() { @@ -93,7 +92,6 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } } - private fun buildRoomMemberSummaries(powerLevelsContent: PowerLevelsContent, roomMembers: List): RoomMemberSummaries { val admins = ArrayList() val moderators = ArrayList() @@ -123,7 +121,5 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } override fun handle(action: RoomMemberListAction) { - } - } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt index bc616994ba..767de9a854 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt @@ -32,7 +32,6 @@ data class RoomMemberListViewState( ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) - } typealias RoomMemberSummaries = Map> From d6e6092eeac4ca73373b39ccd5596f3ade9fd0e3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 14 Jan 2020 19:14:15 +0100 Subject: [PATCH 15/19] Profile: add PowerLevelContent for building future actions + fix divider --- .../roommemberprofile/RoomMemberProfileController.kt | 7 ++++++- .../roommemberprofile/RoomMemberProfileViewModel.kt | 4 ++++ .../roommemberprofile/RoomMemberProfileViewState.kt | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index 1de8c29f89..a77b34b7ab 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -56,6 +56,7 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider title = ignoreActionTitle, destructive = true, editable = false, + divider = false, action = { callback?.onIgnoreClicked() } ) } @@ -85,19 +86,23 @@ class RoomMemberProfileController @Inject constructor(private val stringProvider title = stringProvider.getString(R.string.room_member_jump_to_read_receipt), action = { callback?.onJumpToReadReceiptClicked() } ) + + val ignoreActionTitle = state.buildIgnoreActionTitle() + buildProfileAction( id = "mention", title = stringProvider.getString(R.string.room_participants_action_mention), editable = false, + divider = ignoreActionTitle != null, action = { callback?.onMentionClicked() } ) - val ignoreActionTitle = state.buildIgnoreActionTitle() if (ignoreActionTitle != null) { buildProfileAction( id = "ignore", title = ignoreActionTitle, destructive = true, editable = false, + divider = false, action = { callback?.onIgnoreClicked() } ) } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 757318b1d2..737539937b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -154,6 +154,10 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v roomSummaryLive.execute { copy(isRoomEncrypted = it.invoke()?.isEncrypted == true) } + powerLevelsContentLive.execute { + copy(powerLevelsContent = it) + } + Observable .combineLatest( roomSummaryLive, diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index ff59eb0f03..ba079e9996 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -20,6 +20,7 @@ package im.vector.riotx.features.roommemberprofile import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.util.MatrixItem data class RoomMemberProfileViewState( @@ -29,6 +30,7 @@ data class RoomMemberProfileViewState( val isMine: Boolean = false, val isIgnored: Async = Uninitialized, val isRoomEncrypted: Boolean = false, + val powerLevelsContent: Async = Uninitialized, val userPowerLevelString: Async = Uninitialized, val userMatrixItem: Async = Uninitialized ) : MvRxState { From 52de14b1b516cd54b47de10de994c9c5bd2a4adf Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 15 Jan 2020 11:46:33 +0100 Subject: [PATCH 16/19] Clean code after Benoit's review --- .../SenderNotificationPermissionCondition.kt | 2 +- .../api/session/profile/ProfileService.kt | 19 +++++++++++++++++++ .../session/room/model/PowerLevelsContent.kt | 2 +- .../PowerLevelsConstants.kt | 2 +- .../PowerLevelsHelper.kt | 6 ++---- .../internal/session/profile/ProfileAPI.kt | 2 +- .../RoomMemberProfileViewEvents.kt | 2 +- .../RoomMemberProfileViewModel.kt | 6 +++--- .../roomprofile/RoomProfileController.kt | 10 ++++------ .../roomprofile/RoomProfileFragment.kt | 2 -- .../roomprofile/RoomProfileViewModel.kt | 5 +---- .../roomprofile/RoomProfileViewState.kt | 3 +-- .../members/RoomMemberListViewModel.kt | 4 ++-- vector/src/main/res/values/strings_riotX.xml | 5 ++++- 14 files changed, 41 insertions(+), 29 deletions(-) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/{powerlevers => powerlevels}/PowerLevelsConstants.kt (92%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/{powerlevers => powerlevels}/PowerLevelsHelper.kt (91%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt index 657ad29c87..e0d4034082 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt @@ -17,7 +17,7 @@ package im.vector.matrix.android.api.pushrules import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.PowerLevelsContent -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt index 2718ff742a..c1dc9a8afa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/profile/ProfileService.kt @@ -22,6 +22,9 @@ import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.Optional +/** + * This interface defines methods to handling profile information. It's implemented at the session level. + */ interface ProfileService { companion object Constants { @@ -29,9 +32,25 @@ interface ProfileService { const val AVATAR_URL_KEY = "avatar_url" } + /** + * Return the current dispayname for this user + * @param userId the userId param to look for + * + */ fun getDisplayName(userId: String, matrixCallback: MatrixCallback>): Cancelable + /** + * Return the current avatarUrl for this user. + * @param userId the userId param to look for + * + */ fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback>): Cancelable + /** + * Get the combined profile information for this user. + * This may return keys which are not limited to displayname or avatar_url. + * @param userId the userId param to look for + * + */ fun getProfile(userId: String, matrixCallback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt index 9b4a1f23d9..2f81965d0a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.api.session.room.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants /** * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsConstants.kt similarity index 92% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsConstants.kt index a039626d6e..16af3d216e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsConstants.kt @@ -15,7 +15,7 @@ * */ -package im.vector.matrix.android.api.session.room.powerlevers +package im.vector.matrix.android.api.session.room.powerlevels object PowerLevelsConstants { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt similarity index 91% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt index ec285f0e1f..b18a7dd97b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt @@ -15,7 +15,7 @@ * */ -package im.vector.matrix.android.api.session.room.powerlevers +package im.vector.matrix.android.api.session.room.powerlevels import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.model.PowerLevelsContent @@ -64,9 +64,7 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return the level */ fun notificationLevel(key: String): Int { - val value = powerLevelsContent.notifications[key] - ?: return PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL - return when (value) { + return when (val value = powerLevelsContent.notifications[key]) { // the first implementation was a string value is String -> value.toInt() is Int -> value diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt index eb016280d8..197d85f879 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileAPI.kt @@ -32,5 +32,5 @@ interface ProfileAPI { * @param userId the user id to fetch profile info */ @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}") - fun getProfile(@Path("userId") roomId: String): Call + fun getProfile(@Path("userId") userId: String): Call } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt index 2a72051491..093e54989b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -17,7 +17,7 @@ package im.vector.riotx.features.roommemberprofile /** - * Transient events for RoomProfile + * Transient events for RoomMemberProfile */ sealed class RoomMemberProfileViewEvents { data class Loading(val message: CharSequence) : RoomMemberProfileViewEvents() diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 737539937b..88f394e865 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -33,8 +33,8 @@ import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomSummary -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.api.util.toOptional @@ -196,7 +196,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v if (isIgnored) { session.unIgnoreUserIds(listOf(state.userId), ignoreActionCallback) } else { - session.ignoreUserIds(listOf(initialState.userId), ignoreActionCallback) + session.ignoreUserIds(listOf(state.userId), ignoreActionCallback) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index dfd534badd..37a36fbcf1 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -42,12 +42,10 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri if (data == null) { return } - - val roomSummary = data.roomSummary() - + val roomSummary = data.roomSummary() ?: return // Security buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) - val learnMoreSubtitle = if (data.isEncrypted) { + val learnMoreSubtitle = if (roomSummary.isEncrypted) { R.string.room_profile_encrypted_subtitle } else { R.string.room_profile_not_encrypted_subtitle @@ -73,10 +71,10 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri icon = R.drawable.ic_room_profile_notification, action = { callback?.onNotificationsClicked() } ) - val numberOfMembers = roomSummary?.joinedMembersCount?.toString() ?: "-" + val numberOfMembers = roomSummary.joinedMembersCount ?: 0 buildProfileAction( id = "member_list", - title = stringProvider.getString(R.string.room_profile_section_more_member_list, numberOfMembers), + title = stringProvider.getQuantityString(R.plurals.room_profile_section_more_member_list, numberOfMembers, numberOfMembers), icon = R.drawable.ic_room_profile_member_list, action = { callback?.onMemberListClicked() } ) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index f8994b895b..da161efd6a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -15,8 +15,6 @@ * */ -@file:Suppress("DEPRECATION") - package im.vector.riotx.features.roomprofile import android.os.Bundle diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index d7e86130f9..8134c93b4f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -64,10 +64,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R room.rx().liveRoomSummary() .unwrap() .execute { - copy( - roomSummary = it, - isEncrypted = room.isEncrypted() - ) + copy(roomSummary = it) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt index 7a5e74b10e..aed1488b07 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt @@ -24,8 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary data class RoomProfileViewState( val roomId: String, - val roomSummary: Async = Uninitialized, - val isEncrypted: Boolean = false + val roomSummary: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 3ba457bfb8..c3db239047 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -29,8 +29,8 @@ import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomMemberSummary -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants -import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 8c544a3dbf..c57145d10e 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -12,7 +12,10 @@ More Room settings Notifications - "%1$s people" + + "One person" + "%1$d people" + Uploads Leave Room "Leaving the room..." From 7c0f2d6e32b0f75a41a5d3c733d2d54dc68b481f Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 15 Jan 2020 11:48:02 +0100 Subject: [PATCH 17/19] Test: fix import --- .../src/test/java/im/vector/riotx/features/home/UserColorTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/test/java/im/vector/riotx/features/home/UserColorTest.kt b/vector/src/test/java/im/vector/riotx/features/home/UserColorTest.kt index e0795afb58..da8dba4137 100644 --- a/vector/src/test/java/im/vector/riotx/features/home/UserColorTest.kt +++ b/vector/src/test/java/im/vector/riotx/features/home/UserColorTest.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home import im.vector.riotx.R +import im.vector.riotx.core.utils.getColorFromUserId import org.junit.Assert.assertEquals import org.junit.Test From b44ddcfd61fa18c33de64b47283f2162e74a0223 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 15 Jan 2020 14:57:21 +0100 Subject: [PATCH 18/19] Clean code after Benoit's review #2 --- .../internal/session/profile/ProfileModule.kt | 4 ++-- vector/src/main/AndroidManifest.xml | 8 ++++++- ...fileItemAction.kt => ProfileActionItem.kt} | 2 +- .../epoxy/profiles/ProfileItemExtensions.kt | 4 ++-- ...leItemSection.kt => ProfileSectionItem.kt} | 2 +- .../RoomListQuickActionsEpoxyController.kt | 2 -- .../features/navigation/DefaultNavigator.kt | 22 +++++++++++-------- .../RoomMemberProfileFragment.kt | 6 ++--- 8 files changed, 28 insertions(+), 22 deletions(-) rename vector/src/main/java/im/vector/riotx/core/epoxy/profiles/{ProfileItemAction.kt => ProfileActionItem.kt} (96%) rename vector/src/main/java/im/vector/riotx/core/epoxy/profiles/{ProfileItemSection.kt => ProfileSectionItem.kt} (93%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt index a3084033a3..7005a5341f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/profile/ProfileModule.kt @@ -38,8 +38,8 @@ internal abstract class ProfileModule { } @Binds - abstract fun bindProfileService(userService: DefaultProfileService): ProfileService + abstract fun bindProfileService(service: DefaultProfileService): ProfileService @Binds - abstract fun bindGetProfileTask(getProfileInfoTask: DefaultGetProfileInfoTask): GetProfileInfoTask + abstract fun bindGetProfileTask(task: DefaultGetProfileInfoTask): GetProfileInfoTask } diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index febdefdb16..a0d4402767 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -125,7 +125,13 @@ - + + + + diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt similarity index 96% rename from vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt rename to vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt index 6d80a095d4..0a7dcb0de7 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemAction.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileActionItem.kt @@ -30,7 +30,7 @@ import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.features.themes.ThemeUtils @EpoxyModelClass(layout = R.layout.item_profile_action) -abstract class ProfileItemAction : VectorEpoxyModel() { +abstract class ProfileActionItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var title: String diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt index 78a7bea014..3a9fff52af 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt @@ -22,7 +22,7 @@ import com.airbnb.epoxy.EpoxyController import im.vector.riotx.core.epoxy.DividerItem_ fun EpoxyController.buildProfileSection(title: String) { - profileItemSection { + profileSectionItem { id("section_$title") title(title) } @@ -38,7 +38,7 @@ fun EpoxyController.buildProfileAction( divider: Boolean = true, action: () -> Unit ) { - profileItemAction { + profileActionItem { iconRes(icon) id("action_$id") subtitle(subtitle) diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileSectionItem.kt similarity index 93% rename from vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt rename to vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileSectionItem.kt index f24fd96ebd..ea3e66ad03 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemSection.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileSectionItem.kt @@ -24,7 +24,7 @@ import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyModel @EpoxyModelClass(layout = R.layout.item_profile_section) -abstract class ProfileItemSection: VectorEpoxyModel() { +abstract class ProfileSectionItem: VectorEpoxyModel() { @EpoxyAttribute lateinit var title: String diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 2b7d87267e..0948490d12 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -64,8 +64,6 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar id("leave_separator") } RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) - - // Leave } } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 4551b179a5..7ca6e94d70 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -56,16 +56,9 @@ class DefaultNavigator @Inject constructor( fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast()) return } - val args = RoomDetailArgs(roomId, eventId) val intent = RoomDetailActivity.newIntent(context, args) - if (buildTask) { - val stackBuilder = TaskStackBuilder.create(context) - stackBuilder.addNextIntentWithParentStack(intent) - stackBuilder.startActivities() - } else { - context.startActivity(intent) - } + startActivity(context, intent, buildTask) } override fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String?, buildTask: Boolean) { @@ -86,7 +79,8 @@ class DefaultNavigator @Inject constructor( override fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean) { val args = RoomMemberProfileArgs(userId = userId, roomId = roomId) - context.startActivity(RoomMemberProfileActivity.newIntent(context, args)) + val intent = RoomMemberProfileActivity.newIntent(context, args) + startActivity(context, intent, buildTask) } override fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData) { @@ -141,4 +135,14 @@ class DefaultNavigator @Inject constructor( override fun openRoomProfile(context: Context, roomId: String) { context.startActivity(RoomProfileActivity.newIntent(context, roomId)) } + + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { + if (buildTask) { + val stackBuilder = TaskStackBuilder.create(context) + stackBuilder.addNextIntentWithParentStack(intent) + stackBuilder.startActivities() + } else { + context.startActivity(intent) + } + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index b8a6f247b2..23db6b53a8 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -15,8 +15,6 @@ * */ -@file:Suppress("DEPRECATION") - package im.vector.riotx.features.roommemberprofile import android.os.Bundle @@ -80,8 +78,8 @@ class RoomMemberProfileFragment @Inject constructor( .subscribe { dismissLoadingDialog() when (it) { - is RoomMemberProfileViewEvents.Loading -> showLoadingDialog(it.message) - is RoomMemberProfileViewEvents.Failure -> showErrorInSnackbar(it.throwable) + is RoomMemberProfileViewEvents.Loading -> showLoadingDialog(it.message) + is RoomMemberProfileViewEvents.Failure -> showErrorInSnackbar(it.throwable) } } .disposeOnDestroyView() From 2c331671eec843e6588708eac62a66c2fbbf5a46 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 15 Jan 2020 17:44:38 +0100 Subject: [PATCH 19/19] Fix string --- vector/src/main/res/values/strings_riotX.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index c57145d10e..3986d06f55 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -18,7 +18,7 @@ Uploads Leave Room - "Leaving the room..." + "Leaving the room…" Admins Moderators