You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
manager: some minor improvemnets
This commit is contained in:
@@ -268,14 +268,14 @@ private fun StatusCard(
|
|||||||
text = stringResource(R.string.home_module_count, getModuleCount()),
|
text = stringResource(R.string.home_module_count, getModuleCount()),
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
// Spacer(Modifier.height(4.dp))
|
||||||
val suSFS = getSuSFS()
|
// val suSFS = getSuSFS()
|
||||||
if (suSFS == "Supported") {
|
// if (suSFS == "Supported") {
|
||||||
Text(
|
// Text(
|
||||||
text = stringResource(R.string.home_susfs, getSuSFS()),
|
// text = stringResource(R.string.home_susfs, getSuSFS()),
|
||||||
style = MaterialTheme.typography.bodyMedium
|
// style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,7 +399,7 @@ private fun InfoCard() {
|
|||||||
val managerVersion = getManagerVersion(context)
|
val managerVersion = getManagerVersion(context)
|
||||||
InfoCardItem(
|
InfoCardItem(
|
||||||
label = stringResource(R.string.home_manager_version),
|
label = stringResource(R.string.home_manager_version),
|
||||||
content = "${managerVersion.first}-next (${managerVersion.second})",
|
content = "${managerVersion.first} (${managerVersion.second})",
|
||||||
icon = painterResource(R.drawable.ic_ksu_next),
|
icon = painterResource(R.drawable.ic_ksu_next),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -409,19 +409,26 @@ private fun InfoCard() {
|
|||||||
content = getSELinuxStatus(),
|
content = getSELinuxStatus(),
|
||||||
icon = Icons.Filled.Security,
|
icon = Icons.Filled.Security,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
|
||||||
val suSFS = getSuSFS()
|
|
||||||
|
|
||||||
if (suSFS == "Supported") {
|
Spacer(Modifier.height(16.dp))
|
||||||
val susSUMode = if (isSUS_SU) "| SuS SU mode: ${susfsSUS_SU_Mode()}" else ""
|
InfoCardItem(
|
||||||
InfoCardItem(
|
label = stringResource(R.string.home_module_mount),
|
||||||
label = stringResource(R.string.home_susfs_version),
|
content = stringResource(R.string.home_magic_mount),
|
||||||
content = "${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
|
icon = Icons.Filled.SettingsSuggest,
|
||||||
icon = painterResource(R.drawable.ic_sus),
|
)
|
||||||
)
|
|
||||||
}
|
// Spacer(Modifier.height(16.dp))
|
||||||
|
// val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
||||||
|
// val suSFS = getSuSFS()
|
||||||
|
|
||||||
|
// if (suSFS == "Supported") {
|
||||||
|
// val susSUMode = if (isSUS_SU) "| SuS SU mode: ${susfsSUS_SU_Mode()}" else ""
|
||||||
|
// InfoCardItem(
|
||||||
|
// label = stringResource(R.string.home_susfs_version),
|
||||||
|
// content = "${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
|
||||||
|
// icon = painterResource(R.drawable.ic_sus),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ private fun ModuleList(
|
|||||||
|
|
||||||
val success = loadingDialog.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
uninstallModule(module.id)
|
uninstallModule(module.dirId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ private fun ModuleList(
|
|||||||
|
|
||||||
val success = loadingDialog.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
restoreModule(module.id)
|
restoreModule(module.dirId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +489,7 @@ private fun ModuleList(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
!viewModel.isOverlayAvailable -> {
|
!viewModel.isDummy -> {
|
||||||
item {
|
item {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillParentMaxSize(),
|
modifier = Modifier.fillParentMaxSize(),
|
||||||
@@ -542,7 +542,7 @@ private fun ModuleList(
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val success = loadingDialog.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
toggleModule(module.id, !isChecked)
|
toggleModule(module.dirId, !isChecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -705,7 +705,7 @@ fun ModuleItem(
|
|||||||
FilledTonalButton(
|
FilledTonalButton(
|
||||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
navigator.navigate(ExecuteModuleActionScreenDestination(module.id))
|
navigator.navigate(ExecuteModuleActionScreenDestination(module.dirId))
|
||||||
viewModel.markNeedRefresh()
|
viewModel.markNeedRefresh()
|
||||||
},
|
},
|
||||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||||
@@ -840,7 +840,8 @@ fun ModuleItemPreview() {
|
|||||||
remove = false,
|
remove = false,
|
||||||
updateJson = "",
|
updateJson = "",
|
||||||
hasWebUi = false,
|
hasWebUi = false,
|
||||||
hasActionScript = false
|
hasActionScript = false,
|
||||||
|
dirId = "dirId"
|
||||||
)
|
)
|
||||||
ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}, {})
|
ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}, {})
|
||||||
}
|
}
|
||||||
@@ -160,31 +160,31 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
|
|
||||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
val isSUS_SU = getSuSFSFeatures()
|
// val isSUS_SU = getSuSFSFeatures()
|
||||||
if (isSUS_SU == "CONFIG_KSU_SUSFS_SUS_SU") {
|
// if (isSUS_SU == "CONFIG_KSU_SUSFS_SUS_SU") {
|
||||||
var isEnabled by rememberSaveable {
|
// var isEnabled by rememberSaveable {
|
||||||
mutableStateOf(susfsSUS_SU_Mode() == "2")
|
// mutableStateOf(susfsSUS_SU_Mode() == "2")
|
||||||
}
|
// }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
// LaunchedEffect(Unit) {
|
||||||
isEnabled = susfsSUS_SU_Mode() == "2"
|
// isEnabled = susfsSUS_SU_Mode() == "2"
|
||||||
}
|
// }
|
||||||
|
|
||||||
SwitchItem(
|
// SwitchItem(
|
||||||
icon = Icons.Filled.VisibilityOff,
|
// icon = Icons.Filled.VisibilityOff,
|
||||||
title = stringResource(id = R.string.settings_susfs_toggle),
|
// title = stringResource(id = R.string.settings_susfs_toggle),
|
||||||
summary = stringResource(id = R.string.settings_susfs_toggle_summary),
|
// summary = stringResource(id = R.string.settings_susfs_toggle_summary),
|
||||||
checked = isEnabled
|
// checked = isEnabled
|
||||||
) {
|
// ) {
|
||||||
if (it) {
|
// if (it) {
|
||||||
susfsSUS_SU_2()
|
// susfsSUS_SU_2()
|
||||||
} else {
|
// } else {
|
||||||
susfsSUS_SU_0()
|
// susfsSUS_SU_0()
|
||||||
}
|
// }
|
||||||
prefs.edit().putBoolean("enable_sus_su", it).apply()
|
// prefs.edit().putBoolean("enable_sus_su", it).apply()
|
||||||
isEnabled = it
|
// isEnabled = it
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
var checkUpdate by rememberSaveable {
|
var checkUpdate by rememberSaveable {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
|
|||||||
@@ -120,50 +120,50 @@ fun getModuleCount(): Int {
|
|||||||
}.getOrElse { return 0 }
|
}.getOrElse { return 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSuSFSDaemonPath(): String {
|
// private fun getSuSFSDaemonPath(): String {
|
||||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libsusfsd.so"
|
// return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libsusfsd.so"
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun getSuSFS(): String {
|
// fun getSuSFS(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} support")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} support")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun getSuSFSVersion(): String {
|
// fun getSuSFSVersion(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} version")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} version")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun getSuSFSVariant(): String {
|
// fun getSuSFSVariant(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
fun getSuSFSFeatures(): String {
|
// fun getSuSFSFeatures(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun susfsSUS_SU_0(): String {
|
// fun susfsSUS_SU_0(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 0")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 0")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun susfsSUS_SU_2(): String {
|
// fun susfsSUS_SU_2(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 2")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 2")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun susfsSUS_SU_Mode(): String {
|
// fun susfsSUS_SU_Mode(): String {
|
||||||
val shell = getRootShell()
|
// val shell = getRootShell()
|
||||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode")
|
// val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode")
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun getSuperuserCount(): Int {
|
fun getSuperuserCount(): Int {
|
||||||
return Natives.allowList.size
|
return Natives.allowList.size
|
||||||
@@ -403,10 +403,12 @@ suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
|
|||||||
out.filter { it.isNotBlank() }.map { it.trim() }
|
out.filter { it.isNotBlank() }.map { it.trim() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun overlayFsAvailable(): Boolean {
|
fun hasDummy(): Boolean {
|
||||||
val shell = getRootShell()
|
//fun overlayFsAvailable(): Boolean {
|
||||||
// check /proc/filesystems
|
// val shell = getRootShell()
|
||||||
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
|
// // check /proc/filesystems
|
||||||
|
// return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasMagisk(): Boolean {
|
fun hasMagisk(): Boolean {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import com.rifsxd.ksunext.ui.util.listModules
|
import com.rifsxd.ksunext.ui.util.listModules
|
||||||
import com.rifsxd.ksunext.ui.util.overlayFsAvailable
|
import com.rifsxd.ksunext.ui.util.hasDummy
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
val updateJson: String,
|
val updateJson: String,
|
||||||
val hasWebUi: Boolean,
|
val hasWebUi: Boolean,
|
||||||
val hasActionScript: Boolean,
|
val hasActionScript: Boolean,
|
||||||
|
val dirId: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ModuleUpdateInfo(
|
data class ModuleUpdateInfo(
|
||||||
@@ -44,7 +45,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
val changelog: String,
|
val changelog: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
|
var isDummy by mutableStateOf(hasDummy())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
@@ -79,7 +80,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
val start = SystemClock.elapsedRealtime()
|
val start = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
isOverlayAvailable = overlayFsAvailable()
|
isDummy = hasDummy()
|
||||||
|
|
||||||
val result = listModules()
|
val result = listModules()
|
||||||
|
|
||||||
@@ -102,7 +103,8 @@ class ModuleViewModel : ViewModel() {
|
|||||||
obj.getBoolean("remove"),
|
obj.getBoolean("remove"),
|
||||||
obj.optString("updateJson"),
|
obj.optString("updateJson"),
|
||||||
obj.optBoolean("web"),
|
obj.optBoolean("web"),
|
||||||
obj.optBoolean("action")
|
obj.optBoolean("action"),
|
||||||
|
obj.getString("dir_id")
|
||||||
)
|
)
|
||||||
}.toList()
|
}.toList()
|
||||||
isNeedRefresh = false
|
isNeedRefresh = false
|
||||||
|
|||||||
@@ -63,6 +63,8 @@
|
|||||||
<string name="safe_mode">Mode aman</string>
|
<string name="safe_mode">Mode aman</string>
|
||||||
<string name="reboot_to_apply">Reboot agar berfungsi</string>
|
<string name="reboot_to_apply">Reboot agar berfungsi</string>
|
||||||
<string name="module_magisk_conflict">Konflik dengan Magisk, fungsi modul ditiadakan!</string>
|
<string name="module_magisk_conflict">Konflik dengan Magisk, fungsi modul ditiadakan!</string>
|
||||||
|
<string name="home_module_mount">Modul Sistem</string>
|
||||||
|
<string name="home_magic_mount">Magic Mount</string>
|
||||||
<string name="home_next_kernelsu">🔥 Pembangunan Next</string>
|
<string name="home_next_kernelsu">🔥 Pembangunan Next</string>
|
||||||
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
||||||
<string name="home_next_kernelsu_body">Next cabang eksperimental. Lihat di GitHub!</string>
|
<string name="home_next_kernelsu_body">Next cabang eksperimental. Lihat di GitHub!</string>
|
||||||
|
|||||||
@@ -64,6 +64,8 @@
|
|||||||
<string name="send_log">发送日志</string>
|
<string name="send_log">发送日志</string>
|
||||||
<string name="safe_mode">安全模式</string>
|
<string name="safe_mode">安全模式</string>
|
||||||
<string name="reboot_to_apply">重启生效</string>
|
<string name="reboot_to_apply">重启生效</string>
|
||||||
|
<string name="home_module_mount">模組系統</string>
|
||||||
|
<string name="home_magic_mount">魔法坐騎</string>
|
||||||
<string name="module_magisk_conflict">因与Magisk有冲突,所有模块不可用!</string>
|
<string name="module_magisk_conflict">因与Magisk有冲突,所有模块不可用!</string>
|
||||||
<string name="home_next_kernelsu">🔥 Next 构建</string>
|
<string name="home_next_kernelsu">🔥 Next 构建</string>
|
||||||
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
||||||
|
|||||||
@@ -18,13 +18,13 @@
|
|||||||
<string name="home_module_count">Modules: %d</string>
|
<string name="home_module_count">Modules: %d</string>
|
||||||
<string name="home_failure">KernelSU Next v2 signature not found in kernel! [ !KSU_NEXT || != size/hash ]</string>
|
<string name="home_failure">KernelSU Next v2 signature not found in kernel! [ !KSU_NEXT || != size/hash ]</string>
|
||||||
<string name="home_failure_tip">Ask your kernel developer to integrate KernelSU Next!</string>
|
<string name="home_failure_tip">Ask your kernel developer to integrate KernelSU Next!</string>
|
||||||
<string name="home_kernel">Kernel</string>
|
<string name="home_kernel">Kernel Version</string>
|
||||||
<string name="home_susfs">SuSFS: %s</string>
|
<string name="home_susfs">SuSFS: %s</string>
|
||||||
<string name="home_susfs_version">SuSFS</string>
|
<string name="home_susfs_version">SuSFS</string>
|
||||||
<string name="home_susfs_sus_su">SuS SU</string>
|
<string name="home_susfs_sus_su">SuS SU</string>
|
||||||
<string name="home_android">Android</string>
|
<string name="home_android">Android Version</string>
|
||||||
<string name="home_manager_version">Manager</string>
|
<string name="home_manager_version">Manager Version</string>
|
||||||
<string name="home_selinux_status">SELinux</string>
|
<string name="home_selinux_status">SELinux Mode</string>
|
||||||
<string name="selinux_status_disabled">Disabled</string>
|
<string name="selinux_status_disabled">Disabled</string>
|
||||||
<string name="selinux_status_enforcing">Enforcing</string>
|
<string name="selinux_status_enforcing">Enforcing</string>
|
||||||
<string name="selinux_status_permissive">Permissive</string>
|
<string name="selinux_status_permissive">Permissive</string>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<string name="module_failed_to_enable">Failed to enable module: %s</string>
|
<string name="module_failed_to_enable">Failed to enable module: %s</string>
|
||||||
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
||||||
<string name="module_empty">No module installed</string>
|
<string name="module_empty">No module installed</string>
|
||||||
<string name="module">Module</string>
|
<string name="module">Module System</string>
|
||||||
<string name="confirm_module_installation">Confirm Installation</string>
|
<string name="confirm_module_installation">Confirm Installation</string>
|
||||||
<string name="module_install_prompt_with_name">Do you want to continue installing module %1$s?</string>
|
<string name="module_install_prompt_with_name">Do you want to continue installing module %1$s?</string>
|
||||||
<string name="module_sort_action_first">Sort (Action First)</string>
|
<string name="module_sort_action_first">Sort (Action First)</string>
|
||||||
@@ -66,6 +66,8 @@
|
|||||||
<string name="safe_mode">Safe mode</string>
|
<string name="safe_mode">Safe mode</string>
|
||||||
<string name="reboot_to_apply">Reboot to take effect</string>
|
<string name="reboot_to_apply">Reboot to take effect</string>
|
||||||
<string name="module_magisk_conflict">Modules are unavailable due to a conflict with Magisk!</string>
|
<string name="module_magisk_conflict">Modules are unavailable due to a conflict with Magisk!</string>
|
||||||
|
<string name="home_module_mount">Module System</string>
|
||||||
|
<string name="home_magic_mount">Magic Mount</string>
|
||||||
<string name="home_next_kernelsu">🔥 Next Build</string>
|
<string name="home_next_kernelsu">🔥 Next Build</string>
|
||||||
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
||||||
<string name="home_next_kernelsu_body">Next experimental branch. Check it out on GitHub!</string>
|
<string name="home_next_kernelsu_body">Next experimental branch. Check it out on GitHub!</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user