mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
refactor: rename media repository files for consistency
This commit is contained in:
@@ -22,15 +22,15 @@ import javax.inject.Singleton
|
|||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Singleton
|
@Singleton
|
||||||
class CompositeMediaRepository @Inject constructor(
|
class CompositeLocalMediaRepository @Inject constructor(
|
||||||
@Offline private val offlineRepository: MediaRepository,
|
@Offline private val offlineRepository: LocalMediaRepository,
|
||||||
@Online private val onlineRepository: MediaRepository,
|
@Online private val onlineRepository: LocalMediaRepository,
|
||||||
) : MediaRepository {
|
) : LocalMediaRepository {
|
||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
// TODO move this into the domain layer and there you can use NetworkMonitor. Data should be free of android stuff.
|
// TODO move this into the domain layer and there you can use NetworkMonitor. Data should be free of android stuff.
|
||||||
private val activeRepository: Flow<MediaRepository> = flowOf(onlineRepository)
|
private val activeRepository: Flow<LocalMediaRepository> = flowOf(onlineRepository)
|
||||||
|
|
||||||
override val movies: StateFlow<Map<UUID, Movie>> = activeRepository
|
override val movies: StateFlow<Map<UUID, Movie>> = activeRepository
|
||||||
.flatMapLatest { it.movies }
|
.flatMapLatest { it.movies }
|
||||||
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
interface MediaRepository : MediaProgressWriter {
|
interface LocalMediaRepository : MediaProgressWriter {
|
||||||
val movies: StateFlow<Map<UUID, Movie>>
|
val movies: StateFlow<Map<UUID, Movie>>
|
||||||
val series: StateFlow<Map<UUID, Series>>
|
val series: StateFlow<Map<UUID, Series>>
|
||||||
val episodes: StateFlow<Map<UUID, Episode>>
|
val episodes: StateFlow<Map<UUID, Episode>>
|
||||||
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.HomeRepository
|
import hu.bbara.purefin.data.HomeRepository
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.data.UserSessionRepository
|
import hu.bbara.purefin.data.UserSessionRepository
|
||||||
import hu.bbara.purefin.download.MediaDownloadController
|
import hu.bbara.purefin.download.MediaDownloadController
|
||||||
import hu.bbara.purefin.model.LibraryKind
|
import hu.bbara.purefin.model.LibraryKind
|
||||||
@@ -34,7 +34,7 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class AppViewModel @Inject constructor(
|
class AppViewModel @Inject constructor(
|
||||||
private val homeRepository: HomeRepository,
|
private val homeRepository: HomeRepository,
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package hu.bbara.purefin.feature.content.episode
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.download.DownloadState
|
import hu.bbara.purefin.download.DownloadState
|
||||||
import hu.bbara.purefin.download.MediaDownloadController
|
import hu.bbara.purefin.download.MediaDownloadController
|
||||||
import hu.bbara.purefin.model.Episode
|
import hu.bbara.purefin.model.Episode
|
||||||
@@ -22,7 +22,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EpisodeScreenViewModel @Inject constructor(
|
class EpisodeScreenViewModel @Inject constructor(
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package hu.bbara.purefin.feature.content.movie
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.download.DownloadState
|
import hu.bbara.purefin.download.DownloadState
|
||||||
import hu.bbara.purefin.download.MediaDownloadController
|
import hu.bbara.purefin.download.MediaDownloadController
|
||||||
import hu.bbara.purefin.model.Movie
|
import hu.bbara.purefin.model.Movie
|
||||||
@@ -21,7 +21,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MovieScreenViewModel @Inject constructor(
|
class MovieScreenViewModel @Inject constructor(
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package hu.bbara.purefin.feature.content.series
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.download.DownloadState
|
import hu.bbara.purefin.download.DownloadState
|
||||||
import hu.bbara.purefin.download.MediaDownloadController
|
import hu.bbara.purefin.download.MediaDownloadController
|
||||||
import hu.bbara.purefin.model.Episode
|
import hu.bbara.purefin.model.Episode
|
||||||
@@ -25,7 +25,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SeriesViewModel @Inject constructor(
|
class SeriesViewModel @Inject constructor(
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.Offline
|
import hu.bbara.purefin.Offline
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.download.MediaDownloadController
|
import hu.bbara.purefin.download.MediaDownloadController
|
||||||
import hu.bbara.purefin.image.ArtworkKind
|
import hu.bbara.purefin.image.ArtworkKind
|
||||||
import hu.bbara.purefin.image.ImageUrlBuilder
|
import hu.bbara.purefin.image.ImageUrlBuilder
|
||||||
@@ -23,7 +23,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class DownloadsViewModel @Inject constructor(
|
class DownloadsViewModel @Inject constructor(
|
||||||
@Offline private val offlineCatalogReader: MediaRepository,
|
@Offline private val offlineCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val downloadManager: MediaDownloadController,
|
private val downloadManager: MediaDownloadController,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package hu.bbara.purefin.feature.search
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.data.UserSessionRepository
|
import hu.bbara.purefin.data.UserSessionRepository
|
||||||
import hu.bbara.purefin.image.ArtworkKind
|
import hu.bbara.purefin.image.ArtworkKind
|
||||||
import hu.bbara.purefin.image.ImageUrlBuilder
|
import hu.bbara.purefin.image.ImageUrlBuilder
|
||||||
@@ -26,7 +26,7 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
class SearchViewModel @Inject constructor(
|
class SearchViewModel @Inject constructor(
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import dagger.Binds
|
|||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import hu.bbara.purefin.data.CompositeMediaRepository
|
import hu.bbara.purefin.data.CompositeLocalMediaRepository
|
||||||
import hu.bbara.purefin.data.MediaProgressWriter
|
import hu.bbara.purefin.data.MediaProgressWriter
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -14,8 +14,8 @@ import hu.bbara.purefin.data.MediaRepository
|
|||||||
abstract class RepositoryModule {
|
abstract class RepositoryModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindMediaRepository(impl: CompositeMediaRepository): MediaRepository
|
abstract fun bindMediaRepository(impl: CompositeLocalMediaRepository): LocalMediaRepository
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindMediaProgressWrite(impl: CompositeMediaRepository): MediaProgressWriter
|
abstract fun bindMediaProgressWrite(impl: CompositeLocalMediaRepository): MediaProgressWriter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import androidx.lifecycle.SavedStateHandle
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.image.ArtworkKind
|
import hu.bbara.purefin.image.ArtworkKind
|
||||||
import hu.bbara.purefin.image.ImageUrlBuilder
|
import hu.bbara.purefin.image.ImageUrlBuilder
|
||||||
import hu.bbara.purefin.model.PlayableMedia
|
import hu.bbara.purefin.model.PlayableMedia
|
||||||
@@ -31,7 +31,7 @@ import javax.inject.Inject
|
|||||||
class PlayerViewModel @Inject constructor(
|
class PlayerViewModel @Inject constructor(
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
private val playerManager: PlayerManager,
|
private val playerManager: PlayerManager,
|
||||||
private val mediaCatalogReader: MediaRepository,
|
private val mediaCatalogReader: LocalMediaRepository,
|
||||||
private val progressManager: ProgressManager,
|
private val progressManager: ProgressManager,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class InMemoryAppContentRepository @Inject constructor(
|
|||||||
val userSessionRepository: UserSessionRepository,
|
val userSessionRepository: UserSessionRepository,
|
||||||
val jellyfinApiClient: JellyfinApiClient,
|
val jellyfinApiClient: JellyfinApiClient,
|
||||||
private val homeCacheDataStore: DataStore<HomeCache>,
|
private val homeCacheDataStore: DataStore<HomeCache>,
|
||||||
private val onlineMediaRepository: InMemoryMediaRepository,
|
private val onlineMediaRepository: InMemoryLocalMediaRepository,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
) : HomeRepository {
|
) : HomeRepository {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package hu.bbara.purefin.data.catalog
|
package hu.bbara.purefin.data.catalog
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.data.UserSessionRepository
|
import hu.bbara.purefin.data.UserSessionRepository
|
||||||
import hu.bbara.purefin.data.converter.toEpisode
|
import hu.bbara.purefin.data.converter.toEpisode
|
||||||
import hu.bbara.purefin.data.converter.toMovie
|
import hu.bbara.purefin.data.converter.toMovie
|
||||||
@@ -31,10 +31,10 @@ import javax.inject.Inject
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class InMemoryMediaRepository @Inject constructor(
|
class InMemoryLocalMediaRepository @Inject constructor(
|
||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
private val jellyfinApiClient: JellyfinApiClient,
|
private val jellyfinApiClient: JellyfinApiClient,
|
||||||
) : MediaRepository {
|
) : LocalMediaRepository {
|
||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package hu.bbara.purefin.data.catalog
|
package hu.bbara.purefin.data.catalog
|
||||||
|
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.data.offline.room.offline.OfflineRoomMediaLocalDataSource
|
import hu.bbara.purefin.data.offline.room.offline.OfflineRoomMediaLocalDataSource
|
||||||
import hu.bbara.purefin.model.Episode
|
import hu.bbara.purefin.model.Episode
|
||||||
import hu.bbara.purefin.model.Genre
|
import hu.bbara.purefin.model.Genre
|
||||||
@@ -20,9 +20,9 @@ import javax.inject.Inject
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class OfflineMediaRepository @Inject constructor(
|
class OfflineLocalMediaRepository @Inject constructor(
|
||||||
private val localDataSource: OfflineRoomMediaLocalDataSource,
|
private val localDataSource: OfflineRoomMediaLocalDataSource,
|
||||||
) : MediaRepository {
|
) : LocalMediaRepository {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||||
@@ -107,6 +107,36 @@ class JellyfinApiClient @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun searchBySearchTerm(searchTerm: String): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
||||||
|
logApiFailure("searchBySearchTerm") {
|
||||||
|
if (!ensureConfigured()) {
|
||||||
|
return@logApiFailure emptyList()
|
||||||
|
}
|
||||||
|
val response = api.itemsApi.getItems(
|
||||||
|
userId = getUserId(),
|
||||||
|
includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.SERIES),
|
||||||
|
searchTerm = searchTerm,
|
||||||
|
)
|
||||||
|
Log.d("searchBySearchTerm", response.content.toString())
|
||||||
|
response.content.items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun searchByGenre(genres: List<String>): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
||||||
|
logApiFailure("searchMovie") {
|
||||||
|
if (!ensureConfigured()) {
|
||||||
|
return@logApiFailure emptyList()
|
||||||
|
}
|
||||||
|
val response = api.itemsApi.getItems(
|
||||||
|
userId = getUserId(),
|
||||||
|
includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.SERIES),
|
||||||
|
genres = genres
|
||||||
|
)
|
||||||
|
Log.d("searchByGenre", response.content.toString())
|
||||||
|
response.content.items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getLibraries(): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
suspend fun getLibraries(): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
||||||
logApiFailure("getLibraries") {
|
logApiFailure("getLibraries") {
|
||||||
if (!ensureConfigured()) {
|
if (!ensureConfigured()) {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import dagger.hilt.components.SingletonComponent
|
|||||||
import hu.bbara.purefin.Offline
|
import hu.bbara.purefin.Offline
|
||||||
import hu.bbara.purefin.Online
|
import hu.bbara.purefin.Online
|
||||||
import hu.bbara.purefin.data.HomeRepository
|
import hu.bbara.purefin.data.HomeRepository
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.data.catalog.InMemoryAppContentRepository
|
import hu.bbara.purefin.data.catalog.InMemoryAppContentRepository
|
||||||
import hu.bbara.purefin.data.catalog.InMemoryMediaRepository
|
import hu.bbara.purefin.data.catalog.InMemoryLocalMediaRepository
|
||||||
import hu.bbara.purefin.data.catalog.OfflineMediaRepository
|
import hu.bbara.purefin.data.catalog.OfflineLocalMediaRepository
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
@@ -18,11 +18,11 @@ abstract class MediaRepositoryModule {
|
|||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Online
|
@Online
|
||||||
abstract fun bindOnlineRepository(impl: InMemoryMediaRepository): MediaRepository
|
abstract fun bindOnlineRepository(impl: InMemoryLocalMediaRepository): LocalMediaRepository
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Offline
|
@Offline
|
||||||
abstract fun bindOfflineRepository(impl: OfflineMediaRepository): MediaRepository
|
abstract fun bindOfflineRepository(impl: OfflineLocalMediaRepository): LocalMediaRepository
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindHomeRepository(impl: InMemoryAppContentRepository): HomeRepository
|
abstract fun bindHomeRepository(impl: InMemoryAppContentRepository): HomeRepository
|
||||||
|
|||||||
Reference in New Issue
Block a user