diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 9e1fa7cca9647794bc6338f1b36f7a9566594aa9..39d9f8f8b72e832fa6bd0a04b21f17035a193088 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -71,6 +71,7 @@ native <methods>; -keep class org.futo.circles.core.model.CircleRoomTypeArg -keep class org.futo.circles.auth.model.PasswordModeArg -keep class org.futo.circles.auth.model.TermsModeArg +-keep class org.futo.circles.core.model.ShareUrlTypeArg -keepattributes SourceFile,LineNumberTable # Keep file names and line numbers. -keep public class * extends java.lang.Exception # Optional: Keep custom exceptions. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 003e7251124917384f494aa05e902d8d98751223..c05be574a96487ba5484364dd83bb107ed9b255d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,9 +21,9 @@ android:name=".App" android:allowBackup="false" android:enableOnBackInvokedCallback="true" + android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:hardwareAccelerated="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Circles" @@ -31,13 +31,11 @@ tools:targetApi="tiramisu"> <activity android:name=".auth.feature.active_sessions.verify.qr.QrScannerActivity" - android:exported="false" - tools:ignore="LockedOrientationActivity" /> + android:exported="false" /> <activity android:name=".MainActivity" android:exported="true" - android:launchMode="singleTask" - tools:ignore="LockedOrientationActivity"> + android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -56,6 +54,9 @@ <data android:host="circu.li" /> <data android:pathPrefix="/room" /> <data android:pathPrefix="/profile" /> + <data android:pathPrefix="/group" /> + <data android:pathPrefix="/gallery" /> + <data android:pathPrefix="/timeline" /> </intent-filter> @@ -63,8 +64,7 @@ <activity android:name=".gallery.feature.share.UploadToGalleryActivity" android:exported="true" - android:launchMode="singleTask" - tools:ignore="LockedOrientationActivity"> + android:launchMode="singleTask"> <intent-filter android:label="@string/gallery"> <action android:name="android.intent.action.SEND" /> @@ -77,8 +77,7 @@ <activity android:name=".feature.share.circle.ShareWithCircleActivity" android:exported="true" - android:launchMode="singleTask" - tools:ignore="LockedOrientationActivity"> + android:launchMode="singleTask"> <intent-filter android:label="@string/circle"> <action android:name="android.intent.action.SEND" /> @@ -133,14 +132,17 @@ android:foregroundServiceType="dataSync" tools:ignore="Instantiatable" /> - <service android:name="com.google.android.gms.metadata.ModuleDependencies" + <service + android:name="com.google.android.gms.metadata.ModuleDependencies" android:enabled="false" android:exported="false" tools:ignore="MissingClass"> <intent-filter> <action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" /> </intent-filter> - <meta-data android:name="photopicker_activity:0:required" android:value="" /> + <meta-data + android:name="photopicker_activity:0:required" + android:value="" /> </service> <receiver diff --git a/app/src/main/java/org/futo/circles/feature/home/DeepLinkIntentHandler.kt b/app/src/main/java/org/futo/circles/feature/home/DeepLinkIntentHandler.kt new file mode 100644 index 0000000000000000000000000000000000000000..60a9f98e067c9dd9f7a0e1d6f7124eb953cbb07f --- /dev/null +++ b/app/src/main/java/org/futo/circles/feature/home/DeepLinkIntentHandler.kt @@ -0,0 +1,5 @@ +package org.futo.circles.feature.home + +interface DeepLinkIntentHandler { + fun onNewIntent() +} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/home/HomeFragment.kt b/app/src/main/java/org/futo/circles/feature/home/HomeFragment.kt index cf90204f39803972924ae2934a9c80b15cbb55cf..b32ed46a3a06197d06a41b37cf9f7edee08623fa 100644 --- a/app/src/main/java/org/futo/circles/feature/home/HomeFragment.kt +++ b/app/src/main/java/org/futo/circles/feature/home/HomeFragment.kt @@ -18,17 +18,15 @@ import dagger.hilt.android.AndroidEntryPoint import org.futo.circles.MainActivity import org.futo.circles.R import org.futo.circles.auth.feature.workspace.WorkspaceDialogFragment -import org.futo.circles.core.base.SHARE_PROFILE_URL_PREFIX -import org.futo.circles.core.base.SHARE_ROOM_URL_PREFIX import org.futo.circles.core.extensions.navigateSafe import org.futo.circles.core.extensions.observeData import org.futo.circles.core.extensions.observeResponse import org.futo.circles.core.extensions.setSupportActionBar +import org.futo.circles.core.feature.picker.helper.RuntimePermissionHelper import org.futo.circles.core.model.CircleRoomTypeArg import org.futo.circles.core.model.GROUP_TYPE import org.futo.circles.core.model.LoadingData import org.futo.circles.core.model.TIMELINE_TYPE -import org.futo.circles.core.feature.picker.helper.RuntimePermissionHelper import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.core.view.LoadingDialog import org.futo.circles.databinding.FragmentBottomNavigationBinding @@ -36,9 +34,6 @@ import org.futo.circles.gallery.feature.backup.service.MediaBackupServiceManager import org.matrix.android.sdk.api.session.getRoomSummary import javax.inject.Inject -interface DeepLinkIntentHandler { - fun onNewIntent() -} @AndroidEntryPoint class HomeFragment : Fragment(R.layout.fragment_bottom_navigation), DeepLinkIntentHandler { @@ -104,14 +99,10 @@ class HomeFragment : Fragment(R.layout.fragment_bottom_navigation), DeepLinkInte private fun handleOpenFromShareRoomUrl() { val uri = activity?.intent?.data ?: return val uriString = uri.toString() - if (uriString.startsWith(SHARE_ROOM_URL_PREFIX) - || uriString.startsWith(SHARE_PROFILE_URL_PREFIX) - ) { - findNavController().navigateSafe( - HomeFragmentDirections.toRoomWellKnownDialogFragment(uriString) - ) - activity?.intent?.data = null - } + findNavController().navigateSafe( + HomeFragmentDirections.toRoomWellKnownDialogFragment(uriString) + ) + activity?.intent?.data = null } private fun setupObservers() { diff --git a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt index 607b6b9b46ec70f3152bfaead120aa69d0320160..73333a296aa4169bec753ebea3718c58e03209a2 100644 --- a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt +++ b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt @@ -1,7 +1,6 @@ package org.futo.circles.feature.people import androidx.lifecycle.asFlow -import androidx.lifecycle.map import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -9,22 +8,24 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.withContext import org.futo.circles.core.extensions.createResult -import org.futo.circles.core.provider.MatrixSessionProvider +import org.futo.circles.core.feature.room.knoks.KnockRequestsDataSource import org.futo.circles.core.feature.select_users.SearchUserDataSource import org.futo.circles.core.feature.workspace.SharedCircleDataSource +import org.futo.circles.core.model.KnockRequestListItem +import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.mapping.toPeopleUserListItem import org.futo.circles.model.PeopleHeaderItem import org.futo.circles.model.PeopleItemType import org.futo.circles.model.PeopleListItem +import org.futo.circles.model.toPeopleRequestListItem import org.matrix.android.sdk.api.session.getRoom -import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams -import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.user.model.User import javax.inject.Inject class PeopleDataSource @Inject constructor( private val searchUserDataSource: SearchUserDataSource, - private val sharedCircleDataSource: SharedCircleDataSource + private val sharedCircleDataSource: SharedCircleDataSource, + private val knockRequestsDataSource: KnockRequestsDataSource ) { private val session = MatrixSessionProvider.currentSession @@ -37,10 +38,9 @@ class PeopleDataSource @Inject constructor( suspend fun declineFollowRequest(userId: String) = createResult { session?.getRoom(profileRoomId)?.membershipService()?.remove(userId) } - private fun getProfileRoomMembersKnockFlow(): Flow<List<User>> = - session?.getRoom(profileRoomId)?.membershipService() - ?.getRoomMembersLive(roomMemberQueryParams { memberships = listOf(Membership.KNOCK) }) - ?.map { it.map { User(it.userId, it.displayName, it.avatarUrl) } }?.asFlow() ?: flowOf() + private fun getProfileRoomMembersKnockFlow(): Flow<List<KnockRequestListItem>> = + knockRequestsDataSource.getKnockRequestsListItemsLiveData(profileRoomId)?.asFlow() + ?: flowOf() suspend fun getPeopleList(query: String) = combine( searchUserDataSource.searchKnownUsers(query), @@ -62,11 +62,11 @@ class PeopleDataSource @Inject constructor( knowUsers: List<User>, suggestions: List<User>, ignoredUsers: List<User>, - requests: List<User> + requests: List<KnockRequestListItem> ): List<PeopleListItem> { val uniqueItemsList = mutableListOf<PeopleListItem>().apply { addAll(ignoredUsers.map { it.toPeopleUserListItem(PeopleItemType.Ignored) }) - addAll(requests.map { it.toPeopleUserListItem(PeopleItemType.Request) }) + addAll(requests.map { it.toPeopleRequestListItem() }) addAll(knowUsers.map { it.toPeopleUserListItem(getKnownUserItemType(it.userId)) }) addAll(suggestions.map { it.toPeopleUserListItem(PeopleItemType.Suggestion) }) }.distinctBy { it.id }.filterNot { it.id == session?.myUserId } diff --git a/app/src/main/java/org/futo/circles/feature/people/list/PeopleViewHolder.kt b/app/src/main/java/org/futo/circles/feature/people/list/PeopleViewHolder.kt index aedaebf8985b78e8fc148ce7891e359f3e5444b9..22a0645f64de68e8450517de045843a24157d51b 100644 --- a/app/src/main/java/org/futo/circles/feature/people/list/PeopleViewHolder.kt +++ b/app/src/main/java/org/futo/circles/feature/people/list/PeopleViewHolder.kt @@ -3,17 +3,19 @@ package org.futo.circles.feature.people.list import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import org.futo.circles.core.base.list.ViewBindingHolder +import org.futo.circles.core.base.list.context import org.futo.circles.core.databinding.ListItemInviteHeaderBinding import org.futo.circles.core.extensions.loadProfileIcon import org.futo.circles.core.extensions.onClick -import org.futo.circles.core.base.list.ViewBindingHolder -import org.futo.circles.core.base.list.context +import org.futo.circles.core.extensions.setIsVisible import org.futo.circles.core.model.CirclesUserSummary import org.futo.circles.databinding.ListItemPeopleDefaultBinding import org.futo.circles.databinding.ListItemPeopleIgnoredBinding import org.futo.circles.databinding.ListItemPeopleRequestBinding import org.futo.circles.model.PeopleHeaderItem import org.futo.circles.model.PeopleListItem +import org.futo.circles.model.PeopleRequestListItem import org.futo.circles.model.PeopleUserListItem import org.futo.circles.model.PeopleUserListItemPayload @@ -81,8 +83,12 @@ class PeopleRequestUserViewHolder( } override fun bind(data: PeopleListItem) { - val user = (data as? PeopleUserListItem)?.user ?: return + val user = (data as? PeopleRequestListItem)?.user ?: return bindUser(user) + binding.tvReasonMessage.apply { + setIsVisible(data.reasonMessage != null) + text = data.reasonMessage + } } override fun bindPayload(data: PeopleUserListItemPayload) { diff --git a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDataSource.kt b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDataSource.kt index 3a4c5d323213a39264a416831a0f9c777c5753b6..436380d3bcd220798f0fb04866f5ebc7432d2279 100644 --- a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDataSource.kt +++ b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDataSource.kt @@ -2,17 +2,11 @@ package org.futo.circles.feature.room.well_known import org.futo.circles.core.extensions.Response import org.futo.circles.core.extensions.createResult -import org.futo.circles.core.extensions.getOrFetchUser -import org.futo.circles.core.extensions.notEmptyDisplayName import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.model.RoomPublicInfo import org.futo.circles.model.RoomUrlData -import org.futo.circles.model.UserPublicInfo -import org.futo.circles.model.UserUrlData import org.futo.circles.model.toRoomPublicInfo -import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.getRoom -import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult import javax.inject.Inject @@ -21,30 +15,12 @@ class RoomWellKnownDataSource @Inject constructor() { val session by lazy { MatrixSessionProvider.getSessionOrThrow() } suspend fun resolveRoom(roomUrlData: RoomUrlData): Response<RoomPublicInfo> = createResult { - session.getRoom(roomUrlData.roomId)?.roomSummary()?.toRoomPublicInfo() + session.getRoom(roomUrlData.roomId)?.roomSummary()?.toRoomPublicInfo(roomUrlData.type) ?.let { return@createResult it } when (val peekResult = session.roomService().peekRoom(roomUrlData.roomId)) { - is PeekResult.Success -> peekResult.toRoomPublicInfo() + is PeekResult.Success -> peekResult.toRoomPublicInfo(roomUrlData.type) is PeekResult.PeekingNotAllowed -> roomUrlData.toRoomPublicInfo() PeekResult.UnknownAlias -> throw IllegalArgumentException("Room not found") } } - - suspend fun resolveUser(userUrlData: UserUrlData): Response<UserPublicInfo> = - createResult { - val roomInfo = tryOrNull { - (resolveRoom( - RoomUrlData(userUrlData.sharedSpaceId, userUrlData.sharedSpaceId, null) - ) as? Response.Success)?.data - } - val user = session.getOrFetchUser(userUrlData.userId) - UserPublicInfo( - id = userUrlData.userId, - displayName = user.notEmptyDisplayName(), - avatarUrl = user.avatarUrl, - sharedSpaceId = userUrlData.sharedSpaceId, - memberCount = roomInfo?.memberCount ?: 0, - membership = roomInfo?.membership ?: Membership.NONE - ) - } } \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDialogFragment.kt index 9a8be70f4f0820a1aaadf2645cdc6c86b4bffe91..e2fa98343937dc54b6d7a7ca3fc8c4faa385286a 100644 --- a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDialogFragment.kt +++ b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownDialogFragment.kt @@ -6,6 +6,8 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import org.futo.circles.R +import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment +import org.futo.circles.core.extensions.getText import org.futo.circles.core.extensions.gone import org.futo.circles.core.extensions.loadProfileIcon import org.futo.circles.core.extensions.observeData @@ -13,10 +15,10 @@ import org.futo.circles.core.extensions.observeResponse import org.futo.circles.core.extensions.onBackPressed import org.futo.circles.core.extensions.setIsVisible import org.futo.circles.core.extensions.showSuccess -import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment +import org.futo.circles.core.model.ShareUrlTypeArg import org.futo.circles.databinding.DialogFragmentRoomWellKnownBinding import org.futo.circles.model.RoomPublicInfo -import org.futo.circles.model.UserPublicInfo +import org.futo.circles.model.isProfile import org.matrix.android.sdk.api.session.room.model.Membership @AndroidEntryPoint @@ -37,7 +39,7 @@ class RoomWellKnownDialogFragment : private fun setupViews() { binding.btnRequest.setOnClickListener { - viewModel.sendKnockRequest() + viewModel.sendKnockRequest(binding.tilRequestMessage.getText().takeIf { it.isNotEmpty() }) binding.btnRequest.setIsLoading(true) } } @@ -48,11 +50,6 @@ class RoomWellKnownDialogFragment : onRequestInvoked = { binding.vLoading.gone() }, error = { bindError(it) } ) - viewModel.userPublicInfoLiveData.observeResponse(this, - success = { userInfo -> bindUserData(userInfo) }, - onRequestInvoked = { binding.vLoading.gone() }, - error = { bindError(it) } - ) viewModel.knockRequestLiveData.observeResponse(this, success = { showSuccess(getString(R.string.request_sent)) @@ -74,71 +71,51 @@ class RoomWellKnownDialogFragment : binding.tvRoomName.text = message } - private fun bindGeneralData( - url: String?, - name: String, - membership: Membership - ) { - with(binding) { - ivCover.loadProfileIcon(url, name) - toolbar.title = name - tvRoomName.text = name - btnRequest.setIsVisible(shouldShowKnockButton(membership)) - } - } private fun bindRoomData(roomInfo: RoomPublicInfo) { with(binding) { - bindGeneralData( - roomInfo.avatarUrl, - roomInfo.displayName, - roomInfo.membership - ) + ivCover.apply { + if (roomInfo.avatarUrl != null || roomInfo.name != null) + loadProfileIcon(roomInfo.avatarUrl, roomInfo.name ?: "") + else setImageResource(R.drawable.ic_logo) + } + tvRoomName.apply { + setIsVisible(roomInfo.name != null) + text = roomInfo.name + } + btnRequest.setIsVisible(shouldShowKnockButton(roomInfo.membership)) + tilRequestMessage.setIsVisible(shouldShowKnockButton(roomInfo.membership)) binding.tvRoomId.text = roomInfo.id tvMembersCount.apply { setIsVisible(roomInfo.memberCount > 0) - text = getString(R.string.joined_members_count, roomInfo.memberCount) + text = getString( + if (roomInfo.isProfile()) R.string.following_format + else R.string.joined_members_count, roomInfo.memberCount + ) + } + btnRequest.setText(getString(if (roomInfo.isProfile()) R.string.request_to_follow else R.string.request_to_join)) + tvTopic.apply { + setIsVisible(roomInfo.topic?.isNotEmpty() == true) + text = getString(R.string.topic_format, roomInfo.topic ?: "") + } + tvType.apply { + setIsVisible(roomInfo.type != ShareUrlTypeArg.ROOM) + text = getString(R.string.room_type_format, roomInfo.type.typeKey) } - btnRequest.setText(getString(R.string.requested_to_join)) - tvTopic.setIsVisible(roomInfo.topic?.isNotEmpty() == true) - tvTopic.text = roomInfo.topic ?: "" tvMembersip.text = getString( when (roomInfo.membership) { Membership.NONE, Membership.KNOCK, - Membership.LEAVE -> R.string.request_to_become_member_room + Membership.LEAVE -> if (roomInfo.isProfile()) R.string.send_request_to_follow_user + else R.string.request_to_become_member_room - Membership.INVITE -> R.string.you_have_pending_invitation_room - Membership.JOIN -> R.string.you_are_already_member_room - Membership.BAN -> R.string.you_are_banned_room - } - ) - } - } + Membership.INVITE -> if (roomInfo.isProfile()) R.string.you_have_pending_invitation_user + else R.string.you_have_pending_invitation_room - private fun bindUserData(userInfo: UserPublicInfo) { - with(binding) { - bindGeneralData( - userInfo.avatarUrl, - userInfo.displayName, - userInfo.membership - ) - binding.tvRoomId.text = userInfo.id - tvMembersCount.apply { - setIsVisible(userInfo.memberCount > 0) - text = getString(R.string.following_format, userInfo.memberCount) - } - btnRequest.setText(getString(R.string.follow)) - tvTopic.gone() - tvMembersip.text = getString( - when (userInfo.membership) { - Membership.NONE, - Membership.KNOCK, - Membership.LEAVE -> R.string.send_request_to_follow_user + Membership.JOIN -> if (roomInfo.isProfile()) R.string.you_are_already_following_user + else R.string.you_are_already_member_room - Membership.INVITE -> R.string.you_have_pending_invitation_user - Membership.JOIN -> R.string.you_are_already_following_user - Membership.BAN -> R.string.you_are_banned_user + Membership.BAN -> R.string.you_are_banned_room } ) } diff --git a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownViewModel.kt b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownViewModel.kt index cfbd79bfabc2a52e9ff93f6d10bfc287f6d7e598..4a8f3ff157cc4c2189499c50850c034c00aa7633 100644 --- a/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownViewModel.kt +++ b/app/src/main/java/org/futo/circles/feature/room/well_known/RoomWellKnownViewModel.kt @@ -11,9 +11,6 @@ import org.futo.circles.core.extensions.launchBg import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.model.RoomPublicInfo import org.futo.circles.model.RoomUrlData -import org.futo.circles.model.UrlData -import org.futo.circles.model.UserPublicInfo -import org.futo.circles.model.UserUrlData import org.futo.circles.model.parseUrlData import javax.inject.Inject @@ -26,17 +23,12 @@ class RoomWellKnownViewModel @Inject constructor( private val url: String = savedStateHandle.getOrThrow("url") val roomPublicInfoLiveData = SingleEventLiveData<Response<RoomPublicInfo>>() - val userPublicInfoLiveData = SingleEventLiveData<Response<UserPublicInfo>>() val knockRequestLiveData = SingleEventLiveData<Response<Unit?>>() val parseErrorEventLiveData = SingleEventLiveData<Unit>() - private var urlData: UrlData? = parseUrlData(url) + private var urlData: RoomUrlData? = parseUrlData(url) init { - when (val data = urlData) { - is RoomUrlData -> fetchRoomPublicInfo(data) - is UserUrlData -> fetchUserPublicInfo(data) - null -> parseErrorEventLiveData.postValue(Unit) - } + urlData?.let { fetchRoomPublicInfo(it) } ?: parseErrorEventLiveData.postValue(Unit) } private fun fetchRoomPublicInfo(data: RoomUrlData) { @@ -46,18 +38,13 @@ class RoomWellKnownViewModel @Inject constructor( } } - private fun fetchUserPublicInfo(data: UserUrlData) { - launchBg { - val result = dataSource.resolveUser(data) - userPublicInfoLiveData.postValue(result) - } - } - - fun sendKnockRequest() { - val roomId = urlData?.knockRoomId ?: return + fun sendKnockRequest(message: String?) { + val roomId = urlData?.roomId ?: return launchBg { val result = - createResult { MatrixSessionProvider.currentSession?.roomService()?.knock(roomId) } + createResult { + MatrixSessionProvider.currentSession?.roomService()?.knock(roomId, message) + } knockRequestLiveData.postValue(result) } } diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt index 1e095afa621d733b329e2fdcd63a11c4c3551f79..1f5a46452a67060390b67e67a4bb7b131f4461c9 100644 --- a/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt +++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt @@ -4,6 +4,7 @@ import androidx.navigation.fragment.findNavController import org.futo.circles.R import org.futo.circles.core.extensions.navigateSafe import org.futo.circles.core.extensions.showError +import org.futo.circles.core.model.ShareUrlTypeArg class SettingsNavigator(private val fragment: SettingsFragment) { @@ -41,7 +42,7 @@ class SettingsNavigator(private val fragment: SettingsFragment) { } fragment.findNavController() .navigateSafe( - SettingsFragmentDirections.toShareProfileDialogFragment(sharedSpaceId, true) + SettingsFragmentDirections.toShareProfileDialogFragment(sharedSpaceId, ShareUrlTypeArg.PROFILE) ) } diff --git a/app/src/main/java/org/futo/circles/model/PeopleListItem.kt b/app/src/main/java/org/futo/circles/model/PeopleListItem.kt index 87b0b02762cb9111114bb09a680964cf87f0fd73..fc79e221d6adfec8da216c98020e95d2a4af243b 100644 --- a/app/src/main/java/org/futo/circles/model/PeopleListItem.kt +++ b/app/src/main/java/org/futo/circles/model/PeopleListItem.kt @@ -3,6 +3,8 @@ package org.futo.circles.model import org.futo.circles.R import org.futo.circles.core.base.list.IdEntity import org.futo.circles.core.model.CirclesUserSummary +import org.futo.circles.core.model.KnockRequestListItem +import org.futo.circles.core.model.toCircleUser enum class PeopleItemType { Header, Friend, Following, Follower, Request, Known, Suggestion, Ignored } sealed class PeopleListItem( @@ -31,3 +33,12 @@ class PeopleUserListItem( ) : PeopleListItem(type) { override val id: String = user.id } + +class PeopleRequestListItem( + val user: CirclesUserSummary, + val reasonMessage: String? +) : PeopleListItem(PeopleItemType.Request) { + override val id: String = user.id +} + +fun KnockRequestListItem.toPeopleRequestListItem() = PeopleRequestListItem(toCircleUser(), message) diff --git a/app/src/main/java/org/futo/circles/model/RoomPublicInfo.kt b/app/src/main/java/org/futo/circles/model/RoomPublicInfo.kt index 3ca3988ac8d1b95052219cbec08ba5821fc811a5..82952409de6859c672ae426b4d1b365d48720f53 100644 --- a/app/src/main/java/org/futo/circles/model/RoomPublicInfo.kt +++ b/app/src/main/java/org/futo/circles/model/RoomPublicInfo.kt @@ -1,42 +1,48 @@ package org.futo.circles.model -import org.futo.circles.core.mapping.nameOrId +import org.futo.circles.core.model.ShareUrlTypeArg import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.peeking.PeekResult data class RoomPublicInfo( val id: String, - val displayName: String, + val name: String?, val avatarUrl: String?, val topic: String?, val memberCount: Int, - val membership: Membership + val membership: Membership, + val type: ShareUrlTypeArg ) -fun RoomSummary.toRoomPublicInfo() = RoomPublicInfo( +fun RoomPublicInfo.isProfile() = type == ShareUrlTypeArg.PROFILE + +fun RoomSummary.toRoomPublicInfo(urlType: ShareUrlTypeArg) = RoomPublicInfo( id = roomId, - displayName = nameOrId(), - avatarUrl = avatarUrl, + name = name.takeIf { it.isNotEmpty() }, + avatarUrl = avatarUrl.takeIf { it.isNotEmpty() }, topic = topic, memberCount = joinedMembersCount ?: 0, - membership = membership + membership = membership, + type = urlType ) -fun PeekResult.Success.toRoomPublicInfo() = RoomPublicInfo( +fun PeekResult.Success.toRoomPublicInfo(urlType: ShareUrlTypeArg) = RoomPublicInfo( id = roomId, - displayName = name?.takeIf { it.isNotEmpty() } ?: roomId, - avatarUrl = avatarUrl, + name = name?.takeIf { it.isNotEmpty() }, + avatarUrl = avatarUrl?.takeIf { it.isNotEmpty() }, topic = topic, memberCount = numJoinedMembers ?: 0, - membership = Membership.NONE + membership = Membership.NONE, + type = urlType ) fun RoomUrlData.toRoomPublicInfo() = RoomPublicInfo( id = roomId, - displayName = roomName, + name = null, avatarUrl = null, - topic = topic, + topic = null, memberCount = 0, - membership = Membership.NONE + membership = Membership.NONE, + type = type ) \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/model/UrlData.kt b/app/src/main/java/org/futo/circles/model/UrlData.kt index 02cfe954a3147987af1cc3921bb811e7afcabfea..f3cf5294b8fe225d008d1fe4a769778860e643cc 100644 --- a/app/src/main/java/org/futo/circles/model/UrlData.kt +++ b/app/src/main/java/org/futo/circles/model/UrlData.kt @@ -1,38 +1,18 @@ package org.futo.circles.model -import org.futo.circles.core.base.SHARE_PROFILE_URL_PREFIX -import org.futo.circles.core.base.SHARE_ROOM_URL_PREFIX - - -sealed class UrlData(val knockRoomId: String) +import org.futo.circles.core.feature.share.BASE_SHARE_URL +import org.futo.circles.core.model.ShareUrlTypeArg +import org.futo.circles.core.model.shareUrlTypeArgFromType data class RoomUrlData( val roomId: String, - val roomName: String, - val topic: String? -) : UrlData(roomId) - -data class UserUrlData( - val userId: String, - val sharedSpaceId: String -) : UrlData(sharedSpaceId) - -fun parseUrlData(url: String): UrlData? = - if (url.startsWith(SHARE_ROOM_URL_PREFIX)) createRoomUrlData(url) - else if (url.startsWith(SHARE_PROFILE_URL_PREFIX)) createUserUrlData(url) - else null - -private fun createRoomUrlData(url: String): RoomUrlData? { - val data = url.removePrefix(SHARE_ROOM_URL_PREFIX).split("/") - val roomId = data.getOrNull(0) ?: return null - val roomName = data.getOrNull(1) ?: return null - val topic = data.getOrNull(2) - return RoomUrlData(roomId, roomName, topic) -} - -private fun createUserUrlData(url: String): UserUrlData? { - val data = url.removePrefix(SHARE_PROFILE_URL_PREFIX).split("/") - val userId = data.getOrNull(0) ?: return null - val sharedSpaceId = data.getOrNull(1) ?: return null - return UserUrlData(userId, sharedSpaceId) + val type: ShareUrlTypeArg +) + +fun parseUrlData(url: String): RoomUrlData? { + val data = url.removePrefix(BASE_SHARE_URL).split("/") + val typeString = data.getOrNull(0) ?: return null + val type = shareUrlTypeArgFromType(typeString) ?: return null + val roomId = data.getOrNull(1) ?: return null + return RoomUrlData(roomId, type) } \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/model/UserPublicInfo.kt b/app/src/main/java/org/futo/circles/model/UserPublicInfo.kt deleted file mode 100644 index 4993600dc7bd5f4864bcacfa5ae68700db9821ba..0000000000000000000000000000000000000000 --- a/app/src/main/java/org/futo/circles/model/UserPublicInfo.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.futo.circles.model - -import org.matrix.android.sdk.api.session.room.model.Membership - -data class UserPublicInfo( - val id: String, - val displayName: String, - val avatarUrl: String?, - val sharedSpaceId: String, - val memberCount: Int, - val membership: Membership -) \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_fragment_room_well_known.xml b/app/src/main/res/layout/dialog_fragment_room_well_known.xml index 74743e8f0e3c02b12225d9b3eecc35a7176bebe6..c521dbb67e449eb75283389cd7beafd13eed1a8d 100644 --- a/app/src/main/res/layout/dialog_fragment_room_well_known.xml +++ b/app/src/main/res/layout/dialog_fragment_room_well_known.xml @@ -14,8 +14,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navigationIcon="?attr/homeAsUpIndicator" - app:titleCentered="true" - tools:title="Room name" /> + app:title="URL info" + app:titleCentered="true" /> <View @@ -32,12 +32,16 @@ android:id="@+id/ivCover" android:layout_width="@dimen/group_icon_size" android:layout_height="@dimen/group_icon_size" - android:layout_marginTop="16dp" + android:layout_marginTop="36dp" android:scaleType="centerCrop" + app:layout_constraintBottom_toTopOf="@id/tvRoomName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/toolbarDivider" - app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.GroupIconRadius" /> + app:layout_constraintVertical_bias="0" + app:layout_constraintVertical_chainStyle="packed" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.GroupIconRadius" + tools:src="@drawable/ic_logo" /> <ProgressBar @@ -51,12 +55,13 @@ <TextView android:id="@+id/tvRoomName" - style="@style/body" + style="@style/title2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="36dp" android:layout_marginTop="36dp" android:gravity="center" + app:layout_constraintBottom_toTopOf="@id/tvRoomId" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/ivCover" @@ -64,37 +69,59 @@ <TextView android:id="@+id/tvRoomId" + style="@style/body" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="36dp" android:layout_marginTop="8dp" android:gravity="center" + app:layout_constraintBottom_toTopOf="@id/tvType" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvRoomName" + app:layout_goneMarginTop="36dp" tools:text="Aasdsadasdsaf" /> + <TextView + android:id="@+id/tvType" + style="@style/body" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="36dp" + android:layout_marginTop="8dp" + android:gravity="center" + app:layout_constraintBottom_toTopOf="@id/tvTopic" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tvRoomId" + tools:text="Type: " + tools:visibility="visible" /> + <TextView android:id="@+id/tvTopic" + style="@style/body" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="36dp" android:layout_marginTop="8dp" android:gravity="center" android:visibility="gone" + app:layout_constraintBottom_toTopOf="@id/tvMembersCount" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/tvRoomId" + app:layout_constraintTop_toBottomOf="@id/tvType" tools:text="Topic: " tools:visibility="visible" /> <TextView android:id="@+id/tvMembersCount" + style="@style/body" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="36dp" android:layout_marginTop="8dp" android:gravity="center" + app:layout_constraintBottom_toTopOf="@id/tvMembersip" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvTopic" @@ -102,15 +129,48 @@ <TextView android:id="@+id/tvMembersip" + style="@style/body" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="36dp" android:layout_marginTop="8dp" android:gravity="center" + app:layout_constraintBottom_toTopOf="@id/tilRequestMessage" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvMembersCount" - tools:text="@string/you_have_pending_invitation" /> + tools:text="@string/you_have_pending_invitation_room" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/tilRequestMessage" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginTop="24dp" + android:visibility="gone" + app:hintEnabled="false" + app:layout_constraintBottom_toTopOf="@id/btnRequest" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tvMembersip" + tools:visibility="visible"> + + <com.google.android.material.textfield.TextInputEditText + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="top" + android:hint="@string/optional_request_message" + android:imeOptions="actionDone" + android:inputType="textMultiLine" + android:maxLines="3" + android:minLines="3" + android:padding="12dp" + tools:text="sddsdsdsddsdsdsddsdsdvsdsdsdsfdwefsegegddsfjsdkjnsadknksndkn dn dfmnsdfnkm + sdfnmnasd,fmn,sdfn,dmnf,dsn,mnd,fmn,mdfskdnlsdmlsddsdsdsddsdsdsd11" /> + + </com.google.android.material.textfield.TextInputLayout> <org.futo.circles.core.view.LoadingButton @@ -118,12 +178,13 @@ style="@style/AccentButtonStyle" android:layout_width="450dp" android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:text="@string/requested_to_join" + android:layout_marginTop="24dp" + android:text="@string/request_to_join" android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/tvMembersip" + app:layout_constraintTop_toBottomOf="@id/tilRequestMessage" tools:visibility="visible" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_people_request.xml b/app/src/main/res/layout/list_item_people_request.xml index 3363d370269cefb33eb8e0254a700c2192367378..e6a90d02a3bfff3f6e3d7f74fb4437f04a439edd 100644 --- a/app/src/main/res/layout/list_item_people_request.xml +++ b/app/src/main/res/layout/list_item_people_request.xml @@ -13,7 +13,6 @@ android:layout_width="72dp" android:layout_height="0dp" android:scaleType="centerCrop" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="w,1:1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -26,10 +25,11 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" android:ellipsize="end" android:lines="1" app:layout_constraintBottom_toTopOf="@id/tvFollowText" - app:layout_constraintEnd_toStartOf="@id/btnAccept" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/ivUserImage" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed" @@ -44,23 +44,42 @@ android:lines="1" android:text="@string/wants_to_follow_you" android:textSize="13sp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/btnAccept" + app:layout_constraintEnd_toEndOf="@id/tvUserName" app:layout_constraintStart_toStartOf="@id/tvUserName" app:layout_constraintTop_toBottomOf="@id/tvUserName" /> + <TextView + android:id="@+id/tvReasonMessage" + style="@style/subheadline" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:ellipsize="end" + android:maxLines="3" + android:textSize="13sp" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="@id/tvUserName" + app:layout_constraintStart_toStartOf="@id/tvUserName" + app:layout_constraintTop_toBottomOf="@id/tvFollowText" + tools:text="Reason message messagemessagemessagemessagemessagemessagemessagemessagemessagemessagemessage" + tools:visibility="visible" /> + <com.google.android.material.button.MaterialButton android:id="@+id/btnAccept" style="@style/AccentButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="4dp" android:layout_marginEnd="8dp" android:padding="0dp" android:text="@string/accept" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/btnDecline" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintStart_toEndOf="@id/ivUserImage" + app:layout_constraintTop_toBottomOf="@id/tvReasonMessage" + + /> <com.google.android.material.button.MaterialButton android:id="@+id/btnDecline" @@ -72,6 +91,7 @@ android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintStart_toEndOf="@id/btnAccept" + app:layout_constraintTop_toTopOf="@id/btnAccept" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_nav_graph.xml b/app/src/main/res/navigation/settings_nav_graph.xml index 429c40be504edd3d1e8f8bb768994c5b719fdfff..d2c5326e308072f691612c09e4fc4a755d129e78 100644 --- a/app/src/main/res/navigation/settings_nav_graph.xml +++ b/app/src/main/res/navigation/settings_nav_graph.xml @@ -32,8 +32,8 @@ app:nullable="false" /> <argument - android:name="isProfile" - app:argType="boolean" + android:name="urlType" + app:argType="org.futo.circles.core.model.ShareUrlTypeArg" app:nullable="false" /> </action> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d0d68d4f01dbfdd37dd8c4f22349ce493d78014b..eade56ad373fabfec4110c8cf6297c5f078551d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,8 @@ <string name="log_out">Log out</string> <string name="others">Others</string> <string name="following_format">Following %d</string> + <string name="room_type_format">Room type: %s</string> + <string name="topic_format">Topic: %s</string> <string name="followed_by_format">Followed by %d</string> <string name="requests_format">Requests %d</string> <string name="create_post">Create post</string> @@ -73,7 +75,8 @@ <string name="add_reaction">Add reaction</string> <string name="unfollow">Unfollow</string> <string name="requested_to_follow_format">%s requested to follow</string> - <string name="requested_to_join">Requested to join</string> + <string name="request_to_join">Request to join</string> + <string name="request_to_follow">Request to follow</string> <string name="select_circles_in_which_you_want_to_follow_this_timeline">Select circles in which you want to follow this timeline</string> <string name="accept_invite">Accept invite</string> <string name="save_to_device">Save to device</string> @@ -211,6 +214,7 @@ <string name="group_topic_optional">Group topic (optional)</string> <string name="session">Session</string> <string name="help">Help</string> + <string name="optional_request_message">Optional: Request message</string> <string-array name="report_categories"> <item>@string/crude_language</item> diff --git a/core/src/main/java/org/futo/circles/core/base/Constants.kt b/core/src/main/java/org/futo/circles/core/base/Constants.kt index 0b6ca08402fbc4fabe9027f9efdffd7d296d534c..da27a0b25b74bce18534147cb8f1cc15062b7b72 100644 --- a/core/src/main/java/org/futo/circles/core/base/Constants.kt +++ b/core/src/main/java/org/futo/circles/core/base/Constants.kt @@ -4,13 +4,9 @@ import org.futo.circles.core.BuildConfig import org.futo.circles.core.provider.MatrixSessionProvider const val FILE_PROVIDER_AUTHORITY_EXTENSION = ".provider" - const val MediaCaptionFieldKey = "caption" - const val READ_ONLY_ROLE = -10 -fun getRageShakeUrl(): String = "https://rageshake.${getCirclesDomain()}/bugreports/submit/" - fun getCirclesDomain(): String { if (BuildConfig.DEBUG) return CirclesAppConfig.usServerDomain val homeServerUrl = MatrixSessionProvider.currentSession?.sessionParams?.homeServerUrl ?: "" @@ -18,14 +14,3 @@ fun getCirclesDomain(): String { if (homeServerUrl.contains(CirclesAppConfig.euServerDomain)) return CirclesAppConfig.euServerDomain return CirclesAppConfig.usServerDomain } - -const val SHARE_ROOM_URL_PREFIX = "https://circu.li/room/" -const val SHARE_PROFILE_URL_PREFIX = "https://circu.li/profile/" - -fun buildShareRoomUrl(roomId: String, roomName: String, topic: String?) = - SHARE_ROOM_URL_PREFIX + roomId + "/$roomName" + if (topic.isNullOrEmpty()) "" else "/$topic" - -fun buildShareProfileUrl(sharedSpaceId: String) = - MatrixSessionProvider.currentSession?.myUserId?.let { userId -> - "$SHARE_PROFILE_URL_PREFIX$userId/$sharedSpaceId" - } ?: "" diff --git a/core/src/main/java/org/futo/circles/core/di/ApiModule.kt b/core/src/main/java/org/futo/circles/core/di/ApiModule.kt index 5cc644c0769fe4fe01cbb29e8780ddc61b8cd9a8..ffeab1be25a910004336a9666910796459d2af39 100644 --- a/core/src/main/java/org/futo/circles/core/di/ApiModule.kt +++ b/core/src/main/java/org/futo/circles/core/di/ApiModule.kt @@ -7,7 +7,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient -import org.futo.circles.core.base.getRageShakeUrl +import org.futo.circles.core.feature.rageshake.getRageShakeUrl import org.futo.circles.core.feature.rageshake.io.BugreportApiService import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory diff --git a/core/src/main/java/org/futo/circles/core/feature/rageshake/BugReportDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/rageshake/BugReportDataSource.kt index 5bc5ae5693fedeb647f0c3bdf77ee9e4cfd924f9..40ffa1726af3aaff45facc16b436f140c8a301ab 100644 --- a/core/src/main/java/org/futo/circles/core/feature/rageshake/BugReportDataSource.kt +++ b/core/src/main/java/org/futo/circles/core/feature/rageshake/BugReportDataSource.kt @@ -3,7 +3,6 @@ package org.futo.circles.core.feature.rageshake import okhttp3.ResponseBody import org.futo.circles.core.extensions.Response import org.futo.circles.core.extensions.createResult -import org.futo.circles.core.base.getRageShakeUrl import org.futo.circles.core.feature.rageshake.io.BugreportApiService import javax.inject.Inject diff --git a/core/src/main/java/org/futo/circles/core/feature/rageshake/RageshakeUrlHelper.kt b/core/src/main/java/org/futo/circles/core/feature/rageshake/RageshakeUrlHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..abf3889b7673477e675876db3b91fe99d56f842c --- /dev/null +++ b/core/src/main/java/org/futo/circles/core/feature/rageshake/RageshakeUrlHelper.kt @@ -0,0 +1,5 @@ +package org.futo.circles.core.feature.rageshake + +import org.futo.circles.core.base.getCirclesDomain + +fun getRageShakeUrl(): String = "https://rageshake.${getCirclesDomain()}/bugreports/submit/" diff --git a/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestViewModel.kt b/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestViewModel.kt index 7c14675f9e2921d02774742eea9e033377bef87b..9887aca60dac672351f1ab2fff6e7aae654c72a3 100644 --- a/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestViewModel.kt +++ b/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestViewModel.kt @@ -15,7 +15,7 @@ import javax.inject.Inject class KnockRequestViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val inviteRequestsDataSource: InviteRequestsDataSource, - private val knockRequestsDataSource: KnockRequestsDataSource + knockRequestsDataSource: KnockRequestsDataSource ) : ViewModel() { private val roomId: String = savedStateHandle.getOrThrow("roomId") diff --git a/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestsDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestsDataSource.kt index 7aeeb57058648c93c70076ed8b7d538455c2e1ea..22148cac321a8b898491f1907c1f282d60fb3459 100644 --- a/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestsDataSource.kt +++ b/core/src/main/java/org/futo/circles/core/feature/room/knoks/KnockRequestsDataSource.kt @@ -4,16 +4,20 @@ import androidx.lifecycle.map import org.futo.circles.core.model.KnockRequestListItem import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.core.utils.UserUtils +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.EventType.STATE_ROOM_MEMBER +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import javax.inject.Inject class KnockRequestsDataSource @Inject constructor() { fun getKnockRequestsListItemsLiveData(roomId: String) = getKnockRequestLiveData(roomId)?.map { - it.map { user -> user.toKnockRequestListItem() } + it.map { user -> user.toKnockRequestListItem(roomId) } } fun getKnockRequestCountLiveData(roomId: String) = @@ -28,9 +32,17 @@ class KnockRequestsDataSource @Inject constructor() { } ) - private fun RoomMemberSummary.toKnockRequestListItem() = KnockRequestListItem( + private fun getReasonMessage(roomId: String, userId: String) = + MatrixSessionProvider.currentSession?.getRoom(roomId)?.stateService()?.getStateEvents( + setOf(STATE_ROOM_MEMBER), QueryStringValue.Contains(userId) + )?.firstOrNull { + it.content.toModel<RoomMemberContent>()?.membership == Membership.KNOCK + }?.content.toModel<RoomMemberContent>()?.safeReason + + private fun RoomMemberSummary.toKnockRequestListItem(roomId: String) = KnockRequestListItem( requesterId = userId, requesterName = displayName ?: UserUtils.removeDomainSuffix(userId), - requesterAvatarUrl = avatarUrl + requesterAvatarUrl = avatarUrl, + message = getReasonMessage(roomId, userId) ) } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/feature/room/knoks/list/KnockRequestViewHolder.kt b/core/src/main/java/org/futo/circles/core/feature/room/knoks/list/KnockRequestViewHolder.kt index 5ceaa0242b2cc415c560ec77fa4fa69e1ff6daeb..c0634a69700050b11e5cd67d8d19e84bbf8cda08 100644 --- a/core/src/main/java/org/futo/circles/core/feature/room/knoks/list/KnockRequestViewHolder.kt +++ b/core/src/main/java/org/futo/circles/core/feature/room/knoks/list/KnockRequestViewHolder.kt @@ -5,6 +5,7 @@ import androidx.recyclerview.widget.RecyclerView import org.futo.circles.core.base.list.ViewBindingHolder import org.futo.circles.core.databinding.ListItemKnockRequestBinding import org.futo.circles.core.extensions.onClick +import org.futo.circles.core.extensions.setIsVisible import org.futo.circles.core.model.KnockRequestListItem import org.futo.circles.core.model.toCircleUser @@ -24,6 +25,10 @@ class KnockRequestViewHolder( } fun bind(data: KnockRequestListItem) { + binding.tvReason.apply { + setIsVisible(!data.message.isNullOrBlank()) + text = data.message + } binding.vUserLayout.bind(data.toCircleUser()) } } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomDialogFragment.kt b/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomDialogFragment.kt index bd030f00891cb81ac23ff67242c8a2b7b2b99e53..512bdfea8cb306bc0883687562d1a688b68f4f8d 100644 --- a/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomDialogFragment.kt +++ b/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomDialogFragment.kt @@ -6,16 +6,17 @@ import androidx.fragment.app.viewModels import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint import org.futo.circles.core.R +import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment import org.futo.circles.core.databinding.DialogFragmentShareRoomBinding import org.futo.circles.core.extensions.gone import org.futo.circles.core.extensions.observeData import org.futo.circles.core.extensions.showError import org.futo.circles.core.extensions.visible -import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment +import org.futo.circles.core.feature.share.ShareProvider import org.futo.circles.core.mapping.nameOrId +import org.futo.circles.core.model.ShareUrlTypeArg import org.futo.circles.core.model.TextShareable import org.futo.circles.core.provider.MatrixSessionProvider -import org.futo.circles.core.feature.share.ShareProvider import org.matrix.android.sdk.api.session.room.model.RoomSummary @AndroidEntryPoint @@ -37,7 +38,7 @@ class ShareRoomDialogFragment : private fun setupViews() { binding.toolbar.title = - getString(if (args.isProfile) R.string.share_profile else R.string.share_room) + getString(if (isProfile()) R.string.share_profile else R.string.share_room) binding.btnShare.setOnClickListener { ShareProvider.share(requireContext(), TextShareable(viewModel.buildInviteUrl())) } @@ -59,11 +60,13 @@ class ShareRoomDialogFragment : vLoading.gone() ivQr.visible() ivQr.setData(viewModel.buildInviteUrl()) - tvRoomName.text = if (args.isProfile) + tvRoomName.text = if (isProfile()) MatrixSessionProvider.currentSession?.myUserId ?: roomSummary.nameOrId() else roomSummary.nameOrId() tvRoomId.text = roomSummary.roomId btnShare.visible() } } + + private fun isProfile() = args.urlType == ShareUrlTypeArg.PROFILE } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomViewModel.kt b/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomViewModel.kt index d374bc922b6dcf0b6bc004d6fb3bb16acac48efa..b4a1fa5bfd80eba8f5fb0238a09c8a0dae2046ba 100644 --- a/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomViewModel.kt +++ b/core/src/main/java/org/futo/circles/core/feature/room/share/ShareRoomViewModel.kt @@ -3,12 +3,10 @@ package org.futo.circles.core.feature.room.share import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import org.futo.circles.core.base.buildShareProfileUrl -import org.futo.circles.core.base.buildShareRoomUrl import org.futo.circles.core.extensions.getOrThrow -import org.futo.circles.core.mapping.nameOrId +import org.futo.circles.core.feature.share.buildShareRoomUrl +import org.futo.circles.core.model.ShareUrlTypeArg import org.futo.circles.core.provider.MatrixSessionProvider -import org.matrix.android.sdk.api.session.getRoom import javax.inject.Inject @HiltViewModel @@ -17,16 +15,10 @@ class ShareRoomViewModel @Inject constructor( ) : ViewModel() { private val roomId: String = savedStateHandle.getOrThrow("roomId") - private val isProfile: Boolean = savedStateHandle.getOrThrow("isProfile") + private val shareUrlType: ShareUrlTypeArg = savedStateHandle.getOrThrow("urlType") val roomLiveData = MatrixSessionProvider.currentSession?.roomService()?.getRoomSummaryLive(roomId) - fun buildInviteUrl(): String = - if (isProfile) buildShareProfileUrl(roomId) - else { - val summary = - MatrixSessionProvider.currentSession?.getRoom(roomId)?.roomSummary() - summary?.let { buildShareRoomUrl(roomId, summary.nameOrId(), summary.topic) } ?: "" - } + fun buildInviteUrl(): String = buildShareRoomUrl(shareUrlType, roomId) } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/feature/share/ShareUrl.kt b/core/src/main/java/org/futo/circles/core/feature/share/ShareUrl.kt new file mode 100644 index 0000000000000000000000000000000000000000..16ca4cefc0ad27f22ad0dd1f9a82a56f8e59d594 --- /dev/null +++ b/core/src/main/java/org/futo/circles/core/feature/share/ShareUrl.kt @@ -0,0 +1,8 @@ +package org.futo.circles.core.feature.share + +import org.futo.circles.core.model.ShareUrlTypeArg + +const val BASE_SHARE_URL = "https://circu.li/" + +fun buildShareRoomUrl(type: ShareUrlTypeArg, roomId: String) = + BASE_SHARE_URL + type.typeKey + "/$roomId" diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsDialogFragment.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsDialogFragment.kt index fdb34387bd8c1b41abc9894960c269f2261b334b..8db7e96147847203b80eeaafc60d4d67e5aafdd7 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsDialogFragment.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsDialogFragment.kt @@ -114,7 +114,7 @@ class TimelineOptionsDialogFragment : ) setOnClickListener { showLeaveRoomDialog() } } - tvShare.setOnClickListener { navigator.navigateToShareRoom(timelineId) } + tvShare.setOnClickListener { navigator.navigateToShareRoom(timelineId, args.type) } tvManageMembers.apply { setIsVisible(args.type != CircleRoomTypeArg.Circle) setOnClickListener { navigator.navigateToManageMembers(timelineId, args.type) } diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsNavigator.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsNavigator.kt index 7592406723925c524e57b33a341005d3f24927e2..df8f90896dfa57f0ab4f54fe8acd9b3d8fcf408b 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsNavigator.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/options/TimelineOptionsNavigator.kt @@ -3,6 +3,7 @@ package org.futo.circles.core.feature.timeline.options import androidx.navigation.fragment.findNavController import org.futo.circles.core.extensions.navigateSafe import org.futo.circles.core.model.CircleRoomTypeArg +import org.futo.circles.core.model.toShareUrlType class TimelineOptionsNavigator(private val fragment: TimelineOptionsDialogFragment) { @@ -49,9 +50,13 @@ class TimelineOptionsNavigator(private val fragment: TimelineOptionsDialogFragme } - fun navigateToShareRoom(roomId: String) { + fun navigateToShareRoom(roomId: String, type: CircleRoomTypeArg) { fragment.findNavController() - .navigateSafe(TimelineOptionsDialogFragmentDirections.toShareRoom(roomId, false)) + .navigateSafe( + TimelineOptionsDialogFragmentDirections.toShareRoom( + roomId, type.toShareUrlType() + ) + ) } fun navigateToKnockRequests(timelineId: String) { diff --git a/core/src/main/java/org/futo/circles/core/model/CircleRoomTypeArg.kt b/core/src/main/java/org/futo/circles/core/model/CircleRoomTypeArg.kt index 53769c8aaeb99ffe3feaf185292609da8c905862..ee94c2cd42a961c66ec675a39988e4b25d7dbbbd 100644 --- a/core/src/main/java/org/futo/circles/core/model/CircleRoomTypeArg.kt +++ b/core/src/main/java/org/futo/circles/core/model/CircleRoomTypeArg.kt @@ -1,3 +1,9 @@ package org.futo.circles.core.model -enum class CircleRoomTypeArg { Circle, Group, Photo } \ No newline at end of file +enum class CircleRoomTypeArg { Circle, Group, Photo } + +fun CircleRoomTypeArg.toShareUrlType() = when (this) { + CircleRoomTypeArg.Circle -> ShareUrlTypeArg.TIMELINE + CircleRoomTypeArg.Group -> ShareUrlTypeArg.GROUP + CircleRoomTypeArg.Photo -> ShareUrlTypeArg.GALLERY +} \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/model/KnockRequestListItem.kt b/core/src/main/java/org/futo/circles/core/model/KnockRequestListItem.kt index 54e49e8df4f9824c4663f60fcf784d51b803f1d8..4b7becdc89e219ce92754701ce590cb5b6b570b6 100644 --- a/core/src/main/java/org/futo/circles/core/model/KnockRequestListItem.kt +++ b/core/src/main/java/org/futo/circles/core/model/KnockRequestListItem.kt @@ -5,7 +5,8 @@ import org.futo.circles.core.base.list.IdEntity data class KnockRequestListItem( val requesterId: String, val requesterName: String, - val requesterAvatarUrl: String? + val requesterAvatarUrl: String?, + val message: String? ) : IdEntity<String> { override val id: String = requesterId } diff --git a/core/src/main/java/org/futo/circles/core/model/ShareUrlTypeArg.kt b/core/src/main/java/org/futo/circles/core/model/ShareUrlTypeArg.kt new file mode 100644 index 0000000000000000000000000000000000000000..9cc340fd5895270782cc8da35148a9c545aa9438 --- /dev/null +++ b/core/src/main/java/org/futo/circles/core/model/ShareUrlTypeArg.kt @@ -0,0 +1,15 @@ +package org.futo.circles.core.model + +enum class ShareUrlTypeArg(val typeKey: String) { + ROOM("room"), + PROFILE("profile"), + GALLERY("galley"), + GROUP("group"), + TIMELINE("timeline") +} + +fun shareUrlTypeArgFromType(type: String): ShareUrlTypeArg? { + val urlType: ShareUrlTypeArg? = null + ShareUrlTypeArg.values().forEach { if (type == it.typeKey) return it } + return urlType +} \ No newline at end of file diff --git a/core/src/main/res/layout/list_item_knock_request.xml b/core/src/main/res/layout/list_item_knock_request.xml index 82644229cbc595a4014143629e103e587cec4e75..60906170fa64e93c211a73cb0ca0ab50f4a8230b 100644 --- a/core/src/main/res/layout/list_item_knock_request.xml +++ b/core/src/main/res/layout/list_item_knock_request.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -14,6 +15,23 @@ app:layout_constraintTop_toTopOf="parent" /> + <TextView + android:id="@+id/tvReason" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginTop="8dp" + android:ellipsize="end" + android:maxLines="3" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/vUserLayout" + tools:text="MessageMessageMessageMessageMessageMessage MessageMessageMessageMessageMessageMessage +MessageMessageMessageMessageMessageMessageMessageMessageMessageMessageMessage" + tools:visibility="visible" /> + + <com.google.android.material.button.MaterialButton android:id="@+id/btnInvite" style="@style/AccentButtonStyle" @@ -27,7 +45,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/btnDecline" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/vUserLayout" /> + app:layout_constraintTop_toBottomOf="@id/tvReason" /> <com.google.android.material.button.MaterialButton android:id="@+id/btnDecline" diff --git a/core/src/main/res/navigation/share_room_nav_graph.xml b/core/src/main/res/navigation/share_room_nav_graph.xml index 0b0ef7d7adf11496b5f1f82f72437396319b1147..c52078b379b31f064256fb068842f61d482bf429 100644 --- a/core/src/main/res/navigation/share_room_nav_graph.xml +++ b/core/src/main/res/navigation/share_room_nav_graph.xml @@ -15,8 +15,8 @@ app:nullable="false" /> <argument - android:name="isProfile" - app:argType="boolean" + android:name="urlType" + app:argType="org.futo.circles.core.model.ShareUrlTypeArg" app:nullable="false" /> </dialog> diff --git a/core/src/main/res/navigation/timeline_options_nav_graph.xml b/core/src/main/res/navigation/timeline_options_nav_graph.xml index 2e9e27d3cccc0bef6e4fd84d7cbfc9703e1de9b3..5a6d480a35730d3d1cbc624abfe6d65552caf7ad 100644 --- a/core/src/main/res/navigation/timeline_options_nav_graph.xml +++ b/core/src/main/res/navigation/timeline_options_nav_graph.xml @@ -29,8 +29,8 @@ app:nullable="false" /> <argument - android:name="isProfile" - app:argType="boolean" + android:name="urlType" + app:argType="org.futo.circles.core.model.ShareUrlTypeArg" app:nullable="false" /> </action>