Skip to content
Snippets Groups Projects
Verified Commit 0c0cc852 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement common channels UI in user info page

parent 72818ec2
No related branches found
No related tags found
No related merge requests found
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.info.user
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.BufferInfo
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.chat.ChatActivity
import de.kuschku.quasseldroid.util.helper.visibleIf
import de.kuschku.quasseldroid.util.lists.ListAdapter
import de.kuschku.quasseldroid.viewmodel.data.BufferProps
class ChannelAdapter : ListAdapter<BufferProps, ChannelAdapter.ChannelViewHolder>(
object : DiffUtil.ItemCallback<BufferProps>() {
override fun areItemsTheSame(oldItem: BufferProps, newItem: BufferProps) =
oldItem.info.bufferId == newItem.info.bufferId
override fun areContentsTheSame(oldItem: BufferProps, newItem: BufferProps) =
oldItem == newItem
}
) {
private var clickListener: ((NetworkId, String) -> Unit)? = null
fun setOnClickListener(listener: ((NetworkId, String) -> Unit)?) {
this.clickListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ChannelViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.widget_buffer, parent, false
),
clickListener = clickListener
)
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) =
holder.bind(getItem(position))
class ChannelViewHolder(
itemView: View,
private val clickListener: ((NetworkId, String) -> Unit)? = null
) : RecyclerView.ViewHolder(itemView) {
@BindView(R.id.status)
lateinit var status: ImageView
@BindView(R.id.name)
lateinit var name: TextView
@BindView(R.id.description)
lateinit var description: TextView
var info: BufferInfo? = null
init {
ButterKnife.bind(this, itemView)
itemView.setOnClickListener {
info?.let {
ChatActivity.launch(
itemView.context,
networkId = it.networkId,
channel = it.bufferName
)
}
}
}
fun bind(props: BufferProps) {
info = props.info
name.text = props.info.bufferName
description.text = props.description
description.visibleIf(props.description.isNotBlank())
status.setImageDrawable(props.fallbackDrawable)
}
}
}
...@@ -23,6 +23,7 @@ import de.kuschku.libquassel.protocol.NetworkId ...@@ -23,6 +23,7 @@ import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.BufferInfo
import de.kuschku.libquassel.quassel.syncables.IrcUser import de.kuschku.libquassel.quassel.syncables.IrcUser
import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.quassel.syncables.Network
import de.kuschku.quasseldroid.viewmodel.data.BufferProps
data class IrcUserInfo( data class IrcUserInfo(
val networkId: NetworkId, val networkId: NetworkId,
...@@ -37,5 +38,6 @@ data class IrcUserInfo( ...@@ -37,5 +38,6 @@ data class IrcUserInfo(
val network: Network? = null, val network: Network? = null,
val knownToCore: Boolean = false, val knownToCore: Boolean = false,
val info: BufferInfo? = null, val info: BufferInfo? = null,
val ircUser: IrcUser? = null val ircUser: IrcUser? = null,
val channels: List<BufferProps> = emptyList()
) )
...@@ -31,12 +31,18 @@ import android.widget.Button ...@@ -31,12 +31,18 @@ import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.BufferId
import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.Buffer_Type
import de.kuschku.libquassel.protocol.Message_Type
import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.BufferInfo
import de.kuschku.libquassel.quassel.syncables.BufferSyncer
import de.kuschku.libquassel.quassel.syncables.IrcChannel
import de.kuschku.libquassel.quassel.syncables.IrcUser import de.kuschku.libquassel.quassel.syncables.IrcUser
import de.kuschku.libquassel.util.Optional import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.helpers.nullIf import de.kuschku.libquassel.util.helpers.nullIf
...@@ -46,6 +52,7 @@ import de.kuschku.quasseldroid.R ...@@ -46,6 +52,7 @@ import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.ui.chat.ChatActivity
import de.kuschku.quasseldroid.ui.coresettings.ignorelist.IgnoreListActivity import de.kuschku.quasseldroid.ui.coresettings.ignorelist.IgnoreListActivity
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.ShortcutCreationHelper import de.kuschku.quasseldroid.util.ShortcutCreationHelper
import de.kuschku.quasseldroid.util.avatars.AvatarHelper import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.avatars.MatrixApi import de.kuschku.quasseldroid.util.avatars.MatrixApi
...@@ -57,6 +64,9 @@ import de.kuschku.quasseldroid.util.service.ServiceBoundFragment ...@@ -57,6 +64,9 @@ import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod
import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper
import de.kuschku.quasseldroid.viewmodel.data.Avatar import de.kuschku.quasseldroid.viewmodel.data.Avatar
import de.kuschku.quasseldroid.viewmodel.data.BufferHiddenState
import de.kuschku.quasseldroid.viewmodel.data.BufferProps
import de.kuschku.quasseldroid.viewmodel.data.BufferStatus
import io.reactivex.Observable import io.reactivex.Observable
import javax.inject.Inject import javax.inject.Inject
...@@ -115,6 +125,9 @@ class UserInfoFragment : ServiceBoundFragment() { ...@@ -115,6 +125,9 @@ class UserInfoFragment : ServiceBoundFragment() {
@BindView(R.id.server) @BindView(R.id.server)
lateinit var server: TextView lateinit var server: TextView
@BindView(R.id.common_channels)
lateinit var commonChannels: RecyclerView
@Inject @Inject
lateinit var contentFormatter: ContentFormatter lateinit var contentFormatter: ContentFormatter
...@@ -144,18 +157,66 @@ class UserInfoFragment : ServiceBoundFragment() { ...@@ -144,18 +157,66 @@ class UserInfoFragment : ServiceBoundFragment() {
actionShortcut.visibleIf(currentBufferInfo != null) actionShortcut.visibleIf(currentBufferInfo != null)
} }
val commonChannelsAdapter = ChannelAdapter()
commonChannels.layoutManager = LinearLayoutManager(context)
commonChannels.itemAnimator = DefaultItemAnimator()
commonChannels.adapter = commonChannelsAdapter
val colorContext = ColorContext(requireContext(), messageSettings)
val colorAccent = requireContext().theme.styledAttributes(R.attr.colorAccent) {
getColor(0, 0)
}
val colorAway = requireContext().theme.styledAttributes(R.attr.colorAway) {
getColor(0, 0)
}
combineLatest(viewModel.session, viewModel.networks).switchMap { (sessionOptional, networks) -> combineLatest(viewModel.session, viewModel.networks).switchMap { (sessionOptional, networks) ->
fun processUser(user: IrcUser, info: BufferInfo? = null): Optional<IrcUserInfo> { fun processUser(user: IrcUser, bufferSyncer: BufferSyncer? = null,
info: BufferInfo? = null): Observable<Optional<IrcUserInfo>> {
actionShortcut.post(::updateShortcutVisibility) actionShortcut.post(::updateShortcutVisibility)
return when { return when {
user == IrcUser.NULL && info != null -> Optional.of(IrcUserInfo( user == IrcUser.NULL && info != null -> Observable.just(Optional.of(IrcUserInfo(
networkId = info.networkId, networkId = info.networkId,
nick = info.bufferName ?: "", nick = info.bufferName ?: "",
knownToCore = true, knownToCore = true,
info = info info = info
)) )))
user == IrcUser.NULL -> Optional.empty() user == IrcUser.NULL -> Observable.just(Optional.empty())
else -> Optional.of(IrcUserInfo( else -> {
combineLatest(user.channels().map { channelName ->
user.network().liveIrcChannel(
channelName
).switchMap { channel ->
channel.updates().map {
bufferSyncer?.find(
bufferName = channelName,
networkId = user.network().networkId()
)?.let { info ->
val bufferStatus =
if (it == IrcChannel.NULL) BufferStatus.OFFLINE
else BufferStatus.ONLINE
val color =
if (bufferStatus == BufferStatus.ONLINE) colorAccent
else colorAway
val fallbackDrawable = colorContext.buildTextDrawable("#", color)
BufferProps(
info = info,
network = user.network().networkInfo(),
description = it.topic(),
activity = Message_Type.of(),
bufferStatus = bufferStatus,
hiddenState = BufferHiddenState.VISIBLE,
networkConnectionState = user.network().connectionState(),
fallbackDrawable = fallbackDrawable
)
}
}
}
}).map {
Optional.of(IrcUserInfo(
networkId = user.network().networkId(), networkId = user.network().networkId(),
nick = user.nick(), nick = user.nick(),
user = user.user(), user = user.user(),
...@@ -168,26 +229,29 @@ class UserInfoFragment : ServiceBoundFragment() { ...@@ -168,26 +229,29 @@ class UserInfoFragment : ServiceBoundFragment() {
network = user.network(), network = user.network(),
knownToCore = true, knownToCore = true,
info = info, info = info,
ircUser = user ircUser = user,
channels = it.filterNotNull()
)) ))
} }
} }
}
}
if (openBuffer == true) { if (openBuffer == true) {
val session = sessionOptional?.orNull() val session = sessionOptional?.orNull()
val bufferSyncer = session?.bufferSyncer val bufferSyncer = session?.bufferSyncer
val bufferInfo = bufferSyncer?.bufferInfo(bufferId) val bufferInfo = bufferSyncer?.bufferInfo(bufferId)
bufferInfo?.let { bufferInfo?.let {
networks[it.networkId]?.liveIrcUser(it.bufferName)?.switchMap(IrcUser::updates)?.map { networks[it.networkId]?.liveIrcUser(it.bufferName)?.switchMap(IrcUser::updates)?.switchMap {
processUser(it, bufferInfo) processUser(it, bufferSyncer, bufferInfo)
} }
} }
} else { } else {
networks[networkId] networks[networkId]
?.liveIrcUser(nickName) ?.liveIrcUser(nickName)
?.switchMap(IrcUser::updates) ?.switchMap(IrcUser::updates)
?.map { user -> processUser(user) } ?.switchMap { user -> processUser(user, sessionOptional?.orNull()?.bufferSyncer) }
} ?: Observable.just(IrcUser.NULL).map { user -> processUser(user) } } ?: Observable.just(IrcUser.NULL).switchMap { user -> processUser(user, null, null) }
}.toLiveData().observe(this, Observer { }.toLiveData().observe(this, Observer {
val user = it.orNull() val user = it.orNull()
if (user != null) { if (user != null) {
...@@ -322,6 +386,8 @@ class UserInfoFragment : ServiceBoundFragment() { ...@@ -322,6 +386,8 @@ class UserInfoFragment : ServiceBoundFragment() {
} }
} }
} }
commonChannelsAdapter.submitList(user.channels)
} }
}) })
......
...@@ -251,6 +251,15 @@ ...@@ -251,6 +251,15 @@
style="@style/Widget.Info.Item.Description" style="@style/Widget.Info.Item.Description"
android:text="@string/label_user_server" /> android:text="@string/label_user_server" />
</LinearLayout> </LinearLayout>
<TextView
style="@style/Widget.Info.Section"
android:text="@string/label_user_common_channels" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/common_channels"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<string name="label_user_ident">Ident</string> <string name="label_user_ident">Ident</string>
<string name="label_user_host">Host</string> <string name="label_user_host">Host</string>
<string name="label_user_server">Server</string> <string name="label_user_server">Server</string>
<string name="label_user_common_channels">Common Channels</string>
<string name="label_core_version">Version</string> <string name="label_core_version">Version</string>
<string name="label_core_uptime">Uptime</string> <string name="label_core_uptime">Uptime</string>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment