Skip to content
Snippets Groups Projects
Commit 585766c3 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Minor bugfixes

parent 34335962
No related branches found
No related tags found
No related merge requests found
Showing
with 198 additions and 63 deletions
...@@ -26,9 +26,7 @@ import de.kuschku.quasseldroid.util.helper.editApply ...@@ -26,9 +26,7 @@ import de.kuschku.quasseldroid.util.helper.editApply
import de.kuschku.quasseldroid.util.helper.editCommit import de.kuschku.quasseldroid.util.helper.editCommit
import de.kuschku.quasseldroid.util.helper.sharedPreferences import de.kuschku.quasseldroid.util.helper.sharedPreferences
import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.helper.toLiveData
import io.reactivex.Observable import io.reactivex.subjects.PublishSubject
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.BehaviorSubject
import org.threeten.bp.Instant import org.threeten.bp.Instant
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
...@@ -220,7 +218,7 @@ class QuasselService : DaggerLifecycleService(), ...@@ -220,7 +218,7 @@ class QuasselService : DaggerLifecycleService(),
} }
} }
} }
private val connectivity = BehaviorSubject.createDefault(Unit) private val connectivity = PublishSubject.create<Unit>()
private fun disconnectFromCore() { private fun disconnectFromCore() {
getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editCommit { getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editCommit {
...@@ -264,18 +262,27 @@ class QuasselService : DaggerLifecycleService(), ...@@ -264,18 +262,27 @@ class QuasselService : DaggerLifecycleService(),
} }
}) })
Observable.combineLatest( var wasEverConnected = false
sessionManager.state.filter { it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED }, connectivity
connectivity, .delay(200, TimeUnit.MILLISECONDS)
BiFunction { a: ConnectionState, _: Unit -> a }) .throttleFirst(1, TimeUnit.SECONDS)
.toLiveData()
.observe(this, Observer {
if (wasEverConnected) sessionManager.reconnect(true)
})
sessionManager.state
.distinctUntilChanged() .distinctUntilChanged()
.delay(200, TimeUnit.MILLISECONDS) .delay(200, TimeUnit.MILLISECONDS)
.throttleFirst(1, TimeUnit.SECONDS) .throttleFirst(1, TimeUnit.SECONDS)
.toLiveData() .toLiveData()
.observe( .observe(
this, Observer { this, Observer {
if (it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED) if (it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED) {
sessionManager.reconnect(true) if (wasEverConnected) sessionManager.reconnect()
} else {
wasEverConnected = true
}
}) })
sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
......
...@@ -388,6 +388,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc ...@@ -388,6 +388,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
val selected = dialog.selectedIndices ?: emptyArray() val selected = dialog.selectedIndices ?: emptyArray()
runInBackground { runInBackground {
val newlyFiltered = selected val newlyFiltered = selected
.asSequence()
.map { flags[it] } .map { flags[it] }
.fold(Message_Type.of()) { acc, i -> acc or i } .fold(Message_Type.of()) { acc, i -> acc or i }
......
...@@ -210,28 +210,24 @@ class BufferViewConfigFragment : ServiceBoundFragment() { ...@@ -210,28 +210,24 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
val (config, list) = info ?: Pair(null, emptyList()) val (config, list) = info ?: Pair(null, emptyList())
val minimumActivity = config?.minimumActivity() ?: Buffer_Activity.NONE val minimumActivity = config?.minimumActivity() ?: Buffer_Activity.NONE
val activities = activityList.associate { it.bufferId to it.filtered } val activities = activityList.associate { it.bufferId to it.filtered }
listAdapter.submitList(list.sortedBy { props -> activity?.runOnUiThread {
listAdapter.submitList(list.asSequence().sortedBy { props ->
!props.info.type.hasFlag(Buffer_Type.StatusBuffer) !props.info.type.hasFlag(Buffer_Type.StatusBuffer)
}.sortedBy { props -> }.sortedBy { props ->
props.network.networkName props.network.networkName
}.map { props -> }.map { props ->
val activity = props.activity - (activities[props.info.bufferId] ?: 0)
BufferListItem( BufferListItem(
props, props.copy(
BufferState(
networkExpanded = !collapsedNetworks.contains(props.network.networkId),
selected = selected.info?.bufferId == props.info.bufferId
)
)
}.filter { (props, state) ->
props.info.type.hasFlag(BufferInfo.Type.StatusBuffer) || state.networkExpanded
}.map {
val activity = it.props.activity - (activities[it.props.info.bufferId] ?: 0)
it.copy(
props = it.props.copy(
activity = activity, activity = activity,
description = ircFormatDeserializer.formatString(
requireContext(),
props.description.toString(),
colorize = messageSettings.colorizeMirc
),
bufferActivity = Buffer_Activity.of( bufferActivity = Buffer_Activity.of(
when { when {
it.props.highlights > 0 -> Buffer_Activity.Highlight props.highlights > 0 -> Buffer_Activity.Highlight
activity.hasFlag(Message_Type.Plain) || activity.hasFlag(Message_Type.Plain) ||
activity.hasFlag(Message_Type.Notice) || activity.hasFlag(Message_Type.Notice) ||
activity.hasFlag(Message_Type.Action) -> Buffer_Activity.NewMessage activity.hasFlag(Message_Type.Action) -> Buffer_Activity.NewMessage
...@@ -239,12 +235,19 @@ class BufferViewConfigFragment : ServiceBoundFragment() { ...@@ -239,12 +235,19 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
else -> Buffer_Activity.NoActivity else -> Buffer_Activity.NoActivity
} }
) )
),
BufferState(
networkExpanded = !collapsedNetworks.contains(props.network.networkId),
selected = selected.info?.bufferId == props.info.bufferId
) )
) )
}.filter { (props, state) ->
props.info.type.hasFlag(BufferInfo.Type.StatusBuffer) || state.networkExpanded
}.filter { }.filter {
minimumActivity.toInt() <= it.props.bufferActivity.toInt() || minimumActivity.toInt() <= it.props.bufferActivity.toInt() ||
it.props.info.type.hasFlag(Buffer_Type.StatusBuffer) it.props.info.type.hasFlag(Buffer_Type.StatusBuffer)
}) }.toList())
}
} }
} }
}) })
......
...@@ -120,6 +120,7 @@ class AutoCompleteHelper( ...@@ -120,6 +120,7 @@ class AutoCompleteHelper(
)?.let { ircChannel -> )?.let { ircChannel ->
val users = ircChannel.ircUsers() val users = ircChannel.ircUsers()
val buffers = infos val buffers = infos
.asSequence()
.filter { .filter {
it.type.toInt() == Buffer_Type.ChannelBuffer.toInt() it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
}.mapNotNull { info -> }.mapNotNull { info ->
...@@ -136,7 +137,7 @@ class AutoCompleteHelper( ...@@ -136,7 +137,7 @@ class AutoCompleteHelper(
description = channel.topic() description = channel.topic()
) )
} }
val nicks = users.map { user -> val nicks = users.asSequence().map { user ->
val userModes = ircChannel.userModes(user) val userModes = ircChannel.userModes(user)
val prefixModes = network.prefixModes() val prefixModes = network.prefixModes()
...@@ -160,7 +161,7 @@ class AutoCompleteHelper( ...@@ -160,7 +161,7 @@ class AutoCompleteHelper(
lastWord.first.trimStart(*IGNORED_CHARS), lastWord.first.trimStart(*IGNORED_CHARS),
ignoreCase = true ignoreCase = true
) )
}.sorted() }.sorted().toList()
} }
} else null } else null
} }
......
...@@ -85,7 +85,7 @@ class MessageListFragment : ServiceBoundFragment() { ...@@ -85,7 +85,7 @@ class MessageListFragment : ServiceBoundFragment() {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?) = when (item?.itemId) { override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?) = when (item?.itemId) {
R.id.action_copy -> { R.id.action_copy -> {
val builder = SpannableStringBuilder() val builder = SpannableStringBuilder()
viewModel.selectedMessages.value.values.sortedBy { viewModel.selectedMessages.value.values.asSequence().sortedBy {
it.id it.id
}.map { }.map {
if (it.name != null && it.content != null) { if (it.name != null && it.content != null) {
...@@ -114,7 +114,7 @@ class MessageListFragment : ServiceBoundFragment() { ...@@ -114,7 +114,7 @@ class MessageListFragment : ServiceBoundFragment() {
} }
R.id.action_share -> { R.id.action_share -> {
val builder = SpannableStringBuilder() val builder = SpannableStringBuilder()
viewModel.selectedMessages.value.values.sortedBy { viewModel.selectedMessages.value.values.asSequence().sortedBy {
it.id it.id
}.map { }.map {
if (it.name != null && it.content != null) { if (it.name != null && it.content != null) {
......
...@@ -77,7 +77,7 @@ class NickListFragment : ServiceBoundFragment() { ...@@ -77,7 +77,7 @@ class NickListFragment : ServiceBoundFragment() {
val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size) val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size)
viewModel.nickData.toLiveData().observe(this, Observer { viewModel.nickData.toLiveData().observe(this, Observer {
runInBackground { runInBackground {
it?.map { it?.asSequence()?.map {
val nickName = it.nick val nickName = it.nick
val senderColorIndex = IrcUserUtils.senderColor(nickName) val senderColorIndex = IrcUserUtils.senderColor(nickName)
val rawInitial = nickName.trimStart('-', val rawInitial = nickName.trimStart('-',
...@@ -129,7 +129,11 @@ class NickListFragment : ServiceBoundFragment() { ...@@ -129,7 +129,11 @@ class NickListFragment : ServiceBoundFragment() {
.trimStart(*IGNORED_CHARS) .trimStart(*IGNORED_CHARS)
}?.sortedBy { }?.sortedBy {
it.lowestMode it.lowestMode
}?.let(nickListAdapter::submitList) }?.toList()?.let {
activity?.runOnUiThread {
nickListAdapter.submitList(it)
}
}
} }
}) })
savedInstanceState?.run { savedInstanceState?.run {
......
...@@ -46,7 +46,7 @@ class AppSettingsFragment : DaggerPreferenceFragmentCompat(), ...@@ -46,7 +46,7 @@ class AppSettingsFragment : DaggerPreferenceFragmentCompat(),
fun initSummary(preference: Preference) { fun initSummary(preference: Preference) {
if (preference is PreferenceGroup) { if (preference is PreferenceGroup) {
(0 until preference.preferenceCount).map(preference::getPreference).forEach(::initSummary) (0 until preference.preferenceCount).asSequence().map(preference::getPreference).forEach(::initSummary)
} else { } else {
updateSummary(preference) updateSummary(preference)
} }
......
...@@ -61,7 +61,7 @@ object AvatarHelper { ...@@ -61,7 +61,7 @@ object AvatarHelper {
?: return emptyList() ?: return emptyList()
if (size != null) { if (size != null) {
return listOf("https://static.irccloud-cdn.com/avatar-redirect/w${truncateSize(size)}/$userId") return listOf("https://static.irccloud-cdn.com/avatar-redirect/s${truncateSize(size)}/$userId")
} }
return listOf("https://static.irccloud-cdn.com/avatar-redirect/$userId") return listOf("https://static.irccloud-cdn.com/avatar-redirect/$userId")
......
...@@ -48,9 +48,7 @@ object Patterns { ...@@ -48,9 +48,7 @@ object Patterns {
@Language("RegExp") @Language("RegExp")
const val LOCAL_HOST_NAME = """(?:$IRI_LABEL\.)*$IRI_LABEL""" const val LOCAL_HOST_NAME = """(?:$IRI_LABEL\.)*$IRI_LABEL"""
@Language("RegExp") val DOMAIN_NAME = Regex("""(?:$LOCAL_HOST_NAME|$HOST_NAME|$IP_ADDRESS_STRING)""")
const val DOMAIN_NAME_STR = """(?:$LOCAL_HOST_NAME|$HOST_NAME|$IP_ADDRESS_STRING)"""
val DOMAIN_NAME = Regex(DOMAIN_NAME_STR)
/** /**
* Regular expression for valid email characters. Does not include some of the valid characters * Regular expression for valid email characters. Does not include some of the valid characters
* defined in RFC5321: #&~!^`{}/=$*?| * defined in RFC5321: #&~!^`{}/=$*?|
...@@ -72,12 +70,12 @@ object Patterns { ...@@ -72,12 +70,12 @@ object Patterns {
@Language("RegExp") @Language("RegExp")
const val EMAIL_ADDRESS_DOMAIN = """(?=.{1,255}(?:\s|$|^))$HOST_NAME""" const val EMAIL_ADDRESS_DOMAIN = """(?=.{1,255}(?:\s|$|^))$HOST_NAME"""
/** /**
* Regular expression pattern to match email addresses. It excludes double quoted local parts * Regular expression pattern to match email addresses. It excludes double quoted local parts
* and the special characters #&~!^`{}/=$*?| that are included in RFC5321. * and the special characters #&~!^`{}/=$*?| that are included in RFC5321.
*/ */
const val AUTOLINK_EMAIL_ADDRESS_STR = """($WORD_BOUNDARY(?:$EMAIL_ADDRESS_LOCAL_PART@$EMAIL_ADDRESS_DOMAIN)$WORD_BOUNDARY)""" val AUTOLINK_EMAIL_ADDRESS = Regex("""($WORD_BOUNDARY(?:$EMAIL_ADDRESS_LOCAL_PART@$EMAIL_ADDRESS_DOMAIN)$WORD_BOUNDARY)""")
val AUTOLINK_EMAIL_ADDRESS = Regex(AUTOLINK_EMAIL_ADDRESS_STR)
/** /**
* Regular expression pattern to match IRCCloud user idents. * Regular expression pattern to match IRCCloud user idents.
......
package de.kuschku.quasseldroid.util
import de.kuschku.libquassel.protocol.Message_Flag
import de.kuschku.libquassel.protocol.Message_Type
import de.kuschku.quasseldroid.persistence.QuasselDatabase
import de.kuschku.quasseldroid.settings.MessageSettings
import org.junit.Test
import org.threeten.bp.Instant
class AvatarHelperTest {
@Test
fun testGravatar() {
val message = QuasselDatabase.DatabaseMessage(
messageId = 1,
time = Instant.now(),
type = Message_Type.of(Message_Type.Plain).toInt(),
flag = Message_Flag.of().toInt(),
bufferId = 0,
sender = "justJanne",
senderPrefixes = "",
realName = "Janne Koschinski <janne@kuschku.de>",
avatarUrl = "",
content = "Lorem Ipsum I Dolor Sit Amet",
ignored = false
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = true,
showIRCCloudAvatars = true
),
message
).contains("https://www.gravatar.com/avatar/81128f11cae692bc486e3f88b854ddf1?d=404")
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = false,
showIRCCloudAvatars = false
),
message
).isEmpty()
)
}
@Test
fun testIrcCloud() {
val message = QuasselDatabase.DatabaseMessage(
messageId = 1,
time = Instant.now(),
type = Message_Type.of(Message_Type.Plain).toInt(),
flag = Message_Flag.of().toInt(),
bufferId = 0,
sender = "jwheare!sid2@irccloud.com",
senderPrefixes = "",
realName = "James Wheare",
avatarUrl = "",
content = "Lorem Ipsum I Dolor Sit Amet",
ignored = false
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = true,
showIRCCloudAvatars = true
),
message
).contains("https://static.irccloud-cdn.com/avatar-redirect/2")
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = false,
showIRCCloudAvatars = false
),
message
).isEmpty()
)
}
@Test
fun testActualAvatars() {
val message = QuasselDatabase.DatabaseMessage(
messageId = 1,
time = Instant.now(),
type = Message_Type.of(Message_Type.Plain).toInt(),
flag = Message_Flag.of().toInt(),
bufferId = 0,
sender = "jwheare!sid2@irccloud.com",
senderPrefixes = "",
realName = "James Wheare",
avatarUrl = "https://quasseldroid.info/favicon.png",
content = "Lorem Ipsum I Dolor Sit Amet",
ignored = false
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = true,
showIRCCloudAvatars = true
),
message
).contains("https://quasseldroid.info/favicon.png")
)
assert(
AvatarHelper.avatar(
MessageSettings(
showGravatarAvatars = false,
showIRCCloudAvatars = false
),
message
) == listOf("https://quasseldroid.info/favicon.png")
)
}
}
...@@ -64,10 +64,10 @@ class SessionManager( ...@@ -64,10 +64,10 @@ class SessionManager(
Invokers Invokers
} }
fun ifDisconnected(closure: () -> Unit) { fun ifDisconnected(closure: (ISession) -> Unit) {
state.or(ConnectionState.DISCONNECTED).let { state.or(ConnectionState.DISCONNECTED).let {
if (it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED) { if (it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED) {
closure() closure(inProgressSession.value)
} }
} }
} }
......
...@@ -185,7 +185,7 @@ class QuasselViewModel : ViewModel() { ...@@ -185,7 +185,7 @@ class QuasselViewModel : ViewModel() {
val userModes = ircChannel.userModes(user) val userModes = ircChannel.userModes(user)
val prefixModes = network.prefixModes() val prefixModes = network.prefixModes()
val lowestMode = userModes.mapNotNull { val lowestMode = userModes.asSequence().mapNotNull {
prefixModes.indexOf(it) prefixModes.indexOf(it)
}.min() ?: prefixModes.size }.min() ?: prefixModes.size
...@@ -300,7 +300,7 @@ class QuasselViewModel : ViewModel() { ...@@ -300,7 +300,7 @@ class QuasselViewModel : ViewModel() {
) )
).switchMap { (ids, temp, perm) -> ).switchMap { (ids, temp, perm) ->
fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) = fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) =
ids.mapNotNull { id -> ids.asSequence().mapNotNull { id ->
bufferSyncer.bufferInfo(id) bufferSyncer.bufferInfo(id)
}.filter { }.filter {
currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
...@@ -403,10 +403,10 @@ class QuasselViewModel : ViewModel() { ...@@ -403,10 +403,10 @@ class QuasselViewModel : ViewModel() {
transformIds(ids.distinct(), BufferHiddenState.VISIBLE) transformIds(ids.distinct(), BufferHiddenState.VISIBLE)
} }
combineLatest<BufferProps>(buffers).map { list -> combineLatest<BufferProps>(buffers.toList()).map { list ->
Pair<BufferViewConfig?, List<BufferProps>>( Pair<BufferViewConfig?, List<BufferProps>>(
config, config,
list.filter { list.asSequence().filter {
(!config.hideInactiveBuffers()) || (!config.hideInactiveBuffers()) ||
it.bufferStatus != BufferStatus.OFFLINE || it.bufferStatus != BufferStatus.OFFLINE ||
it.info.type.hasFlag(Buffer_Type.StatusBuffer) it.info.type.hasFlag(Buffer_Type.StatusBuffer)
...@@ -415,7 +415,7 @@ class QuasselViewModel : ViewModel() { ...@@ -415,7 +415,7 @@ class QuasselViewModel : ViewModel() {
it.sortedBy { IrcCaseMappers.unicode.toLowerCaseNullable(it.info.bufferName) } it.sortedBy { IrcCaseMappers.unicode.toLowerCaseNullable(it.info.bufferName) }
.sortedByDescending { it.hiddenState == BufferHiddenState.VISIBLE } .sortedByDescending { it.hiddenState == BufferHiddenState.VISIBLE }
else it else it
}.distinctBy { it.info.bufferId } }.distinctBy { it.info.bufferId }.toList()
) )
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment