Add ChessPosition that will handle position.
This commit is contained in:
parent
122731ac50
commit
8572aa73e6
@ -1,13 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
|
|
||||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||||
|
|
||||||
import '../connection/ws_connection.dart';
|
import '../chess_bloc/chess_events.dart';
|
||||||
import '../utils/chess_utils.dart';
|
import '../utils/chess_utils.dart';
|
||||||
|
|
||||||
int messageIndex = 0;
|
|
||||||
|
|
||||||
class ChessSquare extends StatelessWidget {
|
class ChessSquare extends StatelessWidget {
|
||||||
final ChessCoordinate coordinate;
|
final ChessCoordinate coordinate;
|
||||||
final ChessPiece? containedPiece;
|
final ChessPiece? containedPiece;
|
||||||
@ -60,58 +57,44 @@ class ChessSquare extends StatelessWidget {
|
|||||||
draggableFdbSize = 0.15 * windowHeight;
|
draggableFdbSize = 0.15 * windowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DragTarget<ChessMove>(
|
return DragTarget<PieceMovedFrom>(
|
||||||
|
onWillAccept: (move) {
|
||||||
|
if (move?.fromSquare == coordinate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onAccept: (move) {
|
||||||
|
if (coordinate != move.fromSquare) {
|
||||||
|
ChessBloc.getInstance().add(OwnPieceMoved(
|
||||||
|
startSquare: move.fromSquare, endSquare: coordinate));
|
||||||
|
}
|
||||||
|
},
|
||||||
builder: (context, candidateData, rejectedData) {
|
builder: (context, candidateData, rejectedData) {
|
||||||
return Container(
|
return Container(
|
||||||
color: color,
|
color: color,
|
||||||
width: ChessSquare.pieceWidth,
|
width: ChessSquare.pieceWidth,
|
||||||
height: ChessSquare.pieceWidth,
|
height: ChessSquare.pieceWidth,
|
||||||
child: BlocBuilder<ChessBloc, ChessBoardState>(
|
child: Draggable<PieceMovedFrom>(
|
||||||
builder: (context, state) {
|
/* We create the move with the startSquare == endSquare. The receiving widget will give the correct value to end square. */
|
||||||
int allowDrags = 0;
|
data: PieceMovedFrom(coordinate, containedPiece),
|
||||||
if (ChessBloc.myColor == null) {
|
maxSimultaneousDrags:
|
||||||
allowDrags = 0;
|
ChessBloc.turnColor == ChessBloc.myColor ? 1 : 0,
|
||||||
} else {
|
feedback: FractionalTranslation(
|
||||||
if (containedPiece == null) {
|
translation: const Offset(-0.5, -0.75),
|
||||||
allowDrags = 0;
|
child: SizedBox(
|
||||||
} else if (containedPiece!.color == ChessBloc.myColor) {
|
height: draggableFdbSize,
|
||||||
if (ChessBloc.myColor == state.newTurnColor) {
|
width: draggableFdbSize,
|
||||||
allowDrags = 1;
|
child: containedPiece),
|
||||||
} else {
|
),
|
||||||
allowDrags = 0;
|
childWhenDragging: Container(),
|
||||||
}
|
dragAnchorStrategy: pointerDragAnchorStrategy,
|
||||||
}
|
child: containedPiece ?? Container(),
|
||||||
}
|
onDragCompleted: () {},
|
||||||
|
onDragStarted: () {},
|
||||||
return Draggable<ChessMove>(
|
|
||||||
/* We create the move with the startSquare == endSquare. The receiving widget will give the correct value to end square. */
|
|
||||||
data: ChessMove(coordinate, coordinate, containedPiece),
|
|
||||||
maxSimultaneousDrags: allowDrags,
|
|
||||||
feedback: FractionalTranslation(
|
|
||||||
translation: const Offset(-0.5, -0.75),
|
|
||||||
child: SizedBox(
|
|
||||||
height: draggableFdbSize,
|
|
||||||
width: draggableFdbSize,
|
|
||||||
child: containedPiece),
|
|
||||||
),
|
|
||||||
childWhenDragging: Container(),
|
|
||||||
dragAnchorStrategy: pointerDragAnchorStrategy,
|
|
||||||
child: containedPiece ?? Container(),
|
|
||||||
onDragCompleted: () {},
|
|
||||||
onDragStarted: () {},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onAccept: (move) {
|
|
||||||
move.endSquare = coordinate;
|
|
||||||
|
|
||||||
if (move.endSquare != move.startSquare) {
|
|
||||||
ServerConnection.getInstance().send(
|
|
||||||
'mv ${move.startSquare.toString()} ${move.endSquare.toString()}');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||||
|
import 'package:mchess/chess_bloc/chess_position.dart';
|
||||||
|
import 'package:mchess/connection/ws_connection.dart';
|
||||||
import 'package:mchess/utils/chess_utils.dart';
|
import 'package:mchess/utils/chess_utils.dart';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||||
static final ChessBloc _instance = ChessBloc._internal();
|
static final ChessBloc _instance = ChessBloc._internal();
|
||||||
|
static ChessColor turnColor = ChessColor.white;
|
||||||
static ChessColor? myColor;
|
static ChessColor? myColor;
|
||||||
|
|
||||||
static ChessColor? getSidesColor() {
|
static ChessColor? getSidesColor() {
|
||||||
@ -14,7 +17,8 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
ChessBloc._internal() : super(ChessBoardState.init()) {
|
ChessBloc._internal() : super(ChessBoardState.init()) {
|
||||||
on<InitBoard>(initBoard);
|
on<InitBoard>(initBoard);
|
||||||
on<ColorDetermined>(flipBoard);
|
on<ColorDetermined>(flipBoard);
|
||||||
on<PieceMoved>(moveHandler);
|
on<OpponentPieceMoved>(opponentMoveHandler);
|
||||||
|
on<OwnPieceMoved>(ownMoveHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ChessBloc.getInstance() {
|
factory ChessBloc.getInstance() {
|
||||||
@ -35,25 +39,32 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
|||||||
emit(ChessBoardState(event.myColor, state.newTurnColor, state.position));
|
emit(ChessBoardState(event.myColor, state.newTurnColor, state.position));
|
||||||
}
|
}
|
||||||
|
|
||||||
void moveHandler(PieceMoved event, Emitter<ChessBoardState> emit) {
|
void opponentMoveHandler(
|
||||||
Map<ChessCoordinate, ChessPiece> newPosition = state.position;
|
OpponentPieceMoved event, Emitter<ChessBoardState> emit) {
|
||||||
|
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
||||||
|
var newPosition = ChessPosition.getInstance().currentPosition;
|
||||||
|
|
||||||
newPosition[event.endSquare] = state.position[event.startSquare]!;
|
turnColor = state.newTurnColor == ChessColor.white
|
||||||
newPosition[event.startSquare] = const ChessPiece.none();
|
|
||||||
|
|
||||||
var newTurnColor = state.newTurnColor == ChessColor.white
|
|
||||||
? ChessColor.black
|
? ChessColor.black
|
||||||
: ChessColor.white;
|
: ChessColor.white;
|
||||||
|
|
||||||
log('emitting new state with position $newPosition');
|
log('emitting new state with position $newPosition');
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
ChessBoardState(
|
ChessBoardState(
|
||||||
state.bottomColor,
|
state.bottomColor,
|
||||||
newTurnColor,
|
turnColor,
|
||||||
newPosition,
|
newPosition,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
|
||||||
|
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
||||||
|
|
||||||
|
ServerConnection.getInstance().send(
|
||||||
|
'mv ${event.startSquare.toString()} ${event.endSquare.toString()}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChessBoardState {
|
class ChessBoardState {
|
||||||
@ -74,50 +85,10 @@ class ChessBoardState {
|
|||||||
factory ChessBoardState.init() {
|
factory ChessBoardState.init() {
|
||||||
ChessColor bottomColor = ChessColor.white;
|
ChessColor bottomColor = ChessColor.white;
|
||||||
ChessColor turnColor = ChessColor.white;
|
ChessColor turnColor = ChessColor.white;
|
||||||
Map<ChessCoordinate, ChessPiece> position = {};
|
Map<ChessCoordinate, ChessPiece> position =
|
||||||
|
ChessPosition.getInstance().currentPosition;
|
||||||
|
|
||||||
for (int i = 1; i <= 8; i++) {
|
return ChessBoardState(bottomColor, turnColor, position);
|
||||||
position[ChessCoordinate(i, 7)] =
|
|
||||||
ChessPiece(ChessPieceName.blackPawn, ChessColor.black);
|
|
||||||
position[ChessCoordinate(i, 2)] =
|
|
||||||
ChessPiece(ChessPieceName.whitePawn, ChessColor.white);
|
|
||||||
}
|
|
||||||
|
|
||||||
position[ChessCoordinate(1, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
|
||||||
position[ChessCoordinate(2, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
|
||||||
position[ChessCoordinate(3, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
|
||||||
position[ChessCoordinate(4, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackQueen, ChessColor.black);
|
|
||||||
position[ChessCoordinate(5, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackKing, ChessColor.black);
|
|
||||||
position[ChessCoordinate(6, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
|
||||||
position[ChessCoordinate(7, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
|
||||||
position[ChessCoordinate(8, 8)] =
|
|
||||||
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
|
||||||
|
|
||||||
position[ChessCoordinate(1, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
|
||||||
position[ChessCoordinate(2, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
|
||||||
position[ChessCoordinate(3, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
|
||||||
position[ChessCoordinate(4, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteQueen, ChessColor.white);
|
|
||||||
position[ChessCoordinate(5, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteKing, ChessColor.white);
|
|
||||||
position[ChessCoordinate(6, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
|
||||||
position[ChessCoordinate(7, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
|
||||||
position[ChessCoordinate(8, 1)] =
|
|
||||||
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
|
||||||
|
|
||||||
return ChessBoardState._(bottomColor, turnColor, position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void logPosition(Map<ChessCoordinate, ChessPiece> pos) {
|
void logPosition(Map<ChessCoordinate, ChessPiece> pos) {
|
||||||
|
@ -2,11 +2,18 @@ import 'package:mchess/utils/chess_utils.dart';
|
|||||||
|
|
||||||
abstract class ChessEvent {}
|
abstract class ChessEvent {}
|
||||||
|
|
||||||
class PieceMoved extends ChessEvent {
|
class OpponentPieceMoved extends ChessEvent {
|
||||||
final ChessCoordinate startSquare;
|
final ChessCoordinate startSquare;
|
||||||
final ChessCoordinate endSquare;
|
final ChessCoordinate endSquare;
|
||||||
|
|
||||||
PieceMoved({required this.startSquare, required this.endSquare});
|
OpponentPieceMoved({required this.startSquare, required this.endSquare});
|
||||||
|
}
|
||||||
|
|
||||||
|
class OwnPieceMoved extends ChessEvent {
|
||||||
|
final ChessCoordinate startSquare;
|
||||||
|
final ChessCoordinate endSquare;
|
||||||
|
|
||||||
|
OwnPieceMoved({required this.startSquare, required this.endSquare});
|
||||||
}
|
}
|
||||||
|
|
||||||
class InitBoard extends ChessEvent {
|
class InitBoard extends ChessEvent {
|
||||||
|
89
lib/chess_bloc/chess_position.dart
Normal file
89
lib/chess_bloc/chess_position.dart
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:mchess/utils/chess_utils.dart';
|
||||||
|
|
||||||
|
typedef ChessPositionType = Map<ChessCoordinate, ChessPiece>;
|
||||||
|
|
||||||
|
class ChessPosition {
|
||||||
|
static final ChessPosition _instance = ChessPosition._internal();
|
||||||
|
late ChessPositionType position;
|
||||||
|
|
||||||
|
static ChessPosition getInstance() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChessPosition({required this.position});
|
||||||
|
|
||||||
|
factory ChessPosition._internal() {
|
||||||
|
ChessPositionType pos = {};
|
||||||
|
|
||||||
|
for (int i = 1; i <= 8; i++) {
|
||||||
|
pos[ChessCoordinate(i, 7)] =
|
||||||
|
ChessPiece(ChessPieceName.blackPawn, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(i, 2)] =
|
||||||
|
ChessPiece(ChessPieceName.whitePawn, ChessColor.white);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos[ChessCoordinate(1, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(2, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(3, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(4, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackQueen, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(5, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackKing, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(6, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackBishop, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(7, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackKnight, ChessColor.black);
|
||||||
|
pos[ChessCoordinate(8, 8)] =
|
||||||
|
ChessPiece(ChessPieceName.blackRook, ChessColor.black);
|
||||||
|
|
||||||
|
pos[ChessCoordinate(1, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(2, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(3, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(4, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteQueen, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(5, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteKing, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(6, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteBishop, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(7, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteKnight, ChessColor.white);
|
||||||
|
pos[ChessCoordinate(8, 1)] =
|
||||||
|
ChessPiece(ChessPieceName.whiteRook, ChessColor.white);
|
||||||
|
|
||||||
|
return ChessPosition(position: pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChessPositionType get currentPosition => position;
|
||||||
|
|
||||||
|
void recordMove(ChessCoordinate from, ChessCoordinate to) {
|
||||||
|
position[to] = position[from] ?? const ChessPiece.none();
|
||||||
|
position[from] = const ChessPiece.none();
|
||||||
|
logPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void logPosition(ChessPositionType p) {
|
||||||
|
String logString = '';
|
||||||
|
|
||||||
|
for (int row = 8; row > 0; row--) {
|
||||||
|
for (int col = 1; col <= 8; col++) {
|
||||||
|
var coord = ChessCoordinate(col, row);
|
||||||
|
if (p.containsKey(coord)) {
|
||||||
|
logString = '$logString ${p[coord]?.shortName}';
|
||||||
|
} else {
|
||||||
|
logString = '$logString .';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logString = '$logString\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
log(logString);
|
||||||
|
}
|
||||||
|
}
|
@ -67,8 +67,8 @@ class ServerConnection {
|
|||||||
|
|
||||||
log('Move received : ${splitString[1]}:${splitString[2]}');
|
log('Move received : ${splitString[1]}:${splitString[2]}');
|
||||||
|
|
||||||
ChessBloc.getInstance()
|
ChessBloc.getInstance().add(
|
||||||
.add(PieceMoved(startSquare: startSquare, endSquare: endSquare));
|
OpponentPieceMoved(startSquare: startSquare, endSquare: endSquare));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,22 @@ Map<ChessPieceName, String> chessPiecesAssets = {
|
|||||||
ChessPieceName.none: 'assets/empty.svg',
|
ChessPieceName.none: 'assets/empty.svg',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Map<ChessPieceName, String> chessPiecesShortName = {
|
||||||
|
ChessPieceName.whitePawn: 'P',
|
||||||
|
ChessPieceName.whiteBishop: 'B',
|
||||||
|
ChessPieceName.whiteKnight: 'N',
|
||||||
|
ChessPieceName.whiteRook: 'R',
|
||||||
|
ChessPieceName.whiteQueen: 'Q',
|
||||||
|
ChessPieceName.whiteKing: 'K',
|
||||||
|
ChessPieceName.blackPawn: 'p',
|
||||||
|
ChessPieceName.blackBishop: 'b',
|
||||||
|
ChessPieceName.blackKnight: 'n',
|
||||||
|
ChessPieceName.blackRook: 'r',
|
||||||
|
ChessPieceName.blackQueen: 'q',
|
||||||
|
ChessPieceName.blackKing: 'k',
|
||||||
|
ChessPieceName.none: 'X',
|
||||||
|
};
|
||||||
|
|
||||||
class ChessCoordinate {
|
class ChessCoordinate {
|
||||||
final int column;
|
final int column;
|
||||||
final int row;
|
final int row;
|
||||||
@ -91,22 +107,26 @@ class ChessCoordinate {
|
|||||||
class ChessPiece extends StatelessWidget {
|
class ChessPiece extends StatelessWidget {
|
||||||
final ChessColor color;
|
final ChessColor color;
|
||||||
final ChessPieceName pieceName;
|
final ChessPieceName pieceName;
|
||||||
|
final String shortName;
|
||||||
final Widget? pieceImage;
|
final Widget? pieceImage;
|
||||||
|
|
||||||
const ChessPiece._(this.pieceName, this.color, this.pieceImage);
|
const ChessPiece._(
|
||||||
|
this.pieceName, this.color, this.pieceImage, this.shortName);
|
||||||
|
|
||||||
factory ChessPiece(ChessPieceName name, ChessColor color) {
|
factory ChessPiece(ChessPieceName name, ChessColor color) {
|
||||||
Widget? pieceImage;
|
Widget? pieceImage;
|
||||||
String pieceAssetUrl = chessPiecesAssets[name]!;
|
String pieceAssetUrl = chessPiecesAssets[name]!;
|
||||||
|
String shortName = chessPiecesShortName[name]!;
|
||||||
|
|
||||||
pieceImage = SvgPicture.asset(pieceAssetUrl);
|
pieceImage = SvgPicture.asset(pieceAssetUrl);
|
||||||
return ChessPiece._(name, color, pieceImage);
|
return ChessPiece._(name, color, pieceImage, shortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChessPiece.none({super.key})
|
const ChessPiece.none({super.key})
|
||||||
: pieceName = ChessPieceName.none,
|
: pieceName = ChessPieceName.none,
|
||||||
color = ChessColor.white,
|
color = ChessColor.white,
|
||||||
pieceImage = null;
|
pieceImage = null,
|
||||||
|
shortName = "-";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -116,11 +136,9 @@ class ChessPiece extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChessMove {
|
class PieceMovedFrom {
|
||||||
ChessCoordinate startSquare;
|
ChessCoordinate fromSquare;
|
||||||
ChessCoordinate endSquare;
|
|
||||||
ChessPiece? movedPiece;
|
ChessPiece? movedPiece;
|
||||||
ChessPiece? pieceOnEndSquare;
|
|
||||||
|
|
||||||
ChessMove(this.startSquare, this.endSquare, this.movedPiece);
|
PieceMovedFrom(this.fromSquare, this.movedPiece);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user