Implemented ComponentList rendering and integration of message list
This commit is contained in:
		| @@ -12,7 +12,6 @@ import javax.swing.border.EmptyBorder; | |||||||
| import envoy.client.*; | import envoy.client.*; | ||||||
| import envoy.client.event.ThemeChangeEvent; | import envoy.client.event.ThemeChangeEvent; | ||||||
| import envoy.client.ui.list.ComponentList; | import envoy.client.ui.list.ComponentList; | ||||||
| import envoy.client.ui.list.ComponentListModel; |  | ||||||
| import envoy.client.ui.settings.SettingsScreen; | import envoy.client.ui.settings.SettingsScreen; | ||||||
| import envoy.client.util.EnvoyLog; | import envoy.client.util.EnvoyLog; | ||||||
| import envoy.data.Message; | import envoy.data.Message; | ||||||
| @@ -32,27 +31,29 @@ import envoy.event.EventBus; | |||||||
|  */ |  */ | ||||||
| public class ChatWindow extends JFrame { | public class ChatWindow extends JFrame { | ||||||
|  |  | ||||||
| 	private static final long serialVersionUID = 6865098428255463649L; |  | ||||||
|  |  | ||||||
| 	// User specific objects | 	// User specific objects | ||||||
| 	private Client	client; | 	private Client	client; | ||||||
| 	private LocalDB	localDB; | 	private LocalDB	localDB; | ||||||
|  |  | ||||||
| 	// GUI components | 	// GUI components | ||||||
| 	private JPanel				contentPane				= new JPanel(); | 	private JPanel					contentPane				= new JPanel(); | ||||||
| 	private PrimaryTextArea		messageEnterTextArea	= new PrimaryTextArea(space); | 	private PrimaryTextArea			messageEnterTextArea	= new PrimaryTextArea(space); | ||||||
| 	private JList<User>			userList				= new JList<>(); | 	private JList<User>				userList				= new JList<>(); | ||||||
| 	private Chat				currentChat; | 	private Chat					currentChat; | ||||||
| 	private ComponentList<Message>	messageList; | 	private ComponentList<Message>	messageList				= new ComponentList<>(new MessageListRenderer()); | ||||||
| 	private PrimaryScrollPane	scrollPane				= new PrimaryScrollPane(); | 	private PrimaryScrollPane		scrollPane				= new PrimaryScrollPane(); | ||||||
| 	private JTextPane			textPane				= new JTextPane(); | 	private JTextPane				textPane				= new JTextPane(); | ||||||
| 	private PrimaryButton		postButton				= new PrimaryButton("Post"); | 	private PrimaryButton			postButton				= new PrimaryButton("Post"); | ||||||
| 	private PrimaryButton		settingsButton			= new PrimaryButton("Settings"); | 	private PrimaryButton			settingsButton			= new PrimaryButton("Settings"); | ||||||
|  |  | ||||||
| 	private static int space = 4; |  | ||||||
|  |  | ||||||
| 	private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName()); | 	private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName()); | ||||||
|  |  | ||||||
|  | 	// GUI component spacing | ||||||
|  | 	private final static int	space	= 4; | ||||||
|  | 	private static final Insets	insets	= new Insets(space, space, space, space); | ||||||
|  |  | ||||||
|  | 	private static final long serialVersionUID = 6865098428255463649L; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Initializes a {@link JFrame} with UI elements used to send and read messages | 	 * Initializes a {@link JFrame} with UI elements used to send and read messages | ||||||
| 	 * to different users. | 	 * to different users. | ||||||
| @@ -75,7 +76,6 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbl_contentPane.rowWeights		= new double[] { 0.05, 1.0, 0.07 }; | 		gbl_contentPane.rowWeights		= new double[] { 0.05, 1.0, 0.07 }; | ||||||
| 		contentPane.setLayout(gbl_contentPane); | 		contentPane.setLayout(gbl_contentPane); | ||||||
|  |  | ||||||
| 		messageList = new ComponentList<>(new ComponentListModel<>(), new MessageListRenderer()); |  | ||||||
| 		// TODO: messageList.setFocusTraversalKeysEnabled(false); | 		// TODO: messageList.setFocusTraversalKeysEnabled(false); | ||||||
| 		// messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); | 		// messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); | ||||||
|  |  | ||||||
| @@ -91,7 +91,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_scrollPane.gridx		= 1; | 		gbc_scrollPane.gridx		= 1; | ||||||
| 		gbc_scrollPane.gridy		= 1; | 		gbc_scrollPane.gridy		= 1; | ||||||
|  |  | ||||||
| 		gbc_scrollPane.insets = new Insets(space, space, space, space); | 		gbc_scrollPane.insets = insets; | ||||||
| 		contentPane.add(scrollPane, gbc_scrollPane); | 		contentPane.add(scrollPane, gbc_scrollPane); | ||||||
|  |  | ||||||
| 		// Message enter field | 		// Message enter field | ||||||
| @@ -100,7 +100,7 @@ public class ChatWindow extends JFrame { | |||||||
| 			@Override | 			@Override | ||||||
| 			public void keyReleased(KeyEvent e) { | 			public void keyReleased(KeyEvent e) { | ||||||
| 				if (e.getKeyCode() == KeyEvent.VK_ENTER | 				if (e.getKeyCode() == KeyEvent.VK_ENTER | ||||||
| 						&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) | 						&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)) | ||||||
| 					postMessage(); | 					postMessage(); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| @@ -110,7 +110,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_messageEnterTextfield.gridx	= 1; | 		gbc_messageEnterTextfield.gridx	= 1; | ||||||
| 		gbc_messageEnterTextfield.gridy	= 2; | 		gbc_messageEnterTextfield.gridy	= 2; | ||||||
|  |  | ||||||
| 		gbc_messageEnterTextfield.insets = new Insets(space, space, space, space); | 		gbc_messageEnterTextfield.insets = insets; | ||||||
|  |  | ||||||
| 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); | 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); | ||||||
|  |  | ||||||
| @@ -121,7 +121,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_moveSelectionPostButton.gridx	= 2; | 		gbc_moveSelectionPostButton.gridx	= 2; | ||||||
| 		gbc_moveSelectionPostButton.gridy	= 2; | 		gbc_moveSelectionPostButton.gridy	= 2; | ||||||
|  |  | ||||||
| 		gbc_moveSelectionPostButton.insets = new Insets(space, space, space, space); | 		gbc_moveSelectionPostButton.insets = insets; | ||||||
|  |  | ||||||
| 		postButton.addActionListener((evt) -> { postMessage(); }); | 		postButton.addActionListener((evt) -> { postMessage(); }); | ||||||
| 		contentPane.add(postButton, gbc_moveSelectionPostButton); | 		contentPane.add(postButton, gbc_moveSelectionPostButton); | ||||||
| @@ -133,7 +133,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_moveSelectionSettingsButton.gridx	= 2; | 		gbc_moveSelectionSettingsButton.gridx	= 2; | ||||||
| 		gbc_moveSelectionSettingsButton.gridy	= 0; | 		gbc_moveSelectionSettingsButton.gridy	= 0; | ||||||
|  |  | ||||||
| 		gbc_moveSelectionSettingsButton.insets = new Insets(space, space, space, space); | 		gbc_moveSelectionSettingsButton.insets = insets; | ||||||
|  |  | ||||||
| 		settingsButton.addActionListener((evt) -> { | 		settingsButton.addActionListener((evt) -> { | ||||||
| 			try { | 			try { | ||||||
| @@ -154,7 +154,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_partnerName.gridx	= 1; | 		gbc_partnerName.gridx	= 1; | ||||||
| 		gbc_partnerName.gridy	= 0; | 		gbc_partnerName.gridy	= 0; | ||||||
|  |  | ||||||
| 		gbc_partnerName.insets = new Insets(space, space, space, space); | 		gbc_partnerName.insets = insets; | ||||||
| 		contentPane.add(textPane, gbc_partnerName); | 		contentPane.add(textPane, gbc_partnerName); | ||||||
|  |  | ||||||
| 		userList.setCellRenderer(new UserListRenderer()); | 		userList.setCellRenderer(new UserListRenderer()); | ||||||
| @@ -187,7 +187,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		gbc_userList.gridx	= 0; | 		gbc_userList.gridx	= 0; | ||||||
| 		gbc_userList.gridy	= 1; | 		gbc_userList.gridy	= 1; | ||||||
| 		gbc_userList.anchor	= GridBagConstraints.PAGE_START; | 		gbc_userList.anchor	= GridBagConstraints.PAGE_START; | ||||||
| 		gbc_userList.insets	= new Insets(space, space, space, space); | 		gbc_userList.insets	= insets; | ||||||
|  |  | ||||||
| 		applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); | 		applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); | ||||||
|  |  | ||||||
| @@ -252,15 +252,18 @@ public class ChatWindow extends JFrame { | |||||||
| 				.build(); | 				.build(); | ||||||
|  |  | ||||||
| 			// Send message | 			// Send message | ||||||
|  | 			// TODO: Store offline messages | ||||||
| 			client.sendMessage(message); | 			client.sendMessage(message); | ||||||
|  |  | ||||||
| 			// Add message to LocalDB and update UI | 			// Add message to LocalDB and update UI | ||||||
| 			currentChat.appendMessage(message); | 			currentChat.appendMessage(message); | ||||||
| 			messageList.setModel(currentChat.getModel()); | 			// messageList.setModel(currentChat.getModel()); | ||||||
|  |  | ||||||
| 			// Clear text field | 			// Clear text field | ||||||
| 			messageEnterTextArea.setText(""); | 			messageEnterTextArea.setText(""); | ||||||
| 			contentPane.revalidate(); |  | ||||||
|  | 			revalidate(); | ||||||
|  | 			repaint(); | ||||||
| 		} catch (Exception e) { | 		} catch (Exception e) { | ||||||
| 			JOptionPane.showMessageDialog(this, | 			JOptionPane.showMessageDialog(this, | ||||||
| 					"An exception occured while sending a message. See the log for more details.", | 					"An exception occured while sending a message. See the log for more details.", | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| package envoy.client.ui; | package envoy.client.ui; | ||||||
|  |  | ||||||
| import java.awt.Component; |  | ||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
|  |  | ||||||
|  | import javax.swing.JComponent; | ||||||
| import javax.swing.JLabel; | import javax.swing.JLabel; | ||||||
|  | import javax.swing.border.EmptyBorder; | ||||||
|  |  | ||||||
| import envoy.client.Settings; | import envoy.client.Settings; | ||||||
| import envoy.client.ui.list.ComponentList; | import envoy.client.ui.list.ComponentList; | ||||||
| @@ -21,23 +22,22 @@ import envoy.data.Message; | |||||||
|  * @author Maximilian Käfer |  * @author Maximilian Käfer | ||||||
|  * @since Envoy v0.1-alpha |  * @since Envoy v0.1-alpha | ||||||
|  */ |  */ | ||||||
| public class MessageListRenderer extends JLabel implements ComponentListCellRenderer<Message> { | public class MessageListRenderer implements ComponentListCellRenderer<Message> { | ||||||
|  |  | ||||||
| 	private static final long serialVersionUID = 5164417379767181198L; |  | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public Component getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) { | 	public JComponent getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) { | ||||||
|  | 		final JLabel label = new JLabel(); | ||||||
| 		if (isSelected) { | 		if (isSelected) { | ||||||
| 			setBackground(Color.DARK_GRAY); | 			label.setBackground(Color.DARK_GRAY); | ||||||
| 			setForeground(Color.RED); | 			label.setForeground(Color.RED); | ||||||
| //			setBackground(list.getSelectionBackground()); | 			// setBackground(list.getSelectionBackground()); | ||||||
| //			setForeground(list.getSelectionForeground()); | 			// setForeground(list.getSelectionForeground()); | ||||||
| 		} else { | 		} else { | ||||||
| 			setBackground(list.getBackground()); | 			label.setBackground(list.getBackground()); | ||||||
| 			setForeground(list.getForeground()); | 			label.setForeground(list.getForeground()); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		setOpaque(true); | 		label.setOpaque(true); | ||||||
|  |  | ||||||
| 		// TODO: Handle message attachments | 		// TODO: Handle message attachments | ||||||
|  |  | ||||||
| @@ -51,12 +51,16 @@ public class MessageListRenderer extends JLabel implements ComponentListCellRend | |||||||
| 		// Getting the DateColor in the Chat of the current theme | 		// Getting the DateColor in the Chat of the current theme | ||||||
| 		String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex(); | 		String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex(); | ||||||
|  |  | ||||||
| 		setText(String.format("<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>", | 		label.setText(String.format("<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>", | ||||||
| 				dateColor, | 				dateColor, | ||||||
| 				date, | 				date, | ||||||
| 				textColor, | 				textColor, | ||||||
| 				text, | 				text, | ||||||
| 				state)); | 				state)); | ||||||
| 		return this; |  | ||||||
|  | 		// Define some space to the components above and below | ||||||
|  | 		label.setBorder(new EmptyBorder(0, 0, 15, 0)); | ||||||
|  |  | ||||||
|  | 		return label; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -7,28 +7,34 @@ import javax.swing.JPanel; | |||||||
|  * Project: <strong>envoy-client</strong><br> |  * Project: <strong>envoy-client</strong><br> | ||||||
|  * File: <strong>ComponentList.java</strong><br> |  * File: <strong>ComponentList.java</strong><br> | ||||||
|  * Created: <strong>25.01.2020</strong><br> |  * Created: <strong>25.01.2020</strong><br> | ||||||
|  *  |  * | ||||||
|  * @author Kai S. K. Engelbart |  * @author Kai S. K. Engelbart | ||||||
|  * @since Envoy v0.3-alpha |  * @since Envoy v0.3-alpha | ||||||
|  */ |  */ | ||||||
| public class ComponentList<E> extends JPanel { | public class ComponentList<E> extends JPanel { | ||||||
|  |  | ||||||
| 	private ComponentListModel<E>			model; | 	private ComponentListModel<E>			model; | ||||||
| 	private ComponentListCellRenderer<E> renderer; | 	private ComponentListCellRenderer<E>	renderer; | ||||||
|  |  | ||||||
| 	private static final long serialVersionUID = 1759644503942876737L; | 	private static final long serialVersionUID = 1759644503942876737L; | ||||||
|  |  | ||||||
| 	public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) { | 	public ComponentList(ComponentListCellRenderer<E> renderer) { | ||||||
| 		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); | 		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); | ||||||
| 		this.model		= model; | 		this.renderer = renderer; | ||||||
| 		this.renderer	= renderer; | 	} | ||||||
|  |  | ||||||
|  | 	public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) { | ||||||
|  | 		this(renderer); | ||||||
|  | 		this.model = model; | ||||||
| 		setModel(model); | 		setModel(model); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public void setModel(ComponentListModel<E> model) { | 	public void setModel(ComponentListModel<E> model) { | ||||||
| 		// Remove old model | 		// Remove old model | ||||||
| 		this.model.clear(); | 		if (this.model != null) { | ||||||
| 		this.model.setComponentList(null); | 			this.model.clear(); | ||||||
|  | 			this.model.setComponentList(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// Synchronize with new model | 		// Synchronize with new model | ||||||
| 		this.model = model; | 		this.model = model; | ||||||
| @@ -40,9 +46,10 @@ public class ComponentList<E> extends JPanel { | |||||||
| 		add(renderer.getListCellComponent(this, elem, false)); | 		add(renderer.getListCellComponent(this, elem, false)); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private void synchronizeModel() { | 	void synchronizeModel() { | ||||||
| 		removeAll(); | 		removeAll(); | ||||||
| 		for (E elem : model) | 		if (model != null) | ||||||
| 			add(renderer.getListCellComponent(this, elem, false)); | 			for (E elem : model) | ||||||
|  | 				add(renderer.getListCellComponent(this, elem, false)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,16 +1,16 @@ | |||||||
| package envoy.client.ui.list; | package envoy.client.ui.list; | ||||||
|  |  | ||||||
| import java.awt.Component; | import javax.swing.JComponent; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Project: <strong>envoy-client</strong><br> |  * Project: <strong>envoy-client</strong><br> | ||||||
|  * File: <strong>ComponentListCellRenderer.java</strong><br> |  * File: <strong>ComponentListCellRenderer.java</strong><br> | ||||||
|  * Created: <strong>25.01.2020</strong><br> |  * Created: <strong>25.01.2020</strong><br> | ||||||
|  *  |  * | ||||||
|  * @author Kai S. K. Engelbart |  * @author Kai S. K. Engelbart | ||||||
|  * @since Envoy v0.3-alpha |  * @since Envoy v0.3-alpha | ||||||
|  */ |  */ | ||||||
| public interface ComponentListCellRenderer<E> { | public interface ComponentListCellRenderer<E> { | ||||||
|  |  | ||||||
| 	Component getListCellComponent(ComponentList<? extends E> list, E value, boolean isSelected); | 	JComponent getListCellComponent(ComponentList<? extends E> list, E value, boolean isSelected); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package envoy.client.ui.list; | package envoy.client.ui.list; | ||||||
|  |  | ||||||
|  | import java.io.Serializable; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -8,19 +9,21 @@ import java.util.List; | |||||||
|  * Project: <strong>envoy-client</strong><br> |  * Project: <strong>envoy-client</strong><br> | ||||||
|  * File: <strong>ComponentListModel.java</strong><br> |  * File: <strong>ComponentListModel.java</strong><br> | ||||||
|  * Created: <strong>25.01.2020</strong><br> |  * Created: <strong>25.01.2020</strong><br> | ||||||
|  *  |  * | ||||||
|  * @author Kai S. K. Engelbart |  * @author Kai S. K. Engelbart | ||||||
|  * @since Envoy v0.3-alpha |  * @since Envoy v0.3-alpha | ||||||
|  */ |  */ | ||||||
| public final class ComponentListModel<E> implements Iterable<E> { | public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||||
|  |  | ||||||
| 	private List<E>				elements	= new ArrayList<>(); | 	private List<E>						elements	= new ArrayList<>(); | ||||||
| 	private ComponentList<E>	componentList; | 	transient private ComponentList<E>	componentList; | ||||||
|  |  | ||||||
|  | 	private static final long serialVersionUID = 4815005915255497331L; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Adds an element to this model and notifies the associated | 	 * Adds an element to this model and notifies the associated | ||||||
| 	 * {@link ComponentList} to add the corresponding component. | 	 * {@link ComponentList} to add the corresponding component. | ||||||
| 	 *  | 	 * | ||||||
| 	 * @param e the element to add | 	 * @param e the element to add | ||||||
| 	 * @return {@code true} | 	 * @return {@code true} | ||||||
| 	 * @see java.util.List#add(java.lang.Object) | 	 * @see java.util.List#add(java.lang.Object) | ||||||
| @@ -33,7 +36,7 @@ public final class ComponentListModel<E> implements Iterable<E> { | |||||||
| 	/** | 	/** | ||||||
| 	 * Removes all elements from this model and clears the associated | 	 * Removes all elements from this model and clears the associated | ||||||
| 	 * {@link ComponentList}. | 	 * {@link ComponentList}. | ||||||
| 	 *  | 	 * | ||||||
| 	 * @see java.util.List#clear() | 	 * @see java.util.List#clear() | ||||||
| 	 * @since Envoy v0.3-alpha | 	 * @since Envoy v0.3-alpha | ||||||
| 	 */ | 	 */ | ||||||
| @@ -52,7 +55,7 @@ public final class ComponentListModel<E> implements Iterable<E> { | |||||||
| 	/** | 	/** | ||||||
| 	 * Removes the element at a specific index from this model and the corresponding | 	 * Removes the element at a specific index from this model and the corresponding | ||||||
| 	 * component from the {@link ComponentList}. | 	 * component from the {@link ComponentList}. | ||||||
| 	 *  | 	 * | ||||||
| 	 * @param index | 	 * @param index | ||||||
| 	 * @return the removed element | 	 * @return the removed element | ||||||
| 	 * @see java.util.List#remove(int) | 	 * @see java.util.List#remove(int) | ||||||
| @@ -66,6 +69,7 @@ public final class ComponentListModel<E> implements Iterable<E> { | |||||||
| 	 * @return | 	 * @return | ||||||
| 	 * @see java.util.List#iterator() | 	 * @see java.util.List#iterator() | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@Override | ||||||
| 	public Iterator<E> iterator() { | 	public Iterator<E> iterator() { | ||||||
| 		return new Iterator<E>() { | 		return new Iterator<E>() { | ||||||
|  |  | ||||||
| @@ -81,6 +85,6 @@ public final class ComponentListModel<E> implements Iterable<E> { | |||||||
|  |  | ||||||
| 	void setComponentList(ComponentList<E> componentList) { | 	void setComponentList(ComponentList<E> componentList) { | ||||||
| 		this.componentList = componentList; | 		this.componentList = componentList; | ||||||
| 		if (componentList != null) elements.forEach(componentList::add); | 		if (componentList != null) componentList.synchronizeModel(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user