Merge branch 'view' into UI_2.0
This commit is contained in:
@@ -424,12 +424,10 @@ def callbackCheckMicThreshold(is_turned_on):
|
|||||||
view.replaceMicThresholdCheckButton_Disabled()
|
view.replaceMicThresholdCheckButton_Disabled()
|
||||||
model.startCheckMicEnergy(setProgressBarMicEnergy, view.initProgressBar_MicEnergy)
|
model.startCheckMicEnergy(setProgressBarMicEnergy, view.initProgressBar_MicEnergy)
|
||||||
view.replaceMicThresholdCheckButton_Active()
|
view.replaceMicThresholdCheckButton_Active()
|
||||||
view.setWidgetsStatus_ThresholdCheckButton_Normal()
|
|
||||||
else:
|
else:
|
||||||
view.replaceMicThresholdCheckButton_Disabled()
|
view.replaceMicThresholdCheckButton_Disabled()
|
||||||
model.stopCheckMicEnergy()
|
model.stopCheckMicEnergy()
|
||||||
view.replaceMicThresholdCheckButton_Passive()
|
view.replaceMicThresholdCheckButton_Passive()
|
||||||
view.setWidgetsStatus_ThresholdCheckButton_Normal()
|
|
||||||
|
|
||||||
def callbackSetMicRecordTimeout(value):
|
def callbackSetMicRecordTimeout(value):
|
||||||
print("callbackSetMicRecordTimeout", value)
|
print("callbackSetMicRecordTimeout", value)
|
||||||
@@ -522,12 +520,10 @@ def callbackCheckSpeakerThreshold(is_turned_on):
|
|||||||
view.replaceSpeakerThresholdCheckButton_Disabled()
|
view.replaceSpeakerThresholdCheckButton_Disabled()
|
||||||
model.startCheckSpeakerEnergy(setProgressBarSpeakerEnergy, view.initProgressBar_SpeakerEnergy)
|
model.startCheckSpeakerEnergy(setProgressBarSpeakerEnergy, view.initProgressBar_SpeakerEnergy)
|
||||||
view.replaceSpeakerThresholdCheckButton_Active()
|
view.replaceSpeakerThresholdCheckButton_Active()
|
||||||
view.setWidgetsStatus_ThresholdCheckButton_Normal()
|
|
||||||
else:
|
else:
|
||||||
view.replaceSpeakerThresholdCheckButton_Disabled()
|
view.replaceSpeakerThresholdCheckButton_Disabled()
|
||||||
model.stopCheckSpeakerEnergy()
|
model.stopCheckSpeakerEnergy()
|
||||||
view.replaceSpeakerThresholdCheckButton_Passive()
|
view.replaceSpeakerThresholdCheckButton_Passive()
|
||||||
view.setWidgetsStatus_ThresholdCheckButton_Normal()
|
|
||||||
|
|
||||||
def callbackSetSpeakerRecordTimeout(value):
|
def callbackSetSpeakerRecordTimeout(value):
|
||||||
print("callbackSetSpeakerRecordTimeout", value)
|
print("callbackSetSpeakerRecordTimeout", value)
|
||||||
|
|||||||
19
view.py
19
view.py
@@ -718,25 +718,6 @@ class View():
|
|||||||
vrct_gui.config_window.sb__speaker_dynamic_energy_threshold.grid(pady=(0,1))
|
vrct_gui.config_window.sb__speaker_dynamic_energy_threshold.grid(pady=(0,1))
|
||||||
vrct_gui.config_window.sb__speaker_energy_threshold.grid_remove()
|
vrct_gui.config_window.sb__speaker_energy_threshold.grid_remove()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setWidgetsStatus_ThresholdCheckButton_Disabled():
|
|
||||||
vrct_gui._changeConfigWindowWidgetsStatus(
|
|
||||||
status="disabled",
|
|
||||||
target_names=[
|
|
||||||
"mic_energy_threshold_check_button",
|
|
||||||
"speaker_energy_threshold_check_button",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setWidgetsStatus_ThresholdCheckButton_Normal():
|
|
||||||
vrct_gui._changeConfigWindowWidgetsStatus(
|
|
||||||
status="normal",
|
|
||||||
target_names=[
|
|
||||||
"mic_energy_threshold_check_button",
|
|
||||||
"speaker_energy_threshold_check_button",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def initMicThresholdCheckButton(self):
|
def initMicThresholdCheckButton(self):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, applyUiScalingAndFixTheBugScrollBar
|
from .ui_utils import bindButtonReleaseFunction, bindEnterAndLeaveColor, bindButtonPressColor, applyUiScalingAndFixTheBugScrollBar
|
||||||
from utils import callFunctionIfCallable
|
from utils import callFunctionIfCallable, makeEven
|
||||||
|
|
||||||
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
|
from customtkinter import CTkToplevel, CTkFrame, CTkLabel, CTkFont, CTkScrollableFrame
|
||||||
|
|
||||||
@@ -39,8 +39,8 @@ class _CreateSelectableLanguagesWindow(CTkToplevel):
|
|||||||
self.attach.update_idletasks()
|
self.attach.update_idletasks()
|
||||||
self.x_pos = self.attach.winfo_rootx()
|
self.x_pos = self.attach.winfo_rootx()
|
||||||
self.y_pos = self.attach.winfo_rooty()
|
self.y_pos = self.attach.winfo_rooty()
|
||||||
self.width_new = self.attach.winfo_width()
|
self.width_new = makeEven(self.attach.winfo_width())
|
||||||
self.height_new = self.attach.winfo_height()
|
self.height_new = makeEven(self.attach.winfo_height())
|
||||||
|
|
||||||
|
|
||||||
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
|
self.geometry("{}x{}+{}+{}".format(self.width_new, self.height_new, self.x_pos, self.y_pos))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from customtkinter import CTkImage
|
from customtkinter import CTkImage
|
||||||
|
|
||||||
def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, status, target_names):
|
def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, status, target_names):
|
||||||
if target_names == "All":
|
# if target_names == "All":
|
||||||
target_names = ["mic_energy_threshold_check_button", "speaker_energy_threshold_check_button"]
|
# target_names = []
|
||||||
|
|
||||||
|
|
||||||
def disableOptionmenuWidget(target_widget):
|
def disableOptionmenuWidget(target_widget):
|
||||||
@@ -17,25 +17,6 @@ def _changeConfigWindowWidgetsStatus(config_window, settings, view_variable, sta
|
|||||||
|
|
||||||
for target_name in target_names:
|
for target_name in target_names:
|
||||||
match target_name:
|
match target_name:
|
||||||
case "mic_energy_threshold_check_button":
|
|
||||||
if status == "disabled":
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR)
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.children["!ctklabel"].configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR)
|
|
||||||
|
|
||||||
elif status == "normal":
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR)
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_mic_energy_threshold.children["!ctklabel"].configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR)
|
|
||||||
|
|
||||||
case "speaker_energy_threshold_check_button":
|
|
||||||
if status == "disabled":
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR)
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.children["!ctklabel"].configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_DISABLED_COLOR)
|
|
||||||
|
|
||||||
elif status == "normal":
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR)
|
|
||||||
config_window.sb__progressbar_x_slider__passive_button_speaker_energy_threshold.children["!ctklabel"].configure(fg_color=settings.ctm.SB__PROGRESSBAR_X_SLIDER__PASSIVE_BUTTON_COLOR)
|
|
||||||
|
|
||||||
|
|
||||||
case "sb__optionmenu_mic_host":
|
case "sb__optionmenu_mic_host":
|
||||||
if status == "disabled":
|
if status == "disabled":
|
||||||
target_widget = config_window.sb__widgets["sb__optionmenu_mic_host"]
|
target_widget = config_window.sb__widgets["sb__optionmenu_mic_host"]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from .widgets import createConfigWindowTitle, createSideMenuAndSettingsBoxContainers, createSettingBoxTopBar
|
from .widgets import createConfigWindowTitle, createSideMenuAndSettingsBoxContainers, createSettingBoxTopBar
|
||||||
|
|
||||||
|
|
||||||
from customtkinter import CTkToplevel
|
from customtkinter import CTkToplevel, CTkFrame
|
||||||
|
|
||||||
from ..ui_utils import getImagePath
|
from ..ui_utils import getImagePath, getLatestWidth, getLatestHeight
|
||||||
|
|
||||||
class ConfigWindow(CTkToplevel):
|
class ConfigWindow(CTkToplevel):
|
||||||
def __init__(self, vrct_gui, settings, view_variable):
|
def __init__(self, vrct_gui, settings, view_variable):
|
||||||
@@ -32,8 +32,11 @@ class ConfigWindow(CTkToplevel):
|
|||||||
|
|
||||||
createSettingBoxTopBar(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
createSettingBoxTopBar(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
||||||
|
|
||||||
|
|
||||||
createSideMenuAndSettingsBoxContainers(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
createSideMenuAndSettingsBoxContainers(config_window=self, settings=self.settings, view_variable=self._view_variable)
|
||||||
|
|
||||||
|
# for fixing 1px bug
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(self.side_menu_bg_container, corner_radius=0, width=0, height=0)
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=1, column=0, sticky="ew")
|
||||||
|
|
||||||
|
|
||||||
self.bind_all("<Button-1>", lambda event: event.widget.focus_set(), "+")
|
self.bind_all("<Button-1>", lambda event: event.widget.focus_set(), "+")
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkSwitch
|
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkSwitch
|
||||||
|
|
||||||
def _createSettingBoxCompactModeButton(parent_widget, config_window, settings, view_variable):
|
def _createSettingBoxCompactModeButton(parent_widget, config_window, settings, view_variable, column_num):
|
||||||
|
|
||||||
def switchConfigWindowCompactMode():
|
def switchConfigWindowCompactMode():
|
||||||
if config_window.setting_box_compact_mode_switch_box.get() is True:
|
if config_window.setting_box_compact_mode_switch_box.get() is True:
|
||||||
@@ -13,7 +13,7 @@ def _createSettingBoxCompactModeButton(parent_widget, config_window, settings, v
|
|||||||
|
|
||||||
|
|
||||||
config_window.setting_box_compact_mode_button_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
config_window.setting_box_compact_mode_button_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||||
config_window.setting_box_compact_mode_button_container.grid(row=0, column=1, padx=settings.uism.COMPACT_MODE_PADX, sticky="nsw")
|
config_window.setting_box_compact_mode_button_container.grid(row=0, column=column_num, padx=settings.uism.COMPACT_MODE_PADX, sticky="nsw")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
from customtkinter import CTkFont, CTkFrame, CTkLabel
|
||||||
|
|
||||||
def _createSettingBoxTitle(parent_widget, config_window, settings, view_variable):
|
def _createSettingBoxTitle(parent_widget, config_window, settings, view_variable, column_num):
|
||||||
|
|
||||||
parent_widget.grid_columnconfigure(0, weight=1)
|
parent_widget.grid_columnconfigure(0, weight=1)
|
||||||
config_window.main_current_active_config_title_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
config_window.main_current_active_config_title_container = CTkFrame(parent_widget, corner_radius=0, fg_color=settings.ctm.TOP_BAR_BG_COLOR, width=0, height=0)
|
||||||
config_window.main_current_active_config_title_container.grid(row=0, column=0, sticky="nsew")
|
config_window.main_current_active_config_title_container.grid(row=0, column=column_num, sticky="nsew")
|
||||||
|
|
||||||
|
|
||||||
config_window.main_current_active_config_title_container.grid_rowconfigure(0, weight=1)
|
config_window.main_current_active_config_title_container.grid_rowconfigure(0, weight=1)
|
||||||
@@ -18,3 +18,7 @@ def _createSettingBoxTitle(parent_widget, config_window, settings, view_variable
|
|||||||
)
|
)
|
||||||
config_window.main_current_active_config_title.grid(row=0, column=0, padx=0, pady=settings.uism.TOP_BAR__IPADY)
|
config_window.main_current_active_config_title.grid(row=0, column=0, padx=0, pady=settings.uism.TOP_BAR__IPADY)
|
||||||
|
|
||||||
|
|
||||||
|
# for fixing 1px bug
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(config_window.main_current_active_config_title, corner_radius=0, width=0, height=0)
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=0, column=column_num, sticky="ns")
|
||||||
@@ -3,6 +3,9 @@ from customtkinter import CTkFont, CTkFrame, CTkLabel
|
|||||||
from ._createSettingBoxTitle import _createSettingBoxTitle
|
from ._createSettingBoxTitle import _createSettingBoxTitle
|
||||||
from ._createSettingBoxCompactModeButton import _createSettingBoxCompactModeButton
|
from ._createSettingBoxCompactModeButton import _createSettingBoxCompactModeButton
|
||||||
|
|
||||||
|
from ....ui_utils import getLatestHeight, getLatestWidth
|
||||||
|
from utils import isEven
|
||||||
|
|
||||||
def createSettingBoxTopBar(config_window, settings, view_variable):
|
def createSettingBoxTopBar(config_window, settings, view_variable):
|
||||||
|
|
||||||
config_window.grid_columnconfigure(1, weight=1)
|
config_window.grid_columnconfigure(1, weight=1)
|
||||||
@@ -10,6 +13,18 @@ def createSettingBoxTopBar(config_window, settings, view_variable):
|
|||||||
config_window.setting_box_top_bar.grid(row=0, column=1, sticky="nsew")
|
config_window.setting_box_top_bar.grid(row=0, column=1, sticky="nsew")
|
||||||
|
|
||||||
|
|
||||||
_createSettingBoxTitle(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable)
|
column_num=0
|
||||||
|
_createSettingBoxTitle(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable, column_num=column_num)
|
||||||
|
column_num+=1
|
||||||
|
|
||||||
_createSettingBoxCompactModeButton(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable)
|
_createSettingBoxCompactModeButton(parent_widget=config_window.setting_box_top_bar, config_window=config_window, settings=settings, view_variable=view_variable, column_num=column_num)
|
||||||
|
column_num+=1
|
||||||
|
|
||||||
|
|
||||||
|
l_height = getLatestHeight(config_window.side_menu_config_window_title_logo_frame)
|
||||||
|
if isEven(l_height) is False:
|
||||||
|
config_window.grid_rowconfigure(0, weight=0, minsize=l_height+1)
|
||||||
|
|
||||||
|
# for fixing 1px bug
|
||||||
|
setting_box_top_bar_fix_1px_bug = CTkFrame(config_window.setting_box_top_bar, corner_radius=0, width=0, height=0)
|
||||||
|
setting_box_top_bar_fix_1px_bug.grid(row=0, column=column_num, sticky="ns")
|
||||||
@@ -96,9 +96,6 @@ def _addConfigSideMenuItem(config_window, settings, view_variable, side_menu_set
|
|||||||
setattr(config_window, selected_mark_attr_name, selected_mark_widget)
|
setattr(config_window, selected_mark_attr_name, selected_mark_widget)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Arrange
|
# Arrange
|
||||||
selected_mark_widget.place(relx=-1, rely=0.5, relheight=1, anchor="w")
|
selected_mark_widget.place(relx=-1, rely=0.5, relheight=1, anchor="w")
|
||||||
label_widget.grid(row=0, column=0, padx=settings.uism.SIDE_MENU_LABELS_IPADX, pady=settings.uism.SIDE_MENU_LABELS_IPADY, sticky="ew")
|
label_widget.grid(row=0, column=0, padx=settings.uism.SIDE_MENU_LABELS_IPADX, pady=settings.uism.SIDE_MENU_LABELS_IPADY, sticky="ew")
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ def createSideMenuAndSettingsBoxContainers(config_window, settings, view_variabl
|
|||||||
config_window.side_menu_bg_container = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_BG_COLOR, width=0, height=0)
|
config_window.side_menu_bg_container = CTkFrame(config_window, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_BG_COLOR, width=0, height=0)
|
||||||
config_window.side_menu_bg_container.grid(row=1, column=0, sticky="nsew")
|
config_window.side_menu_bg_container.grid(row=1, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
config_window.side_menu_bg_container.grid_rowconfigure(0, weight=1)
|
||||||
config_window.side_menu_container = CTkFrame(config_window.side_menu_bg_container, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_LABELS_BG_FOR_FAKE_BORDER_COLOR, width=0, height=0)
|
config_window.side_menu_container = CTkFrame(config_window.side_menu_bg_container, corner_radius=0, fg_color=settings.ctm.SIDE_MENU_LABELS_BG_FOR_FAKE_BORDER_COLOR, width=0, height=0)
|
||||||
config_window.side_menu_container.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=(settings.uism.SIDE_MENU_TOP_PADY, 0))
|
config_window.side_menu_container.grid(row=0, column=0, padx=settings.uism.TOP_BAR_SIDE__TITLE_PADX, pady=(settings.uism.SIDE_MENU_TOP_PADY, 0), sticky="nsew")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkImage
|
from customtkinter import CTkFont, CTkFrame, CTkLabel, CTkImage
|
||||||
|
|
||||||
from ....ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, switchActiveTabAndPassiveTab, switchTabsColor, createOptionMenuBox
|
from ....ui_utils import bindEnterAndLeaveColor, bindButtonPressColor, bindButtonReleaseFunction, switchActiveTabAndPassiveTab, switchTabsColor, createOptionMenuBox, getLatestWidth
|
||||||
|
|
||||||
from utils import callFunctionIfCallable
|
from utils import callFunctionIfCallable
|
||||||
|
|
||||||
@@ -101,6 +101,9 @@ def createSidebarLanguagesSettings(settings, main_window, view_variable):
|
|||||||
)
|
)
|
||||||
sls__selected_language_box.grid(row=0, column=0, sticky="ew")
|
sls__selected_language_box.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug = CTkFrame(optionmenu_label_widget, corner_radius=0, width=0, height=0)
|
||||||
|
sls__box_optionmenu_wrapper_fix_1px_bug.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
return sls__box
|
return sls__box
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .main_window import createMainWindowWidgets
|
|||||||
from .config_window import ConfigWindow
|
from .config_window import ConfigWindow
|
||||||
from .ui_utils import _setDefaultActiveTab, getLatestHeight, setGeometryToCenterOfScreen, fadeInAnimation
|
from .ui_utils import _setDefaultActiveTab, getLatestHeight, setGeometryToCenterOfScreen, fadeInAnimation
|
||||||
|
|
||||||
from utils import callFunctionIfCallable
|
from utils import callFunctionIfCallable, makeEven
|
||||||
|
|
||||||
class VRCT_GUI(CTk):
|
class VRCT_GUI(CTk):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -238,8 +238,8 @@ class VRCT_GUI(CTk):
|
|||||||
self.update_idletasks()
|
self.update_idletasks()
|
||||||
x_pos = self.winfo_rootx()
|
x_pos = self.winfo_rootx()
|
||||||
y_pos = self.winfo_rooty()
|
y_pos = self.winfo_rooty()
|
||||||
width_new = self.winfo_width()
|
width_new = makeEven(self.winfo_width())
|
||||||
height_new = self.winfo_height()
|
height_new = makeEven(self.winfo_height())
|
||||||
self.modal_window.geometry("{}x{}+{}+{}".format(width_new, height_new, x_pos, y_pos))
|
self.modal_window.geometry("{}x{}+{}+{}".format(width_new, height_new, x_pos, y_pos))
|
||||||
|
|
||||||
self.modal_window.lift()
|
self.modal_window.lift()
|
||||||
|
|||||||
Reference in New Issue
Block a user