Consolidated the server into the project
Added npm scripts to build and launch the whole project
This commit is contained in:
		
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# HTML Canvas with WebSocket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## About
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a demo app that allows you to draw on an HTML canvas. Everythng that gets drawn will be
 | 
				
			||||||
 | 
					transmitted to a server using websocket and displayed by a receiver app.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This app needs Node.js and npm to build and run.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Running the app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Open the command line in the project directory and run ```npm run launch```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then open your browser and navigate to http://localhost:8080.
 | 
				
			||||||
@@ -8,7 +8,9 @@
 | 
				
			|||||||
    "watch": "tsc -watch -p ./",
 | 
					    "watch": "tsc -watch -p ./",
 | 
				
			||||||
    "serve": "serve -l 8080 .",
 | 
					    "serve": "serve -l 8080 .",
 | 
				
			||||||
    "debug": "npm-run-all --parallel watch serve",
 | 
					    "debug": "npm-run-all --parallel watch serve",
 | 
				
			||||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
					    "test": "echo \"Error: no test specified\" && exit 1",
 | 
				
			||||||
 | 
					    "run-server": "cd server && npm run launch",
 | 
				
			||||||
 | 
					    "launch": "npm-run-all --parallel run-server build serve"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "author": "",
 | 
					  "author": "",
 | 
				
			||||||
  "license": "ISC",
 | 
					  "license": "ISC",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										102
									
								
								server/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								server/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					dist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					node_modules/
 | 
				
			||||||
 | 
					.node_modules/
 | 
				
			||||||
 | 
					built/*
 | 
				
			||||||
 | 
					tests/cases/rwc/*
 | 
				
			||||||
 | 
					tests/cases/test262/*
 | 
				
			||||||
 | 
					tests/cases/perf/*
 | 
				
			||||||
 | 
					!tests/cases/webharness/compilerToString.js
 | 
				
			||||||
 | 
					test-args.txt
 | 
				
			||||||
 | 
					~*.docx
 | 
				
			||||||
 | 
					\#*\#
 | 
				
			||||||
 | 
					.\#*
 | 
				
			||||||
 | 
					tests/baselines/local/*
 | 
				
			||||||
 | 
					tests/baselines/local.old/*
 | 
				
			||||||
 | 
					tests/services/baselines/local/*
 | 
				
			||||||
 | 
					tests/baselines/prototyping/local/*
 | 
				
			||||||
 | 
					tests/baselines/rwc/*
 | 
				
			||||||
 | 
					tests/baselines/test262/*
 | 
				
			||||||
 | 
					tests/baselines/reference/projectOutput/*
 | 
				
			||||||
 | 
					tests/baselines/local/projectOutput/*
 | 
				
			||||||
 | 
					tests/baselines/reference/testresults.tap
 | 
				
			||||||
 | 
					tests/services/baselines/prototyping/local/*
 | 
				
			||||||
 | 
					tests/services/browser/typescriptServices.js
 | 
				
			||||||
 | 
					src/harness/*.js
 | 
				
			||||||
 | 
					src/compiler/diagnosticInformationMap.generated.ts
 | 
				
			||||||
 | 
					src/compiler/diagnosticMessages.generated.json
 | 
				
			||||||
 | 
					src/parser/diagnosticInformationMap.generated.ts
 | 
				
			||||||
 | 
					src/parser/diagnosticMessages.generated.json
 | 
				
			||||||
 | 
					rwc-report.html
 | 
				
			||||||
 | 
					*.swp
 | 
				
			||||||
 | 
					build.json
 | 
				
			||||||
 | 
					*.actual
 | 
				
			||||||
 | 
					tests/webTestServer.js
 | 
				
			||||||
 | 
					tests/webTestServer.js.map
 | 
				
			||||||
 | 
					tests/webhost/*.d.ts
 | 
				
			||||||
 | 
					tests/webhost/webtsc.js
 | 
				
			||||||
 | 
					tests/cases/**/*.js
 | 
				
			||||||
 | 
					!tests/cases/docker/*.js/
 | 
				
			||||||
 | 
					tests/cases/**/*.js.map
 | 
				
			||||||
 | 
					*.config
 | 
				
			||||||
 | 
					scripts/eslint/built/
 | 
				
			||||||
 | 
					scripts/debug.bat
 | 
				
			||||||
 | 
					scripts/run.bat
 | 
				
			||||||
 | 
					scripts/word2md.js
 | 
				
			||||||
 | 
					scripts/buildProtocol.js
 | 
				
			||||||
 | 
					scripts/ior.js
 | 
				
			||||||
 | 
					scripts/authors.js
 | 
				
			||||||
 | 
					scripts/configurePrerelease.js
 | 
				
			||||||
 | 
					scripts/configureLanguageServiceBuild.js
 | 
				
			||||||
 | 
					scripts/open-user-pr.js
 | 
				
			||||||
 | 
					scripts/open-cherry-pick-pr.js
 | 
				
			||||||
 | 
					scripts/processDiagnosticMessages.d.ts
 | 
				
			||||||
 | 
					scripts/processDiagnosticMessages.js
 | 
				
			||||||
 | 
					scripts/produceLKG.js
 | 
				
			||||||
 | 
					scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
 | 
				
			||||||
 | 
					scripts/generateLocalizedDiagnosticMessages.js
 | 
				
			||||||
 | 
					scripts/request-pr-review.js
 | 
				
			||||||
 | 
					scripts/*.js.map
 | 
				
			||||||
 | 
					scripts/typings/
 | 
				
			||||||
 | 
					coverage/
 | 
				
			||||||
 | 
					internal/
 | 
				
			||||||
 | 
					**/.DS_Store
 | 
				
			||||||
 | 
					.settings
 | 
				
			||||||
 | 
					**/.vs
 | 
				
			||||||
 | 
					**/.vscode/*
 | 
				
			||||||
 | 
					!**/.vscode/tasks.json
 | 
				
			||||||
 | 
					!**/.vscode/settings.template.json
 | 
				
			||||||
 | 
					!**/.vscode/launch.template.json
 | 
				
			||||||
 | 
					!**/.vscode/extensions.json
 | 
				
			||||||
 | 
					!tests/cases/projects/projectOption/**/node_modules
 | 
				
			||||||
 | 
					!tests/cases/projects/NodeModulesSearch/**/*
 | 
				
			||||||
 | 
					!tests/baselines/reference/project/nodeModules*/**/*
 | 
				
			||||||
 | 
					.idea
 | 
				
			||||||
 | 
					yarn.lock
 | 
				
			||||||
 | 
					yarn-error.log
 | 
				
			||||||
 | 
					.parallelperf.*
 | 
				
			||||||
 | 
					tests/cases/user/*/package-lock.json
 | 
				
			||||||
 | 
					tests/cases/user/*/node_modules/
 | 
				
			||||||
 | 
					tests/cases/user/*/**/*.js
 | 
				
			||||||
 | 
					tests/cases/user/*/**/*.js.map
 | 
				
			||||||
 | 
					tests/cases/user/*/**/*.d.ts
 | 
				
			||||||
 | 
					!tests/cases/user/zone.js/
 | 
				
			||||||
 | 
					!tests/cases/user/bignumber.js/
 | 
				
			||||||
 | 
					!tests/cases/user/discord.js/
 | 
				
			||||||
 | 
					tests/baselines/reference/dt
 | 
				
			||||||
 | 
					.failed-tests
 | 
				
			||||||
 | 
					TEST-results.xml
 | 
				
			||||||
 | 
					package-lock.json
 | 
				
			||||||
 | 
					tests/cases/user/npm/npm
 | 
				
			||||||
 | 
					tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter
 | 
				
			||||||
 | 
					tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
 | 
				
			||||||
 | 
					tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter
 | 
				
			||||||
 | 
					tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter
 | 
				
			||||||
 | 
					tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter
 | 
				
			||||||
 | 
					tests/cases/user/create-react-app/create-react-app
 | 
				
			||||||
 | 
					tests/cases/user/fp-ts/fp-ts
 | 
				
			||||||
 | 
					tests/cases/user/webpack/webpack
 | 
				
			||||||
 | 
					tests/cases/user/puppeteer/puppeteer
 | 
				
			||||||
 | 
					tests/cases/user/axios-src/axios-src
 | 
				
			||||||
 | 
					tests/cases/user/prettier/prettier
 | 
				
			||||||
 | 
					.eslintcache
 | 
				
			||||||
							
								
								
									
										17
									
								
								server/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "build": "npm install && tsc -p ./",
 | 
				
			||||||
 | 
					    "start": "node dist/app.js",
 | 
				
			||||||
 | 
					    "launch": "npm-run-all build start"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@tsconfig/node16": "^1.0.2",
 | 
				
			||||||
 | 
					    "@types/node": "^16.7.1",
 | 
				
			||||||
 | 
					    "@types/ws": "^7.4.7",
 | 
				
			||||||
 | 
					    "npm-run-all": "^4.1.5",
 | 
				
			||||||
 | 
					    "typescript": "^4.3.5"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "ws": "^8.2.0"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										102
									
								
								server/ts/app.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								server/ts/app.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					import WebSocket from 'ws';
 | 
				
			||||||
 | 
					import Coordinates from './coordinates';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const wss = new WebSocket.Server({ port: 8081 });
 | 
				
			||||||
 | 
					const receivers: WebSocket[] = [];
 | 
				
			||||||
 | 
					let sender: WebSocket | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const paths = [] as Coordinates[][];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let currentPath = [] as Coordinates[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wss.on('connection', ws => {
 | 
				
			||||||
 | 
					    console.log('CONNECTION incoming');
 | 
				
			||||||
 | 
					    ws.once('message', message => {
 | 
				
			||||||
 | 
					        const str = message.toString();
 | 
				
			||||||
 | 
					        switch (str) {
 | 
				
			||||||
 | 
					            case 'SENDER':
 | 
				
			||||||
 | 
					                setSender(ws);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'RECEIVER':
 | 
				
			||||||
 | 
					                addReceiver(ws)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                console.log('Unknown register code: ' + str);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log('READY');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function addReceiver(ws: WebSocket) {
 | 
				
			||||||
 | 
					    console.log('RECEIVER registered');
 | 
				
			||||||
 | 
					    for (const path of paths) {
 | 
				
			||||||
 | 
					        ws.send('START');
 | 
				
			||||||
 | 
					        path.forEach(c => ws.send(JSON.stringify(c)));
 | 
				
			||||||
 | 
					        ws.send('STOP');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    receivers.push(ws);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ws.onclose = () => {
 | 
				
			||||||
 | 
					        // Remove receiver when connection closes
 | 
				
			||||||
 | 
					        for (const [i, recv] of receivers.entries()) {
 | 
				
			||||||
 | 
					            if (ws === recv) {
 | 
				
			||||||
 | 
					                receivers.splice(i, 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function setSender(ws: WebSocket) {
 | 
				
			||||||
 | 
					    console.log('SENDER is being registered')
 | 
				
			||||||
 | 
					    if (sender) {
 | 
				
			||||||
 | 
					        console.log('Removing current sender...')
 | 
				
			||||||
 | 
					        sender.close();
 | 
				
			||||||
 | 
					        clearPaths();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sender = ws;
 | 
				
			||||||
 | 
					    ws.on('message', msg => processMessage(msg));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function processMessage(message: WebSocket.Data) {
 | 
				
			||||||
 | 
					    const text = message.toString();
 | 
				
			||||||
 | 
					    switch (text) {
 | 
				
			||||||
 | 
					        case 'START':
 | 
				
			||||||
 | 
					            startPath();
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 'STOP':
 | 
				
			||||||
 | 
					            finishPath();
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            processCoordinates(text);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function processCoordinates(text: string) {
 | 
				
			||||||
 | 
					    console.log('COORDINATES received: ' + text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log('SENDING to ' + receivers.length, 'clients');
 | 
				
			||||||
 | 
					    receivers.forEach(r => r.send(text));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentPath.push(JSON.parse(text));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function startPath() {
 | 
				
			||||||
 | 
					    currentPath = [];
 | 
				
			||||||
 | 
					    receivers.forEach(r => r.send('START'));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function finishPath() {
 | 
				
			||||||
 | 
					    paths.push(currentPath);
 | 
				
			||||||
 | 
					    receivers.forEach(r => r.send("STOP"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function clearPaths() {
 | 
				
			||||||
 | 
					    paths.splice(0, paths.length);
 | 
				
			||||||
 | 
					    receivers.forEach(r => r.send('CLEAR'));
 | 
				
			||||||
 | 
					    console.log('Paths have been cleared');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								server/ts/coordinates.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								server/ts/coordinates.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					export default class Coordinates {
 | 
				
			||||||
 | 
					    public x: number;
 | 
				
			||||||
 | 
					    public y: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(x: number, y: number) {
 | 
				
			||||||
 | 
					        this.x = x;
 | 
				
			||||||
 | 
					        this.y = y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								server/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "extends": "@tsconfig/node16/tsconfig.json",
 | 
				
			||||||
 | 
					    "compilerOptions": {
 | 
				
			||||||
 | 
					      "sourceMap": true,
 | 
				
			||||||
 | 
					      "outDir": "dist",
 | 
				
			||||||
 | 
					      "moduleResolution": "node"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "include": ["ts/**/*"],
 | 
				
			||||||
 | 
					    "exclude": ["node_modules", "**/*.spec.ts"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import CanvasUtil from './canvas-util/canvasUtil.js';
 | 
					import CanvasUtil from './canvas-util/canvasUtil.js';
 | 
				
			||||||
import Coordinates from './canvas-util/coordinates.js';
 | 
					import Coordinates from './canvas-util/coordinates.js';
 | 
				
			||||||
 | 
					import { getWebsocketUrl } from "./websocket/websocket.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CanvasReceiverController {
 | 
					class CanvasReceiverController {
 | 
				
			||||||
    private canvas!: HTMLCanvasElement;
 | 
					 | 
				
			||||||
    private canvasDiv: HTMLDivElement;
 | 
					    private canvasDiv: HTMLDivElement;
 | 
				
			||||||
    private canvasUtil: CanvasUtil;
 | 
					    private canvasUtil: CanvasUtil;
 | 
				
			||||||
    private ws: WebSocket;
 | 
					    private ws: WebSocket;
 | 
				
			||||||
@@ -23,7 +23,7 @@ class CanvasReceiverController {
 | 
				
			|||||||
            alert('Canvas API unavailable, drawing will not work!');
 | 
					            alert('Canvas API unavailable, drawing will not work!');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.ws = new WebSocket('ws://localhost:8081', 'json');
 | 
					        this.ws = new WebSocket(getWebsocketUrl(), 'json');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.ws.onmessage = message => this.processMessage(message);
 | 
					        this.ws.onmessage = message => this.processMessage(message);
 | 
				
			||||||
        this.ws.onopen = () => this.ws.send('RECEIVER');
 | 
					        this.ws.onopen = () => this.ws.send('RECEIVER');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import CanvasUtil from "./canvas-util/canvasUtil.js";
 | 
					import CanvasUtil from "./canvas-util/canvasUtil.js";
 | 
				
			||||||
import Coordinates from "./canvas-util/coordinates.js";
 | 
					import Coordinates from "./canvas-util/coordinates.js";
 | 
				
			||||||
 | 
					import { getWebsocketUrl } from "./websocket/websocket.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CanvasSenderController {
 | 
					class CanvasSenderController {
 | 
				
			||||||
    private canvasUtil: CanvasUtil;
 | 
					    private canvasUtil: CanvasUtil;
 | 
				
			||||||
@@ -28,7 +29,7 @@ class CanvasSenderController {
 | 
				
			|||||||
            canvas.addEventListener('touchmove', e => this.onMouseMove(e.touches[0]));
 | 
					            canvas.addEventListener('touchmove', e => this.onMouseMove(e.touches[0]));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.ws = new WebSocket('ws://localhost:8081', 'json');
 | 
					        this.ws = new WebSocket(getWebsocketUrl(), 'json');
 | 
				
			||||||
        this.ws.onopen = () => this.ws.send('SENDER');
 | 
					        this.ws.onopen = () => this.ws.send('SENDER');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								ts/websocket/websocket.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ts/websocket/websocket.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					export function getWebsocketUrl(): string {
 | 
				
			||||||
 | 
					    let scheme = 'ws';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (document.location.protocol === 'https:') {
 | 
				
			||||||
 | 
					        scheme += 's';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return scheme + '://' + document.location.hostname + ':8081';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,4 +8,4 @@
 | 
				
			|||||||
    "extends": "@tsconfig/recommended/tsconfig.json",
 | 
					    "extends": "@tsconfig/recommended/tsconfig.json",
 | 
				
			||||||
    "include": ["ts/**/*"],
 | 
					    "include": ["ts/**/*"],
 | 
				
			||||||
    "exclude": ["node_modules", "**/*.spec.ts"]
 | 
					    "exclude": ["node_modules", "**/*.spec.ts"]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user