chore(deps): update plugin ktlint to v12 (#357)

This commit is contained in:
Nik Clayton 2024-01-08 23:26:46 +01:00 committed by GitHub
parent d4fe1dff9e
commit d8be70a465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 356 additions and 254 deletions

View File

@ -7,14 +7,21 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
# Disable wildcard imports
[*.{java,kt}]
ktlint_code_style = android_studio
ij_kotlin_imports_layout = *
# Disable wildcard imports
ij_kotlin_name_count_to_use_star_import = 999
ij_kotlin_name_count_to_use_star_import_for_members = 999
ij_java_class_count_to_use_import_on_demand = 999
# Require trailing comma
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
max_line_length = off
[*.{yml,yaml}]
indent_size = 2

View File

@ -22,8 +22,8 @@ import app.pachli.util.NoUnderlineURLSpan
import app.pachli.util.hide
import app.pachli.util.show
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlinx.coroutines.launch
@AndroidEntryPoint
class AboutActivity : BottomSheetActivity() {

View File

@ -45,8 +45,8 @@ import app.pachli.util.viewBinding
import app.pachli.viewmodel.AccountsInListViewModel
import app.pachli.viewmodel.State
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlinx.coroutines.launch
private typealias AccountInfo = Pair<TimelineAccount, Boolean>

View File

@ -51,8 +51,8 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.EntryPointAccessors.fromApplication
import dagger.hilt.components.SingletonComponent
import timber.log.Timber
import javax.inject.Inject
import timber.log.Timber
@AndroidEntryPoint
abstract class BaseActivity : AppCompatActivity() {

View File

@ -30,8 +30,8 @@ import app.pachli.util.looksLikeMastodonUrl
import app.pachli.util.openLink
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlinx.coroutines.launch
/** this is the base class for all activities that open links
* links are checked against the api if they are mastodon links so they can be opened in Tusky
@ -97,7 +97,7 @@ abstract class BottomSheetActivity : BaseActivity() {
onEndSearch(url)
performUrlFallbackAction(url, lookupFallbackBehavior)
}
},)
})
}
}

View File

@ -124,7 +124,10 @@ class EditProfileActivity : BaseActivity() {
binding.fieldList.layoutManager = LinearLayoutManager(this)
binding.fieldList.adapter = accountFieldEditAdapter
val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply { sizeDp = 12; colorInt = Color.WHITE }
val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply {
sizeDp = 12
colorInt = Color.WHITE
}
binding.addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds(plusDrawable, null, null, null)

View File

@ -237,7 +237,10 @@ class ListsActivity : BaseActivity() {
.apply {
val iconColor = MaterialColors.getColor(nameTextView, android.R.attr.textColorTertiary)
val context = nameTextView.context
val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).apply { sizeDp = 20; colorInt = iconColor }
val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).apply {
sizeDp = 20
colorInt = iconColor
}
nameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
}

View File

@ -153,9 +153,9 @@ import com.mikepenz.materialdrawer.widget.AccountHeaderView
import dagger.hilt.android.AndroidEntryPoint
import de.c1710.filemojicompat_ui.helpers.EMOJI_PREFERENCE
import io.reactivex.rxjava3.schedulers.Schedulers
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {

View File

@ -40,11 +40,11 @@ import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper
import de.c1710.filemojicompat_ui.helpers.EmojiPreference
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import org.conscrypt.Conscrypt
import timber.log.Timber
import java.security.Security
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import org.conscrypt.Conscrypt
import timber.log.Timber
@HiltAndroidApp
class PachliApplication : Application() {

View File

@ -41,10 +41,10 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject
/**
* Show a list of statuses of a particular type; containing a particular hashtag,

View File

@ -60,14 +60,14 @@ import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform
import dagger.hilt.android.AndroidEntryPoint
import java.util.regex.Pattern
import javax.inject.Inject
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.regex.Pattern
import javax.inject.Inject
@AndroidEntryPoint
class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {

View File

@ -63,12 +63,12 @@ import dagger.hilt.android.AndroidEntryPoint
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import timber.log.Timber
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.Locale
import timber.log.Timber
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit

View File

@ -3,12 +3,12 @@ package app.pachli.appstore
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.TimelineDao
import com.google.gson.Gson
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import javax.inject.Inject
class CacheUpdater @Inject constructor(
eventHub: EventHub,

View File

@ -1,9 +1,9 @@
package app.pachli.appstore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
interface Event

View File

@ -20,11 +20,11 @@ import app.pachli.util.Success
import app.pachli.util.getDomain
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class AccountViewModel @Inject constructor(
@ -137,8 +137,8 @@ class AccountViewModel @Inject constructor(
fun changeSubscribingState() {
val relationship = relationshipData.value?.data
if (relationship?.notifying == true || /* Mastodon 3.3.0rc1 */
relationship?.subscribing == true /* Pleroma */
if (relationship?.notifying == true || // Mastodon 3.3.0rc1
relationship?.subscribing == true // Pleroma
) {
changeRelationship(RelationShipAction.UNSUBSCRIBE)
} else {
@ -156,7 +156,7 @@ class AccountViewModel @Inject constructor(
}
}, { e ->
Timber.e("Error muting $instance", e)
},)
})
}
}
@ -169,7 +169,7 @@ class AccountViewModel @Inject constructor(
}
}, { e ->
Timber.e("Error unmuting $instance", e)
},)
})
}
}
@ -317,6 +317,13 @@ class AccountViewModel @Inject constructor(
}
enum class RelationShipAction {
FOLLOW, UNFOLLOW, BLOCK, UNBLOCK, MUTE, UNMUTE, SUBSCRIBE, UNSUBSCRIBE
FOLLOW,
UNFOLLOW,
BLOCK,
UNBLOCK,
MUTE,
UNMUTE,
SUBSCRIBE,
UNSUBSCRIBE,
}
}

View File

@ -25,6 +25,7 @@ import at.connyduck.calladapter.networkresult.onFailure
import at.connyduck.calladapter.networkresult.onSuccess
import at.connyduck.calladapter.networkresult.runCatching
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -32,7 +33,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
data class AccountListState(
val list: MastoList,

View File

@ -50,9 +50,9 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
/**
* Fragment with multiple columns of media previews for the specified account.

View File

@ -64,11 +64,11 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import java.io.IOException
import javax.inject.Inject
import kotlinx.coroutines.launch
import retrofit2.Response
import timber.log.Timber
import java.io.IOException
import javax.inject.Inject
@AndroidEntryPoint
class AccountListFragment :
@ -228,7 +228,7 @@ class AccountListFragment :
onBlockSuccess(block, id, position)
}, {
onBlockFailure(block, id, it)
},)
})
}
}

View File

@ -130,7 +130,13 @@ class AnnouncementAdapter(
spanBuilder.setSpan(span, 0, 1, 0)
Glide.with(this)
.asDrawable()
.load(if (animateEmojis) { reaction.url } else { reaction.staticUrl })
.load(
if (animateEmojis) {
reaction.url
} else {
reaction.staticUrl
},
)
.into(span.getTarget(animateEmojis))
this.text = spanBuilder
}

View File

@ -32,9 +32,9 @@ import app.pachli.util.Resource
import app.pachli.util.Success
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class AnnouncementsViewModel @Inject constructor(

View File

@ -111,17 +111,17 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.text.DecimalFormat
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* Compose a status, either by creating one from scratch, or by editing an existing
@ -186,7 +186,8 @@ class ComposeActivity :
uriNew,
size,
itemOld.description,
null, // Intentionally reset focus when cropping
// Intentionally reset focus when cropping
null,
itemOld,
)
}
@ -515,13 +516,22 @@ class ComposeActivity :
val textColor = MaterialColors.getColor(binding.root, android.R.attr.textColorTertiary)
val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { colorInt = textColor; sizeDp = 18 }
val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply {
colorInt = textColor
sizeDp = 18
}
binding.actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null)
val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply { colorInt = textColor; sizeDp = 18 }
val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply {
colorInt = textColor
sizeDp = 18
}
binding.actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null)
val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply { colorInt = textColor; sizeDp = 18 }
val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply {
colorInt = textColor
sizeDp = 18
}
binding.addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null)
binding.actionPhotoTake.visible(Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(packageManager) != null)
@ -1274,10 +1284,15 @@ class ComposeActivity :
val state: State,
) {
enum class Type {
IMAGE, VIDEO, AUDIO;
IMAGE,
VIDEO,
AUDIO,
}
enum class State {
UPLOADING, UNPROCESSED, PROCESSED, PUBLISHED
UPLOADING,
UNPROCESSED,
PROCESSED,
PUBLISHED,
}
}

View File

@ -40,6 +40,7 @@ import app.pachli.service.ServiceClient
import app.pachli.service.StatusToSend
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -53,7 +54,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class ComposeViewModel @Inject constructor(
@ -168,7 +168,11 @@ class ComposeViewModel @Inject constructor(
item.copy(
id = event.mediaId,
uploadPercent = -1,
state = if (event.processed) { QueuedMedia.State.PROCESSED } else { QueuedMedia.State.UNPROCESSED },
state = if (event.processed) {
QueuedMedia.State.PROCESSED
} else {
QueuedMedia.State.UNPROCESSED
},
)
is UploadEvent.ErrorEvent -> {
media.update { mediaList -> mediaList.filter { it.localId != mediaItem.localId } }
@ -383,7 +387,7 @@ class ComposeViewModel @Inject constructor(
}, { e ->
Timber.e("Autocomplete search for $token failed.", e)
emptyList()
},)
})
}
'#' -> {
return api.searchSync(query = token, type = SearchType.Hashtag.apiParameter, limit = 10)
@ -392,7 +396,7 @@ class ComposeViewModel @Inject constructor(
}, { e ->
Timber.e("Autocomplete search for $token failed.", e)
emptyList()
},)
})
}
':' -> {
val emojiList = emoji.replayCache.firstOrNull() ?: return emptyList()

View File

@ -52,10 +52,10 @@ fun downsizeImage(
// Get EXIF data, for orientation info.
val orientation = getImageOrientation(uri, contentResolver)
/* Unfortunately, there isn't a determined worst case compression ratio for image
* formats. So, the only way to tell if they're too big is to compress them and
* test, and keep trying at smaller sizes. The initial estimate should be good for
* many cases, so it should only iterate once, but the loop is used to be absolutely
* sure it gets downsized to below the limit. */
* formats. So, the only way to tell if they're too big is to compress them and
* test, and keep trying at smaller sizes. The initial estimate should be good for
* many cases, so it should only iterate once, but the loop is used to be absolutely
* sure it gets downsized to below the limit. */
var scaledImageSize = 1024
do {
val outputStream = try {

View File

@ -38,6 +38,13 @@ import app.pachli.util.getImageSquarePixels
import app.pachli.util.getMediaSize
import app.pachli.util.getServerErrorMessage
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.Date
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -56,13 +63,6 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import retrofit2.HttpException
import timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.Date
import javax.inject.Inject
import javax.inject.Singleton
sealed interface FinalUploadEvent
@ -85,9 +85,9 @@ fun createNewImageFile(context: Context, suffix: String = ".jpg"): File {
val imageFileName = "Pachli_${randomId}_"
val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
imageFileName, /* prefix */
suffix, /* suffix */
storageDir, /* directory */
imageFileName,
suffix,
storageDir,
)
}
@ -259,9 +259,9 @@ class MediaUploader @Inject constructor(
// .m4a files. See https://github.com/tuskyapp/Tusky/issues/3189 for details.
// Sniff the content of the file to determine the actual type.
if (mimeType != null && (
mimeType.startsWith("audio/", ignoreCase = true) ||
mimeType.startsWith("video/", ignoreCase = true)
)
mimeType.startsWith("audio/", ignoreCase = true) ||
mimeType.startsWith("video/", ignoreCase = true)
)
) {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(context, media.uri)

View File

@ -25,6 +25,8 @@ import app.pachli.R
import app.pachli.core.network.model.NewPoll
import app.pachli.databinding.DialogAddPollBinding
const val DAY_SECONDS = 60 * 60 * 24
fun showAddPollDialog(
context: Context,
poll: NewPoll?,
@ -74,7 +76,6 @@ fun showAddPollDialog(
}
}
val DAY_SECONDS = 60 * 60 * 24
val desiredDuration = poll?.expiresIn ?: DAY_SECONDS
val pollDurationId = durations.indexOfLast {
it <= desiredDuration

View File

@ -225,7 +225,7 @@ class ComposeScheduleView
}
companion object {
var MINIMUM_SCHEDULED_SECONDS = 330 // Minimum is 5 minutes, pad 30 seconds for posting
const val MINIMUM_SCHEDULED_SECONDS = 330 // Minimum is 5 minutes, pad 30 seconds for posting
fun calendar(): Calendar = Calendar.getInstance(TimeZone.getDefault())
}
}

View File

@ -62,7 +62,10 @@ class TootButton
Status.Visibility.DIRECT,
-> {
setText(R.string.action_send)
IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply { sizeDp = 18; colorInt = Color.WHITE }
IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply {
sizeDp = 18
colorInt = Color.WHITE
}
}
else -> {
null

View File

@ -61,13 +61,13 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlin.time.DurationUnit
import kotlin.time.toDuration
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.time.DurationUnit
import kotlin.time.toDuration
@AndroidEntryPoint
class ConversationsFragment :

View File

@ -32,10 +32,10 @@ import app.pachli.usecase.TimelineCases
import app.pachli.util.EmptyPagingSource
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class ConversationsViewModel @Inject constructor(
@ -81,7 +81,7 @@ class ConversationsViewModel @Inject constructor(
saveConversationToDb(newConversation)
}, { e ->
Timber.w("failed to favourite status", e)
},)
})
}
}
@ -96,7 +96,7 @@ class ConversationsViewModel @Inject constructor(
saveConversationToDb(newConversation)
}, { e ->
Timber.w("failed to bookmark status", e)
},)
})
}
}
@ -112,7 +112,7 @@ class ConversationsViewModel @Inject constructor(
saveConversationToDb(newConversation)
}, { e ->
Timber.w("failed to vote in poll", e)
},)
})
}
}

View File

@ -30,6 +30,12 @@ import app.pachli.core.network.model.NewPoll
import app.pachli.core.network.model.Status
import app.pachli.util.copyToFile
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
@ -37,12 +43,6 @@ import okhttp3.Request
import okio.buffer
import okio.sink
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import javax.inject.Inject
class DraftHelper @Inject constructor(
@ApplicationContext val context: Context,

View File

@ -37,11 +37,11 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class DraftsActivity : BaseActivity(), DraftActionListener {

View File

@ -28,8 +28,8 @@ import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.NetworkResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlinx.coroutines.launch
@HiltViewModel
class DraftsViewModel @Inject constructor(

View File

@ -27,10 +27,10 @@ import com.google.android.material.chip.Chip
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.switchmaterial.SwitchMaterial
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.util.Date
import javax.inject.Inject
import kotlinx.coroutines.launch
import retrofit2.HttpException
/**
* Edit a single server-side filter.
@ -302,7 +302,11 @@ class EditFilterActivity : BaseActivity() {
// but create/edit take a number of seconds (relative to the time the operation is posted)
fun getSecondsForDurationIndex(index: Int, context: Context?, default: Date? = null): Int? {
return when (index) {
-1 -> if (default == null) { default } else { ((default.time - System.currentTimeMillis()) / 1000).toInt() }
-1 -> if (default == null) {
default
} else {
((default.time - System.currentTimeMillis()) / 1000).toInt()
}
0 -> null
else -> context?.resources?.getIntArray(R.array.filter_duration_values)?.get(index)
}

View File

@ -10,10 +10,10 @@ import app.pachli.core.network.model.FilterKeyword
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
import retrofit2.HttpException
import javax.inject.Inject
@HiltViewModel
class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub: EventHub) : ViewModel() {

View File

@ -10,11 +10,11 @@ import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import retrofit2.HttpException
import javax.inject.Inject
@HiltViewModel
class FiltersViewModel @Inject constructor(
@ -23,7 +23,11 @@ class FiltersViewModel @Inject constructor(
) : ViewModel() {
enum class LoadingState {
INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER
INITIAL,
LOADING,
LOADED,
ERROR_NETWORK,
ERROR_OTHER,
}
data class State(val filters: List<Filter>, val loadingState: LoadingState)

View File

@ -27,10 +27,10 @@ import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class FollowedTagsActivity :

View File

@ -12,8 +12,8 @@ import app.pachli.core.network.model.HashTag
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import timber.log.Timber
import javax.inject.Inject
import timber.log.Timber
@HiltViewModel
class FollowedTagsViewModel @Inject constructor(
@ -43,6 +43,6 @@ class FollowedTagsViewModel @Inject constructor(
}, { e ->
Timber.e("Autocomplete search for $token failed.", e)
emptyList()
},)
})
}
}

View File

@ -25,10 +25,10 @@ import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.getOrElse
import at.connyduck.calladapter.networkresult.onSuccess
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
class InstanceInfoRepository @Inject constructor(
private val api: MastodonApi,
@ -77,12 +77,18 @@ class InstanceInfoRepository @Inject constructor(
maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength,
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength,
)
try { instanceDao.upsert(instanceEntity) } catch (_: Exception) { }
try {
instanceDao.upsert(instanceEntity)
} catch (_: Exception) { }
instanceEntity
},
{ throwable ->
Timber.w("failed to instance, falling back to cache and default values", throwable)
try { instanceDao.getInstanceInfo(instanceName) } catch (_: Exception) { null }
try {
instanceDao.getInstanceInfo(instanceName)
} catch (_: Exception) {
null
}
},
).let { instanceInfo: InstanceInfoEntity? ->
InstanceInfo(

View File

@ -20,9 +20,9 @@ import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class InstanceListFragment :
@ -71,7 +71,7 @@ class InstanceListFragment :
adapter.addItem(instance)
}, { e ->
Timber.e("Error muting domain $instance", e)
},)
})
} else {
api.unblockDomain(instance).fold({
adapter.removeItem(position)
@ -82,7 +82,7 @@ class InstanceListFragment :
.show()
}, { e ->
Timber.e("Error unmuting domain $instance", e)
},)
})
}
}
}

View File

@ -44,10 +44,10 @@ import app.pachli.util.viewBinding
import at.connyduck.calladapter.networkresult.fold
import com.bumptech.glide.Glide
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch
import okhttp3.HttpUrl
import timber.log.Timber
import javax.inject.Inject
/**
* Main login page, the first thing that users see.
@ -333,7 +333,7 @@ class LoginActivity : BaseActivity() {
binding.domainTextInputLayout.error =
getString(R.string.error_loading_account_details)
Timber.e(getString(R.string.error_loading_account_details), e)
},)
})
}
private fun setLoading(loadingState: Boolean) {

View File

@ -21,10 +21,10 @@ import androidx.lifecycle.viewModelScope
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class LoginWebViewViewModel @Inject constructor(
@ -43,7 +43,7 @@ class LoginWebViewViewModel @Inject constructor(
instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty()
}, { throwable ->
Timber.w("failed to load instance info", throwable)
},)
})
}
}
}

View File

@ -28,11 +28,11 @@ import app.pachli.core.network.model.Marker
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.delay
import timber.log.Timber
import javax.inject.Inject
import kotlin.math.min
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.delay
import timber.log.Timber
/**
* Fetch Mastodon notifications and show Android notifications, with summaries, for them.

View File

@ -57,9 +57,9 @@ import app.pachli.viewdata.calculatePercent
import app.pachli.worker.NotificationWorker
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import timber.log.Timber
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import timber.log.Timber
/** ID of notification shown when fetching notifications */
const val NOTIFICATION_ID_FETCH_NOTIFICATION = 0

View File

@ -324,7 +324,8 @@ class NotificationsFragment :
when (it) {
is UiSuccess.Block, is UiSuccess.Mute, is UiSuccess.MuteConversation ->
adapter.refresh()
else -> { /* nothing to do */
else -> {
/* nothing to do */
}
}
}

View File

@ -25,12 +25,12 @@ import app.pachli.core.network.model.Links
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import com.google.gson.Gson
import javax.inject.Inject
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import okhttp3.Headers
import retrofit2.Response
import timber.log.Timber
import javax.inject.Inject
private val INVALID = LoadResult.Invalid<String, Notification>()

View File

@ -26,13 +26,13 @@ import app.pachli.core.common.di.ApplicationScope
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import com.google.gson.Gson
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import okhttp3.ResponseBody
import retrofit2.Response
import timber.log.Timber
import javax.inject.Inject
class NotificationsRepository @Inject constructor(
private val mastodonApi: MastodonApi,

View File

@ -49,6 +49,8 @@ import app.pachli.viewdata.NotificationViewData
import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.getOrThrow
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
@ -72,8 +74,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
data class UiState(
/** Filtered notification types */
@ -333,6 +333,7 @@ class NotificationsViewModel @Inject constructor(
// message, as it will be confusing to the user.
val uiSuccess = MutableSharedFlow<UiSuccess>()
@Suppress("ktlint:standard:property-naming")
/** Channel for error results */
// Errors are sent to a channel to ensure that any errors that occur *before* there are any
// subscribers are retained. If this was a SharedFlow any errors would be dropped, and if it

View File

@ -55,11 +55,11 @@ import com.google.android.material.snackbar.Snackbar
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class AccountPreferencesFragment : PreferenceFragmentCompat() {

View File

@ -36,10 +36,10 @@ import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.databinding.ActivityPreferencesBinding
import app.pachli.util.setAppNightMode
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
/**
* Show specific preferences.

View File

@ -40,12 +40,12 @@ import app.pachli.util.Success
import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ReportViewModel @Inject constructor(
@ -195,7 +195,7 @@ class ReportViewModel @Inject constructor(
}
}, { t ->
blockStateMutable.value = Error(false, t.message)
},)
})
}
blockStateMutable.value = Loading()
}
@ -208,7 +208,7 @@ class ReportViewModel @Inject constructor(
reportingStateMutable.value = Success(true)
}, { error ->
reportingStateMutable.value = Error(cause = error)
},)
})
}
}

View File

@ -55,9 +55,9 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class ReportStatusesFragment :

View File

@ -44,9 +44,9 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class ScheduledStatusActivity :

View File

@ -26,9 +26,9 @@ import app.pachli.core.network.model.ScheduledStatus
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class ScheduledStatusViewModel @Inject constructor(

View File

@ -33,11 +33,11 @@ import at.connyduck.calladapter.networkresult.NetworkResult
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.onFailure
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class SearchViewModel @Inject constructor(
@ -127,7 +127,7 @@ class SearchViewModel @Inject constructor(
)
}, { t ->
Timber.d("Failed to reblog status ${statusViewData.id}", t)
},)
})
}
}

View File

@ -26,8 +26,8 @@ import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import com.google.android.material.divider.MaterialDividerItemDecoration
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@AndroidEntryPoint
class SearchAccountsFragment : SearchFragment<TimelineAccount>() {

View File

@ -32,10 +32,10 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
abstract class SearchFragment<T : Any> :
Fragment(R.layout.fragment_search),

View File

@ -58,10 +58,10 @@ import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class SearchStatusesFragment : SearchFragment<StatusViewData>(), StatusActionListener {

View File

@ -41,14 +41,14 @@ import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.NetworkResult
import at.connyduck.calladapter.networkresult.fold
import com.google.gson.Gson
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
// TODO: This is very similar to NetworkTimelineRepository. They could be merged (and the use
// of the cache be made a parameter to getStatusStream), except that they return Pagers of
@ -213,7 +213,7 @@ class CachedTimelineRepository @Inject constructor(
}, {
// Reset the translation state
saveStatusViewData(statusViewData)
},)
})
return translation
}

View File

@ -22,9 +22,9 @@ import app.pachli.core.network.model.FilterV1
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.getOrThrow
import retrofit2.HttpException
import javax.inject.Inject
import javax.inject.Singleton
import retrofit2.HttpException
sealed interface FilterKind {
/** API v1 filter, filtering happens client side */

View File

@ -31,10 +31,10 @@ import app.pachli.core.network.model.Status
import app.pachli.core.network.model.TimelineKind
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.getDomain
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import timber.log.Timber
import javax.inject.Inject
// Things that make this more difficult than it should be:
//

View File

@ -82,6 +82,7 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
@ -91,7 +92,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.time.Duration.Companion.seconds
@AndroidEntryPoint
class TimelineFragment :
@ -212,7 +212,10 @@ class TimelineFragment :
*/
// TODO: Copied from NotificationsFragment
val updateTimestampFlow = flow {
while (true) { delay(60.seconds); emit(Unit) }
while (true) {
delay(60.seconds)
emit(Unit)
}
}.onEach {
adapter.notifyItemRangeChanged(0, adapter.itemCount, listOf(StatusBaseViewHolder.Key.KEY_CREATED))
}

View File

@ -1,7 +1,7 @@
package app.pachli.components.timeline.util
import retrofit2.HttpException
import java.io.IOException
import retrofit2.HttpException
fun Throwable.isExpected() = this is IOException || this is HttpException

View File

@ -37,13 +37,13 @@ import app.pachli.core.network.model.Links
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import com.google.gson.Gson
import java.io.IOException
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import okhttp3.Headers
import retrofit2.HttpException
import retrofit2.Response
import timber.log.Timber
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class CachedTimelineRemoteMediator(

View File

@ -39,13 +39,13 @@ import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.viewdata.StatusViewData
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
/**
* TimelineViewModel that caches all statuses in a local database

View File

@ -21,8 +21,8 @@ import androidx.paging.PagingSource
import androidx.paging.PagingSource.LoadResult
import androidx.paging.PagingState
import app.pachli.core.network.model.Status
import timber.log.Timber
import javax.inject.Inject
import timber.log.Timber
private val INVALID = LoadResult.Invalid<String, Status>()

View File

@ -27,11 +27,11 @@ import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Status
import app.pachli.core.network.model.TimelineKind
import app.pachli.core.network.retrofit.MastodonApi
import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import retrofit2.HttpException
import retrofit2.Response
import timber.log.Timber
import java.io.IOException
/** Remote mediator for accessing timelines that are not backed by the database. */
@OptIn(ExperimentalPagingApi::class)

View File

@ -38,13 +38,13 @@ import app.pachli.usecase.TimelineCases
import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.viewdata.StatusViewData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
/**
* TimelineViewModel that caches all statuses in an in-memory list

View File

@ -21,12 +21,12 @@ import app.pachli.BuildConfig
import app.pachli.core.common.string.isLessThan
import app.pachli.core.network.model.Links
import app.pachli.core.network.model.Status
import retrofit2.HttpException
import retrofit2.Response
import timber.log.Timber
import java.util.TreeMap
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
import retrofit2.HttpException
import retrofit2.Response
import timber.log.Timber
/** A page of data from the Mastodon API */
data class Page(

View File

@ -57,6 +57,7 @@ import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.util.throttleFirst
import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.getOrThrow
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -75,7 +76,6 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.time.Duration.Companion.milliseconds
data class UiState(
/** True if the FAB should be shown while scrolling */
@ -287,6 +287,7 @@ abstract class TimelineViewModel(
// message, as it will be confusing to the user.
val uiSuccess = MutableSharedFlow<UiSuccess>()
@Suppress("ktlint:standard:property-naming")
/** Channel for error results */
// Errors are sent to a channel to ensure that any errors that occur *before* there are any
// subscribers are retained. If this was a SharedFlow any errors would be dropped, and if it

View File

@ -26,6 +26,8 @@ import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.util.throttleFirst
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -34,8 +36,6 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
sealed interface UiAction

View File

@ -28,14 +28,14 @@ import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.viewdata.TrendingViewData
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import java.io.IOException
import javax.inject.Inject
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.IOException
import javax.inject.Inject
@HiltViewModel
class TrendingTagsViewModel @Inject constructor(
@ -43,7 +43,12 @@ class TrendingTagsViewModel @Inject constructor(
private val eventHub: EventHub,
) : ViewModel() {
enum class LoadingState {
INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER
INITIAL,
LOADING,
REFRESHING,
LOADED,
ERROR_NETWORK,
ERROR_OTHER,
}
data class TrendingTagsUiState(

View File

@ -49,6 +49,7 @@ import at.connyduck.calladapter.networkresult.getOrElse
import at.connyduck.calladapter.networkresult.getOrThrow
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.BufferOverflow
@ -59,7 +60,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class ViewThreadViewModel @Inject constructor(
@ -235,7 +235,7 @@ class ViewThreadViewModel @Inject constructor(
detailedStatusPosition = 0,
revealButton = RevealButtonState.NO_BUTTON,
)
},)
})
}
}
@ -473,7 +473,7 @@ class ViewThreadViewModel @Inject constructor(
if (it is HttpException && it.code() == 403) return@fold
_errors.emit(it)
},)
})
}
}
@ -643,5 +643,7 @@ sealed interface ThreadUiState {
}
enum class RevealButtonState {
NO_BUTTON, REVEAL, HIDE
NO_BUTTON,
REVEAL,
HIDE,
}

View File

@ -50,9 +50,9 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class ViewEditsFragment :

View File

@ -25,6 +25,7 @@ import app.pachli.core.network.model.StatusEdit
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.getOrElse
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -44,7 +45,6 @@ import org.pageseeder.diffx.token.impl.SpaceToken
import org.pageseeder.diffx.xml.NamespaceSet
import org.pageseeder.xmlwriter.XML.NamespaceAware
import org.pageseeder.xmlwriter.XMLStringWriter
import javax.inject.Inject
@HiltViewModel
class ViewEditsViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() {

View File

@ -26,10 +26,10 @@ import app.pachli.R
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.DraftDao
import app.pachli.core.navigation.DraftsActivityIntent
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* This class manages an alert popup when a post has failed and been saved to drafts.

View File

@ -66,9 +66,9 @@ import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.onFailure
import com.google.android.material.snackbar.Snackbar
import io.github.z4kn4fein.semver.constraints.toConstraint
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
/* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an
* awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature

View File

@ -59,9 +59,9 @@ import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import okhttp3.OkHttpClient
import javax.inject.Inject
import kotlin.math.abs
import okhttp3.OkHttpClient
/**
* Plays a video, showing media description if available.

View File

@ -17,11 +17,11 @@
package app.pachli.network
import java.io.IOException
import java.io.InputStream
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.IOException
import java.io.InputStream
class ProgressRequestBody(
private val content: InputStream,

View File

@ -23,12 +23,12 @@ import app.pachli.core.network.ServerCapabilities
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import com.github.michaelbull.result.getOr
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ServerCapabilitiesRepository @Inject constructor(
@ -58,7 +58,7 @@ class ServerCapabilitiesRepository @Inject constructor(
{
mastodonApi.getInstanceV1().fold({ instance ->
ServerCapabilities.from(instance).getOr { null }
}, { null },)
}, { null })
},
) ?: ServerCapabilities()
}

View File

@ -27,10 +27,10 @@ import app.pachli.components.notifications.updateUnifiedPushSubscription
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.retrofit.MastodonApi
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@DelicateCoroutinesApi
@AndroidEntryPoint

View File

@ -43,8 +43,8 @@ import app.pachli.core.network.model.Status
import app.pachli.service.SendStatusService
import app.pachli.service.StatusToSend
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject
import timber.log.Timber
@AndroidEntryPoint
class SendStatusBroadcastReceiver : BroadcastReceiver() {

View File

@ -25,12 +25,12 @@ import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.worker.NotificationWorker
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.unifiedpush.android.connector.MessagingReceiver
import timber.log.Timber
import javax.inject.Inject
@DelicateCoroutinesApi
@AndroidEntryPoint

View File

@ -38,6 +38,9 @@ import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.unsafeLazy
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.AndroidEntryPoint
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -48,9 +51,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.parcelize.Parcelize
import retrofit2.HttpException
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AndroidEntryPoint
class SendStatusService : Service() {
@ -194,7 +194,7 @@ class SendStatusService : Service() {
failOrRetry(throwable, statusId)
return@launch
},)
})
}
}
}
@ -260,7 +260,7 @@ class SendStatusService : Service() {
}, { throwable ->
Timber.w("failed sending status", throwable)
failOrRetry(throwable, statusId)
},)
})
stopSelfWhenDone()
}
}
@ -480,7 +480,8 @@ data class StatusToSend(
@Parcelize
data class MediaToSend(
val localId: Int,
val id: String?, // null if media is not yet completely uploaded
// null if media is not yet completely uploaded
val id: String?,
val uri: String,
val description: String?,
val focus: Attachment.Focus?,

View File

@ -5,10 +5,10 @@ import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.di.ApplicationScope
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.preferences.PrefKeys
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
class AccountPreferenceDataStore @Inject constructor(
private val accountManager: AccountManager,

View File

@ -35,7 +35,10 @@ inline fun PreferenceParent.listPreference(builder: ListPreference.() -> Unit):
return pref
}
inline fun <A> PreferenceParent.emojiPreference(activity: A, builder: EmojiPickerPreference.() -> Unit): EmojiPickerPreference
inline fun <A> PreferenceParent.emojiPreference(
activity: A,
builder: EmojiPickerPreference.() -> Unit,
): EmojiPickerPreference
where A : Context, A : ActivityResultRegistryOwner, A : LifecycleOwner {
val pref = EmojiPickerPreference.get(activity)
builder(pref)

View File

@ -39,8 +39,8 @@ import at.connyduck.calladapter.networkresult.NetworkResult
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.onFailure
import at.connyduck.calladapter.networkresult.onSuccess
import timber.log.Timber
import javax.inject.Inject
import timber.log.Timber
class TimelineCases @Inject constructor(
private val mastodonApi: MastodonApi,
@ -123,7 +123,7 @@ class TimelineCases @Inject constructor(
}, { e ->
Timber.w("Failed to change pin state", e)
NetworkResult.failure(TimelineError(e.getServerErrorMessage()))
},)
})
}
suspend fun voteInPoll(statusId: String, pollId: String, choices: List<Int>): NetworkResult<Poll> {

View File

@ -121,8 +121,6 @@ enum class UserRefreshState {
/** A refresh or prepend operation was [LoadState.Error] */
ERROR,
;
}
/**
@ -185,7 +183,9 @@ fun Flow<CombinedLoadStates>.asRefreshState(): Flow<UserRefreshState> {
refresh = when (loadState.refresh) {
is LoadState.Loading -> if (refresh == UserRefreshState.WAITING) UserRefreshState.ACTIVE else refresh
is LoadState.NotLoading -> if (refresh == UserRefreshState.ACTIVE) UserRefreshState.COMPLETE else refresh
else -> { throw IllegalStateException("can't happen, LoadState.Error is already handled") }
else -> {
throw IllegalStateException("can't happen, LoadState.Error is already handled")
}
}
// Prepend can only transition to active if there is an active or complete refresh

View File

@ -17,13 +17,13 @@
package app.pachli.util
import android.util.Base64
import java.security.KeyPairGenerator
import java.security.SecureRandom
import java.security.Security
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECPrivateKey
import org.bouncycastle.jce.interfaces.ECPublicKey
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.KeyPairGenerator
import java.security.SecureRandom
import java.security.Security
object CryptoUtil {
const val CURVE_PRIME256_V1 = "prime256v1"

View File

@ -55,7 +55,13 @@ fun CharSequence.emojify(emojis: List<Emoji>?, view: View, animate: Boolean): Ch
builder.setSpan(span, matcher.start(), matcher.end(), 0)
Glide.with(view)
.asDrawable()
.load(if (animate) { url } else { staticUrl })
.load(
if (animate) {
url
} else {
staticUrl
},
)
.into(span.getTarget(animate))
}
}

View File

@ -17,11 +17,11 @@
package app.pachli.util
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlin.time.Duration
import kotlin.time.TimeMark
import kotlin.time.TimeSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
/**
* Returns a flow that mirrors the original flow, but filters out values that occur within

View File

@ -48,10 +48,10 @@ import com.mikepenz.iconics.IconicsSize
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.color
import com.mikepenz.iconics.utils.size
import timber.log.Timber
import java.lang.ref.WeakReference
import java.net.URI
import java.net.URISyntaxException
import timber.log.Timber
fun getDomain(urlString: String?): String {
val host = urlString?.toUri()?.host ?: return ""

View File

@ -19,8 +19,8 @@ package app.pachli.util
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import app.pachli.core.database.model.AccountEntity
import timber.log.Timber
import java.util.Locale
import timber.log.Timber
private const val TAG: String = "LocaleUtils"

View File

@ -24,7 +24,6 @@ import android.graphics.Matrix
import android.net.Uri
import android.provider.OpenableColumns
import androidx.exifinterface.media.ExifInterface
import timber.log.Timber
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
@ -32,6 +31,7 @@ import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import timber.log.Timber
/**
* Helper methods for obtaining and resizing media files

View File

@ -4,9 +4,9 @@ sealed interface Resource<T> {
val data: T?
}
class Loading<T> (override val data: T? = null) : Resource<T>
class Loading<T>(override val data: T? = null) : Resource<T>
class Success<T> (override val data: T? = null) : Resource<T>
class Success<T>(override val data: T? = null) : Resource<T>
class Error<T>(
override val data: T? = null,

View File

@ -43,7 +43,8 @@ private val finders = mapOf(
FoundMatchType.HTTP_URL to PatternFinder(':', HTTP_URL_REGEX, 5, Character::isWhitespace),
FoundMatchType.HTTPS_URL to PatternFinder(':', HTTPS_URL_REGEX, 6, Character::isWhitespace),
FoundMatchType.TAG to PatternFinder('#', TAG_REGEX, 1, ::isValidForTagPrefix),
FoundMatchType.MENTION to PatternFinder('@', MENTION_REGEX, 1, Character::isWhitespace), // TODO: We also need a proper validator for mentions
// TODO: We also need a proper validator for mentions
FoundMatchType.MENTION to PatternFinder('@', MENTION_REGEX, 1, Character::isWhitespace),
)
private enum class FoundMatchType {

View File

@ -28,6 +28,8 @@ import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.network.ServerCapabilitiesRepository
import app.pachli.settings.AccountPreferenceDataStore
import io.github.z4kn4fein.semver.constraints.toConstraint
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -35,8 +37,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
/**
* Repository for [StatusDisplayOptions], exposed through the [flow] property.
@ -125,7 +125,9 @@ class StatusDisplayOptionsRepository @Inject constructor(
PrefKeys.SHOW_STATS_INLINE -> prev.copy(
showStatsInline = sharedPreferencesRepository.getBoolean(key, default.showStatsInline),
)
else -> { prev }
else -> {
prev
}
}
}
}
@ -146,7 +148,9 @@ class StatusDisplayOptionsRepository @Inject constructor(
PrefKeys.MEDIA_PREVIEW_ENABLED -> prev.copy(mediaPreviewEnabled = value)
PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA -> prev.copy(showSensitiveMedia = value)
PrefKeys.ALWAYS_OPEN_SPOILER -> prev.copy(openSpoiler = value)
else -> { prev }
else -> {
prev
}
}
}
}

View File

@ -2,10 +2,10 @@ package app.pachli.util
import android.content.Context
import app.pachli.R
import java.io.IOException
import org.json.JSONException
import org.json.JSONObject
import retrofit2.HttpException
import java.io.IOException
/**
* checks if this throwable indicates an error causes by a 4xx/5xx server response and

View File

@ -38,10 +38,10 @@ import androidx.appcompat.widget.AppCompatTextView
import androidx.core.view.doOnLayout
import app.pachli.BuildConfig
import app.pachli.R
import timber.log.Timber
import java.lang.Float.max
import java.lang.Float.min
import kotlin.math.abs
import timber.log.Timber
/**
* Displays text to the user with optional [ClickableSpan]s. Extends the touchable area of the spans

View File

@ -62,7 +62,7 @@ class SliderPreference @JvmOverloads constructor(
* @see Slider.getValue
* @see Slider.setValue
*/
var value: Float = defaultValue
var value: Float = DEFAULT_VALUE
get() = _value
set(v) {
val clamped = max(max(v, valueFrom), min(v, valueTo))
@ -85,7 +85,7 @@ class SliderPreference @JvmOverloads constructor(
* Format string to be applied to values before setting the summary. For more control set
* [SliderPreference.formatter]
*/
var format: String = defaultFormat
var format: String = DEFAULT_FORMAT
/**
* Function that will be used to format the summary. The default formatter formats using the
@ -115,11 +115,11 @@ class SliderPreference @JvmOverloads constructor(
val a = context.obtainStyledAttributes(attrs, R.styleable.SliderPreference, defStyleAttr, defStyleRes)
value = a.getFloat(R.styleable.SliderPreference_android_value, defaultValue)
valueFrom = a.getFloat(R.styleable.SliderPreference_android_valueFrom, defaultValueFrom)
valueTo = a.getFloat(R.styleable.SliderPreference_android_valueTo, defaultValueTo)
stepSize = a.getFloat(R.styleable.SliderPreference_android_stepSize, defaultStepSize)
format = a.getString(R.styleable.SliderPreference_format) ?: defaultFormat
value = a.getFloat(R.styleable.SliderPreference_android_value, DEFAULT_VALUE)
valueFrom = a.getFloat(R.styleable.SliderPreference_android_valueFrom, DEFAULT_VALUE_FROM)
valueTo = a.getFloat(R.styleable.SliderPreference_android_valueTo, DEFAULT_VALUE_TO)
stepSize = a.getFloat(R.styleable.SliderPreference_android_stepSize, DEFAULT_STEP_SIZE)
format = a.getString(R.styleable.SliderPreference_format) ?: DEFAULT_FORMAT
val decrementIconResource = a.getResourceId(R.styleable.SliderPreference_iconStart, -1)
if (decrementIconResource != -1) {
@ -135,11 +135,11 @@ class SliderPreference @JvmOverloads constructor(
}
override fun onGetDefaultValue(a: TypedArray, i: Int): Any {
return a.getFloat(i, defaultValue)
return a.getFloat(i, DEFAULT_VALUE)
}
override fun onSetInitialValue(defaultValue: Any?) {
value = getPersistedFloat((defaultValue ?: Companion.defaultValue) as Float)
value = getPersistedFloat((defaultValue ?: Companion.DEFAULT_VALUE) as Float)
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
@ -195,10 +195,10 @@ class SliderPreference @JvmOverloads constructor(
}
companion object {
private const val defaultValueFrom = 0F
private const val defaultValueTo = 1F
private const val defaultValue = 0.5F
private const val defaultStepSize = 0.1F
private const val defaultFormat = "%3.1f"
private const val DEFAULT_VALUE_FROM = 0F
private const val DEFAULT_VALUE_TO = 1F
private const val DEFAULT_VALUE = 0.5F
private const val DEFAULT_STEP_SIZE = 0.1F
private const val DEFAULT_FORMAT = "%3.1f"
}
}

View File

@ -66,12 +66,12 @@ data class StatusViewData(
*/
val isDetailed: Boolean = false,
/** Whether this status should be filtered, and if so, how */
// TODO: This means that null checks are required elsewhere in the code to deal with
// the possibility that this might not be NONE, but that status.filtered is null or
// empty (e.g., StatusBaseViewHolder.setupFilterPlaceholder()). It would be better
// if the Filter.Action class subtypes carried the FilterResult information with them,
// and it's impossible to construct them with an empty list.
/** Whether this status should be filtered, and if so, how */
var filterAction: Filter.Action = Filter.Action.NONE,
/** True if the translated content should be shown (if it exists) */
@ -90,12 +90,15 @@ data class StatusViewData(
private val _content: Spanned
@Suppress("ktlint:standard:property-naming")
private val _translatedContent: Spanned
val content: Spanned
get() = if (translationState == TranslationState.SHOW_TRANSLATION) _translatedContent else _content
private val _spoilerText: String
@Suppress("ktlint:standard:property-naming")
private val _translatedSpoilerText: String
/** The content warning, may be the empty string */
@ -251,7 +254,8 @@ data class StatusViewData(
isExpanded = conversationStatusEntity.expanded,
isShowingContent = conversationStatusEntity.showingHiddenContent,
isCollapsed = conversationStatusEntity.collapsed,
translationState = TranslationState.SHOW_ORIGINAL, // TODO: Include this in conversationStatusEntity
// TODO: Include translationState in conversationStatusEntity
translationState = TranslationState.SHOW_ORIGINAL,
)
fun from(

Some files were not shown because too many files have changed in this diff Show More