mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 03:41:50 +01:00
Option to collaspe categories
Sort button on category now shows category direction Category name now a bit larger
This commit is contained in:
parent
3d6ad28437
commit
877cb43043
@ -25,6 +25,8 @@ interface Category : Serializable {
|
||||
val nameLower: String
|
||||
get() = name.toLowerCase()
|
||||
|
||||
var isHidden: Boolean
|
||||
|
||||
fun isAscending(): Boolean {
|
||||
return ((mangaSort?.minus('a') ?: 0) % 2) != 1
|
||||
}
|
||||
@ -47,7 +49,7 @@ interface Category : Serializable {
|
||||
LAST_READ_ASC, LAST_READ_DSC -> R.string.last_read
|
||||
TOTAL_ASC, TOTAL_DSC -> R.string.total_chapters
|
||||
DATE_ADDED_ASC, DATE_ADDED_DSC -> R.string.date_added
|
||||
else -> R.string.drag_and_drop
|
||||
else -> if (id == -1) R.string.category else R.string.drag_and_drop
|
||||
}
|
||||
|
||||
fun catSortingMode(): Int? = when (mangaSort) {
|
||||
|
@ -18,6 +18,8 @@ class CategoryImpl : Category {
|
||||
|
||||
override var isLast: Boolean? = null
|
||||
|
||||
override var isHidden: Boolean = false
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
@ -14,5 +14,9 @@ class LibraryManga : MangaImpl() {
|
||||
id = Long.MIN_VALUE
|
||||
category = categoryId
|
||||
}
|
||||
|
||||
fun createHide(categoryId: Int): LibraryManga = createBlank(categoryId).apply {
|
||||
status = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,10 +51,6 @@ object PreferenceKeys {
|
||||
|
||||
const val readWithVolumeKeysInverted = "reader_volume_keys_inverted"
|
||||
|
||||
const val portraitColumns = "pref_library_columns_portrait_key"
|
||||
|
||||
const val landscapeColumns = "pref_library_columns_landscape_key"
|
||||
|
||||
const val updateOnlyNonCompleted = "pref_update_only_non_completed_key"
|
||||
|
||||
const val autoUpdateTrack = "pref_auto_update_manga_sync_key"
|
||||
|
@ -103,10 +103,6 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun readWithVolumeKeysInverted() = rxPrefs.getBoolean(Keys.readWithVolumeKeysInverted, false)
|
||||
|
||||
fun portraitColumns() = rxPrefs.getInteger(Keys.portraitColumns, 0)
|
||||
|
||||
fun landscapeColumns() = rxPrefs.getInteger(Keys.landscapeColumns, 0)
|
||||
|
||||
fun updateOnlyNonCompleted() = prefs.getBoolean(Keys.updateOnlyNonCompleted, false)
|
||||
|
||||
fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
|
||||
@ -205,6 +201,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun automaticExtUpdates() = rxPrefs.getBoolean(Keys.automaticExtUpdates, false)
|
||||
|
||||
fun collapsedCategories() = rxPrefs.getStringSet("collapsed_categories", mutableSetOf())
|
||||
|
||||
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf())
|
||||
|
||||
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
|
||||
|
@ -153,7 +153,7 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
}
|
||||
|
||||
private fun getFirstLetter(name: String): String {
|
||||
val letter = name.first()
|
||||
val letter = name.firstOrNull() ?: '#'
|
||||
return if (letter.isLetter()) letter.toString()
|
||||
.toUpperCase(Locale.ROOT) else "#"
|
||||
}
|
||||
@ -272,5 +272,6 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
fun sortCategory(catId: Int, sortBy: Int)
|
||||
fun selectAll(position: Int)
|
||||
fun allSelected(position: Int): Boolean
|
||||
fun toggleCategoryVisibility(position: Int)
|
||||
}
|
||||
}
|
||||
|
@ -804,6 +804,11 @@ class LibraryController(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toggleCategoryVisibility(position: Int) {
|
||||
val catId = (adapter.getItem(position) as? LibraryHeaderItem)?.category?.id ?: return
|
||||
presenter.toggleCategoryVisibility(catId)
|
||||
}
|
||||
|
||||
override fun sortCategory(catId: Int, sortBy: Int) {
|
||||
presenter.sortCategory(catId, sortBy)
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
@ -30,7 +30,6 @@ import eu.kanade.tachiyomi.util.view.invisible
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.visInvisIf
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import kotlinx.android.synthetic.main.library_category_header_item.view.*
|
||||
|
||||
class LibraryHeaderItem(
|
||||
private val categoryF: (Int) -> Category,
|
||||
@ -83,7 +82,7 @@ class LibraryHeaderItem(
|
||||
}
|
||||
|
||||
class Holder(val view: View, private val adapter: LibraryCategoryAdapter, padEnd: Boolean) :
|
||||
FlexibleViewHolder(view, adapter, true) {
|
||||
BaseFlexibleViewHolder(view, adapter, true) {
|
||||
|
||||
private val sectionText: TextView = view.findViewById(R.id.category_title)
|
||||
private val sortText: TextView = view.findViewById(R.id.category_sort)
|
||||
@ -96,6 +95,10 @@ class LibraryHeaderItem(
|
||||
marginEnd = (if (padEnd && adapter.recyclerView.paddingEnd == 0) 12 else 2).dpToPx
|
||||
}
|
||||
updateButton.setOnClickListener { addCategoryToUpdate() }
|
||||
sectionText.setOnLongClickListener {
|
||||
adapter.libraryListener.toggleCategoryVisibility(adapterPosition)
|
||||
true
|
||||
}
|
||||
sortText.setOnClickListener { it.post { showCatSortOptions() } }
|
||||
checkboxImage.setOnClickListener { selectAll() }
|
||||
updateButton.drawable.mutate()
|
||||
@ -109,20 +112,25 @@ class LibraryHeaderItem(
|
||||
if (category.isFirst == true && category.isLast == true) sectionText.text = ""
|
||||
else sectionText.text = category.name
|
||||
sortText.text = itemView.context.getString(R.string.sort_by_,
|
||||
itemView.context.getString(
|
||||
when (category.sortingMode()) {
|
||||
LibrarySort.LATEST_CHAPTER -> R.string.latest_chapter
|
||||
LibrarySort.DRAG_AND_DROP ->
|
||||
if (category.id == -1) R.string.category
|
||||
else R.string.drag_and_drop
|
||||
LibrarySort.TOTAL -> R.string.total_chapters
|
||||
LibrarySort.UNREAD -> R.string.unread
|
||||
LibrarySort.LAST_READ -> R.string.last_read
|
||||
LibrarySort.ALPHA -> R.string.title
|
||||
LibrarySort.DATE_ADDED -> R.string.date_added
|
||||
else -> R.string.drag_and_drop
|
||||
itemView.context.getString(category.sortRes())
|
||||
)
|
||||
|
||||
val isAscending = category.isAscending()
|
||||
val sortingMode = category.sortingMode()
|
||||
val sortDrawable = if (category.isHidden) R.drawable.ic_expand_more_white_24dp
|
||||
else
|
||||
when {
|
||||
sortingMode == LibrarySort.DRAG_AND_DROP || sortingMode == null -> R.drawable
|
||||
.ic_sort_white_24dp
|
||||
if (sortingMode == LibrarySort.DATE_ADDED ||
|
||||
sortingMode == LibrarySort.LATEST_CHAPTER ||
|
||||
sortingMode == LibrarySort.LAST_READ) !isAscending else isAscending ->
|
||||
R.drawable.ic_arrow_down_white_24dp
|
||||
else -> R.drawable.ic_arrow_up_white_24dp
|
||||
}
|
||||
))
|
||||
|
||||
sortText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, sortDrawable, 0)
|
||||
sortText.setText(if (category.isHidden) R.string.collasped else category.sortRes())
|
||||
|
||||
when {
|
||||
adapter.mode == SelectableAdapter.Mode.MULTI -> {
|
||||
@ -157,8 +165,12 @@ class LibraryHeaderItem(
|
||||
private fun showCatSortOptions() {
|
||||
val category =
|
||||
(adapter.getItem(adapterPosition) as? LibraryHeaderItem)?.category ?: return
|
||||
if (category.isHidden) {
|
||||
adapter.libraryListener.toggleCategoryVisibility(adapterPosition)
|
||||
return
|
||||
}
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
val popup = PopupMenu(itemView.context, view.category_sort)
|
||||
val popup = PopupMenu(itemView.context, sortText)
|
||||
|
||||
// Inflate our menu resource into the PopupMenu's Menu
|
||||
popup.menuInflater.inflate(
|
||||
@ -254,14 +266,17 @@ class LibraryHeaderItem(
|
||||
|
||||
fun setSelection() {
|
||||
val allSelected = adapter.libraryListener.allSelected(adapterPosition)
|
||||
val drawable =
|
||||
ContextCompat.getDrawable(contentView.context,
|
||||
if (allSelected) R.drawable.ic_check_circle_white_24dp else
|
||||
R.drawable.ic_radio_button_unchecked_white_24dp)
|
||||
val drawable = ContextCompat.getDrawable(
|
||||
contentView.context,
|
||||
if (allSelected) R.drawable.ic_check_circle_white_24dp else R.drawable.ic_radio_button_unchecked_white_24dp
|
||||
)
|
||||
val tintedDrawable = drawable?.mutate()
|
||||
tintedDrawable?.setTint(ContextCompat.getColor(contentView.context,
|
||||
if (allSelected) R.color.colorAccent
|
||||
else R.color.gray_button))
|
||||
tintedDrawable?.setTint(
|
||||
ContextCompat.getColor(
|
||||
contentView.context, if (allSelected) R.color.colorAccent
|
||||
else R.color.gray_button
|
||||
)
|
||||
)
|
||||
checkboxImage.setImageDrawable(tintedDrawable)
|
||||
}
|
||||
|
||||
|
@ -46,13 +46,23 @@ class LibraryListHolder(
|
||||
*/
|
||||
override fun onSetValues(item: LibraryItem) {
|
||||
|
||||
title.visible()
|
||||
constraint_layout.minHeight = 56.dpToPx
|
||||
if (item.manga.isBlank()) {
|
||||
title.text = itemView.context.getString(R.string.category_is_empty)
|
||||
constraint_layout.minHeight = 0
|
||||
if (item.manga.status == -1) {
|
||||
title.gone()
|
||||
} else
|
||||
title.text = itemView.context.getString(R.string.category_is_empty)
|
||||
title.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
card.gone()
|
||||
badge_view.gone()
|
||||
play_layout.gone()
|
||||
padding.gone()
|
||||
subtitle.gone()
|
||||
return
|
||||
}
|
||||
padding.visible()
|
||||
card.visible()
|
||||
title.textAlignment = View.TEXT_ALIGNMENT_TEXT_START
|
||||
|
||||
|
@ -425,12 +425,27 @@ class LibraryPresenter(
|
||||
LibraryItem(it, libraryLayout, preferences.uniformGrid(), seekPref, headerItem)
|
||||
}.toMutableList()
|
||||
|
||||
val categoriesHidden = preferences.collapsedCategories().getOrDefault().mapNotNull {
|
||||
it.toIntOrNull()
|
||||
}.toMutableSet()
|
||||
|
||||
if (showCategories) {
|
||||
categories.forEach { category ->
|
||||
if (category.id ?: 0 <= 0 && !categorySet.contains(category.id)) {
|
||||
val headerItem = headerItems[category.id ?: 0]
|
||||
val catId = category.id ?: return@forEach
|
||||
if (catId > 0 && !categorySet.contains(catId)) {
|
||||
val headerItem = headerItems[catId]
|
||||
items.add(LibraryItem(
|
||||
LibraryManga.createBlank(category.id!!),
|
||||
LibraryManga.createBlank(catId),
|
||||
libraryLayout,
|
||||
preferences.uniformGrid(),
|
||||
preferences.alwaysShowSeeker(),
|
||||
headerItem
|
||||
))
|
||||
} else if (catId in categoriesHidden) {
|
||||
val headerItem = headerItems[catId]
|
||||
items.removeAll { it.manga.category == catId }
|
||||
items.add(LibraryItem(
|
||||
LibraryManga.createHide(catId),
|
||||
libraryLayout,
|
||||
preferences.uniformGrid(),
|
||||
preferences.alwaysShowSeeker(),
|
||||
@ -446,6 +461,10 @@ class LibraryPresenter(
|
||||
if (categorySet.contains(0))
|
||||
categories.add(0, createDefaultCategory())
|
||||
|
||||
categories.forEach {
|
||||
it.isHidden = it.id in categoriesHidden
|
||||
}
|
||||
|
||||
this.allCategories = categories
|
||||
this.categories = if (!showCategories) arrayListOf(categoryAll)
|
||||
else categories
|
||||
@ -678,7 +697,21 @@ class LibraryPresenter(
|
||||
return catId in categories
|
||||
}
|
||||
|
||||
fun toggleCategoryVisibility(categoryId: Int) {
|
||||
if (categoryId <= -1) return
|
||||
val categoriesHidden = preferences.collapsedCategories().getOrDefault().mapNotNull {
|
||||
it.toIntOrNull()
|
||||
}.toMutableSet()
|
||||
if (categoryId in categoriesHidden)
|
||||
categoriesHidden.remove(categoryId)
|
||||
else
|
||||
categoriesHidden.add(categoryId)
|
||||
preferences.collapsedCategories().set(categoriesHidden.map { it.toString() }.toMutableSet())
|
||||
getLibrary()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// var catsHidden = mutableListOf<Int>()
|
||||
private var lastLibraryItems: List<LibraryItem>? = null
|
||||
private var lastCategories: List<Category>? = null
|
||||
|
||||
|
@ -521,7 +521,7 @@ class MangaDetailsPresenter(
|
||||
asyncUpdateMangaAndChapters()
|
||||
}
|
||||
|
||||
fun globalSort() = preferences.chaptersDescAsDefault().getOrDefault()
|
||||
fun globalSort(): Boolean = preferences.chaptersDescAsDefault().getOrDefault()
|
||||
|
||||
fun setGlobalChapterSort(descend: Boolean) {
|
||||
preferences.chaptersDescAsDefault().set(descend)
|
||||
|
@ -4,7 +4,7 @@
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/gray_button" />
|
||||
<solid android:color="@color/fullRippleColor" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
@ -5,6 +5,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:id="@+id/constraint_layout"
|
||||
android:background="?attr/selectable_list_drawable"
|
||||
android:minHeight="@dimen/material_component_lists_single_line_with_avatar_height">
|
||||
|
||||
|
@ -6,25 +6,23 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkbox"
|
||||
android:padding="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:tint="?attr/colorAccent"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/select_all"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/select_all"
|
||||
android:focusable="true"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_check_circle_white_24dp"
|
||||
android:layout_marginStart="2dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/category_title"
|
||||
app:layout_constraintEnd_toStartOf="@+id/category_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/category_title"
|
||||
/>
|
||||
tools:tint="?attr/colorAccent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_title"
|
||||
@ -32,18 +30,20 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/square_ripple"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center|start"
|
||||
android:inputType="none"
|
||||
android:maxLines="2"
|
||||
android:layout_marginTop="32dp"
|
||||
android:textSize="22sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/checkbox"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/update_button"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/checkbox"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title dfdsfsfsfsfsfdsfsfsfs" />
|
||||
|
||||
<Space
|
||||
@ -59,15 +59,15 @@
|
||||
android:id="@+id/update_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="35dp"
|
||||
android:padding="5dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:tint="?attr/colorAccent"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_refresh_white_24dp"
|
||||
android:tint="?attr/colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/category_title"
|
||||
app:layout_constraintTop_toTopOf="@id/category_title"
|
||||
app:layout_constraintEnd_toStartOf="@id/space"
|
||||
app:layout_constraintStart_toEndOf="@id/category_title"
|
||||
app:layout_constraintTop_toTopOf="@id/category_title"
|
||||
app:rippleColor="@color/fullRippleColor" />
|
||||
|
||||
<ProgressBar
|
||||
@ -76,17 +76,18 @@
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toTopOf="@id/update_button"
|
||||
app:layout_constraintBottom_toBottomOf="@id/update_button"
|
||||
app:layout_constraintEnd_toEndOf="@id/update_button"
|
||||
app:layout_constraintStart_toStartOf="@id/update_button"
|
||||
app:layout_constraintEnd_toEndOf="@id/update_button"/>
|
||||
app:layout_constraintTop_toTopOf="@id/update_button"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_sort"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:background="@drawable/square_ripple"
|
||||
android:clickable="true"
|
||||
android:drawableEnd="@drawable/ic_sort_white_24dp"
|
||||
android:drawablePadding="6dp"
|
||||
@ -94,7 +95,6 @@
|
||||
android:ellipsize="start"
|
||||
android:focusable="true"
|
||||
android:gravity="center|end"
|
||||
android:background="@drawable/square_ripple"
|
||||
android:maxLines="2"
|
||||
android:padding="6dp"
|
||||
android:textAlignment="textEnd"
|
||||
@ -108,6 +108,5 @@
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toEndOf="@id/space"
|
||||
app:layout_constraintTop_toTopOf="@id/category_title"
|
||||
app:layout_constraintWidth_min="100dp"
|
||||
tools:text="Drag and Drop" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -66,6 +66,7 @@
|
||||
<string name="_already_in_queue">%1$s is already in queue</string>
|
||||
<string name="create_new_category">Create new category</string>
|
||||
<string name="category_is_empty">Category is empty</string>
|
||||
<string name="category_is_hidden">Category is hidden</string>
|
||||
<string name="top_category">Top category (%1$s)</string>
|
||||
<string name="default_category">Default category</string>
|
||||
<string name="first_category">First category</string>
|
||||
@ -600,6 +601,7 @@
|
||||
<string name="charging">Charging</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="collasped">Collapsed</string>
|
||||
<string name="common">Common</string>
|
||||
<string name="cover_of_image">Cover of manga</string>
|
||||
<string name="create">Create</string>
|
||||
@ -637,6 +639,8 @@
|
||||
<string name="options">Options</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="picture_saved">Picture saved</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="refreshing">Refreshing</string>
|
||||
<string name="remove">Remove</string>
|
||||
<string name="reorder">Reorder</string>
|
||||
<string name="reset">Reset</string>
|
||||
@ -656,6 +660,7 @@
|
||||
<string name="top">Top</string>
|
||||
<string name="undo">Undo</string>
|
||||
<string name="unknown_error">Unknown error</string>
|
||||
<string name="un_select_all">Un-select all</string>
|
||||
<string name="use_default">Use default</string>
|
||||
<string name="view_all_errors">View all errors</string>
|
||||
<string name="view_chapters">View chapters</string>
|
||||
|
BIN
captures/eu.kanade.tachiyomi.debug_2020.04.16_14.54.li
Normal file
BIN
captures/eu.kanade.tachiyomi.debug_2020.04.16_14.54.li
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user