Commit c1502b82 authored by Ayush's avatar Ayush

Refactor and optimize the code

parent f0c56e5d
import { Injectable, Output, EventEmitter } from '@angular/core';
import { map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import {EventEmitter, Injectable, Output} from '@angular/core';
import {map} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
......@@ -8,41 +9,46 @@ import { HttpClient } from '@angular/common/http';
export class ApiService {
redirectUrl: string;
baseUrl: string = "http://localhost/sfcode/backend/";
baseUrl = 'http://localhost/sfcode/backend/';
@Output() getLoggedInState: EventEmitter<any> = new EventEmitter();
constructor(private httpClient: HttpClient) { }
public userLogin(username: string, password: string) {
return this.httpClient.post<any>(this.baseUrl + '/login.php', { username, password })
constructor(private httpClient: HttpClient) {
}
public userLogin(username: string, password: string): Observable<any> {
return this.httpClient.post<any>(this.baseUrl + '/login.php', {username, password})
.pipe(map(user => {
if (user.length == 0) throw new Error();
if (user.length === 0) {
throw new Error();
}
this.setToken(JSON.stringify(user));
this.getLoggedInState.emit(true);
return user;
}));
}
public userReg(name, email, pwd, username) {
return this.httpClient.post<any>(this.baseUrl + '/register.php', { name, email, pwd, username })
public userReg(name, email, pwd, username): Observable<any> {
return this.httpClient.post<any>(this.baseUrl + '/register.php', {name, email, pwd, username})
.pipe(map(user => {
return user;
}));
}
setToken(token: string) {
setToken(token: string): void {
localStorage.setItem('sfcode_user_token_2n1289bpxd', token);
}
getToken() {
getToken(): string {
return localStorage.getItem('sfcode_user_token_2n1289bpxd');
}
deleteToken() {
deleteToken(): void {
localStorage.removeItem('sfcode_user_token_2n1289bpxd');
}
isLoggedIn() {
isLoggedIn(): boolean {
const userToken = this.getToken();
if (userToken != null) {
return true
}
return false;
return userToken != null;
}
}
\ No newline at end of file
}
......@@ -7,5 +7,6 @@
<button id="try">Try Code</button>
<button id="submit">Submit Code</button>
</div>
<label for="editor"></label>
<textarea name="editor" id="editor"></textarea>
</div>
......@@ -50,6 +50,10 @@
}
}
label {
display: none;
}
@media screen and (max-width: 1000px) {
.container {
padding: 20px 0;
......
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Problem } from '../problem';
import { ProblemService } from '../problem.service';
import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import {Problem} from '../problem';
import {ProblemService} from '../problem.service';
declare const CodeMirror: any;
@Component({
......@@ -11,42 +12,44 @@ declare const CodeMirror: any;
})
export class ArenaComponent implements OnInit {
constructor(public router: Router, private problemService: ProblemService) { }
id: number;
problem: Problem;
constructor(public router: Router, private problemService: ProblemService) {
}
ngOnInit(): void {
this.id = parseInt(this.router.url.split('/')[2]);
this.id = parseInt(this.router.url.split('/')[2], 10);
this.getProblem();
var editor_area = document.getElementById('editor');
var editor = CodeMirror.fromTextArea(editor_area, {
const editorArea = document.getElementById('editor');
const editor = CodeMirror.fromTextArea(editorArea as HTMLTextAreaElement, {
lineNumbers: true,
theme: "material-ocean",
mode: "text/x-c++src",
theme: 'material-ocean',
mode: 'text/x-c++src',
autoCloseBrackets: true,
matchBrackets: true
});
editor.setSize("auto", "70vh");
editor.setSize('auto', '70vh');
editor.setValue(this.problem.default_code[0]);
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
border: "2px solid #ddf",
padding: "20px",
fontFamily: "\"Anonymous Pro\", monospace",
border: '2px solid #ddf',
padding: '20px',
fontFamily: '"Anonymous Pro", monospace',
});
var active_lang = 0;
var langs = ["C++", "Python", "Java"];
var langs_mime = ["text/x-c++src", "text/x-python", "text/x-java"];
let activeLang = 0;
const langs = ['C++', 'Python', 'Java'];
const langsMime = ['text/x-c++src', 'text/x-python', 'text/x-java'];
var problem = this.problem;
const problem = this.problem;
document.getElementById('toggle-lang').onclick = function () {
let th = this as HTMLDivElement;
active_lang = (active_lang + 1) % 3;
th.innerHTML = langs[active_lang];
editor.setValue(problem.default_code[active_lang]);
editor.setOption("mode", langs_mime[active_lang]);
}
document.getElementById('toggle-lang').onclick = function(): void {
const th = this as HTMLDivElement;
activeLang = (activeLang + 1) % 3;
th.innerHTML = langs[activeLang];
editor.setValue(problem.default_code[activeLang]);
editor.setOption('mode', langsMime[activeLang]);
};
}
......@@ -54,7 +57,7 @@ export class ArenaComponent implements OnInit {
this.problemService.getProblems()
.subscribe(problems => {
this.problem = problems.find(i => i.id === this.id);
})
});
}
}
......@@ -21,4 +21,4 @@ export class HeaderComponent implements OnInit {
this.loggedIn = state;
}
}
\ No newline at end of file
}
<div id="count">{{problems.length}} problems available!</div>
<div class="card" *ngFor="let problem of problems">
<div *ngFor="let problem of problems" class="card">
<div class="title">{{problem.title}}</div>
<div class="details">{{problem.details}}</div>
<button class="attempt" [routerLink]="['/arena', problem.id]">Attempt</button>
<button [routerLink]="['/arena', problem.id]" class="attempt">Attempt</button>
<span class="stats"><span *ngIf="problem.n_attempts != 0">{{problem.n_correct}} accepted out of
{{problem.n_attempts}}
({{problem.n_correct / problem.n_attempts * 100}}%)</span><span *ngIf="problem.n_attempts === 0">No attempt
{{problem.n_attempts}}
({{problem.n_correct / problem.n_attempts * 100}}%)</span><span *ngIf="problem.n_attempts === 0">No attempt
yet</span></span>
</div>
......@@ -4,5 +4,6 @@
<button id="input">Input</button>
<button id="run">Run Code</button>
</div>
<label for="editor"></label>
<textarea name="editor" id="editor"></textarea>
</div>
......@@ -4,15 +4,19 @@
#toggle-lang {
position: absolute;
background: var(--color);
color: var(--bgcolor);
background: var(--bgcolor);
color: var(--color);
padding: 5px 10px;
right: 20px;
top: 20px;
right: 22px;
top: 22px;
z-index: 100;
cursor: pointer;
}
label {
display: none;
}
#attempt {
position: absolute;
right: 24px;
......
......@@ -12,25 +12,25 @@ export class IdeComponent implements OnInit {
constructor(public router: Router) { }
ngOnInit(): void {
var editor_area = document.getElementById('editor');
var editor = CodeMirror.fromTextArea(editor_area, {
const editorArea = document.getElementById('editor');
const editor = CodeMirror.fromTextArea(editorArea as HTMLTextAreaElement, {
lineNumbers: true,
theme: "material-ocean",
mode: "text/x-c++src",
theme: 'material-ocean',
mode: 'text/x-c++src',
autoCloseBrackets: true,
matchBrackets: true
});
editor.setSize("auto", "70vh");
editor.setSize('auto', '70vh');
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
border: "2px solid #ddf",
padding: "20px",
fontFamily: "\"Anonymous Pro\", monospace",
border: '2px solid #ddf',
padding: '20px',
fontFamily: '"Anonymous Pro", monospace',
});
var active_lang = 0;
var langs = ["C++", "Python", "Java"];
var langs_mime = ["text/x-c++src", "text/x-python", "text/x-java"];
var default_code = [`#include <iostream>
let activeLang = 0;
const langs = ['C++', 'Python', 'Java'];
const langsMime = ['text/x-c++src', 'text/x-python', 'text/x-java'];
const defaultCode = [`#include <iostream>
using namespace std;
int main() {
......@@ -38,18 +38,18 @@ int main() {
return 0;
}`, `print("Hello World!")`, `class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}`]
editor.setValue(default_code[active_lang]);
document.getElementById('toggle-lang').onclick = function () {
let th = this as HTMLDivElement;
active_lang = (active_lang + 1) % 3;
th.innerHTML = langs[active_lang];
editor.setValue(default_code[active_lang]);
editor.setOption("mode", langs_mime[active_lang]);
System.out.println("Hello, World!");
}
}`];
editor.setValue(defaultCode[activeLang]);
document.getElementById('toggle-lang').onclick = function(): void {
const th = this as HTMLDivElement;
activeLang = (activeLang + 1) % 3;
th.innerHTML = langs[activeLang];
editor.setValue(defaultCode[activeLang]);
editor.setOption('mode', langsMime[activeLang]);
};
}
......
......@@ -13,17 +13,17 @@ export class LDModeComponent implements OnInit {
ngOnInit(): void {
this.currentTheme = 0;
var btn = document.getElementById('button') as HTMLButtonElement;
const btn = document.getElementById('button') as HTMLButtonElement;
btn.onclick = () => {
this.currentTheme = 1 - this.currentTheme;
document.querySelector('body').classList.toggle('light');
if (btn.innerHTML === 'Light') btn.innerHTML = 'Dark';
else btn.innerHTML = 'Light';
var str = this.currentTheme == 1 ? "2px solid #112" : "2px solid #ddf";
if (btn.innerHTML === 'Light') { btn.innerHTML = 'Dark'; }
else { btn.innerHTML = 'Light'; }
const str = this.currentTheme === 1 ? '2px solid #112' : '2px solid #ddf';
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
border: str
});
}
};
}
}
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { first } from 'rxjs/operators';
import { ApiService } from '../api.service';
import {Component, OnInit} from '@angular/core';
import {AbstractControl, FormBuilder, FormGroup, Validators} from '@angular/forms';
import {Router} from '@angular/router';
import {first} from 'rxjs/operators';
import {ApiService} from '../api.service';
@Component({
selector: 'app-login',
......@@ -13,8 +13,9 @@ export class LoginComponent implements OnInit {
form: FormGroup;
timeout: any;
user_data: any;
submit_element: HTMLInputElement;
userData: any;
submitElement: HTMLInputElement;
set: NodeListOf<HTMLInputElement>;
constructor(private fb: FormBuilder, private dataService: ApiService, private router: Router) {
this.form = this.fb.group({
......@@ -23,58 +24,68 @@ export class LoginComponent implements OnInit {
});
}
set: NodeListOf<HTMLInputElement>;
get username(): AbstractControl {
return this.form.get('username');
}
get password(): AbstractControl {
return this.form.get('password');
}
ngOnInit(): void {
this.submit_element = document.getElementById('submit') as HTMLInputElement;
this.submitElement = document.getElementById('submit') as HTMLInputElement;
this.set = document.querySelectorAll('input:not([type=submit])');
this.set.forEach(function (item) {
item.addEventListener('focus', function () {
this.set.forEach(item => {
item.addEventListener('focus', () => {
item.parentElement.querySelector('label').classList.add('active');
});
item.addEventListener('blur', function () {
if (item.value == "")
item.addEventListener('blur', () => {
if (item.value === '') {
item.parentElement.querySelector('label').classList.remove('active');
}
});
})
});
}
updateLabels() {
this.set.forEach(function (item) {
if (item.value !== "" && item.value !== null)
updateLabels(): void {
this.set.forEach(item => {
if (item.value !== '' && item.value !== null) {
item.parentElement.querySelector('label').classList.add('active');
else
} else {
item.parentElement.querySelector('label').classList.remove('active');
}
});
}
postData(form: FormGroup) {
if (this.submit_element.classList.contains('disabled')) return;
this.submit_element.classList.add('disabled');
postData(form: FormGroup): void {
if (this.submitElement.classList.contains('disabled')) {
return;
}
this.submitElement.classList.add('disabled');
this.dataService.userLogin(form.value.username, form.value.password)
.pipe(first())
.subscribe(data => {
this.user_data = data;
this.userData = data;
const redirect = this.dataService.redirectUrl ? this.dataService.redirectUrl : '/home';
this.router.navigate([redirect]);
}, error => {
this.error("Incorrect username or password.");
this.router.navigate([redirect]).then();
}, () => {
this.error('Incorrect username or password.');
form.get('password').reset();
});
}
error(str: string) {
let co = (document.getElementById('cover') as HTMLDivElement);
let pu = (document.getElementById('error') as HTMLDivElement);
error(str: string): void {
const co = (document.getElementById('cover') as HTMLDivElement);
const pu = (document.getElementById('error') as HTMLDivElement);
pu.querySelector('.desc').innerHTML = str;
co.querySelectorAll('.popup').forEach(function (item) {
co.querySelectorAll('.popup').forEach(item => {
item.classList.remove('active');
});
co.style.display = "block";
setTimeout(function () {
co.style.display = 'block';
setTimeout(() => {
co.classList.add('active');
pu.classList.add('active');
}, 100);
......@@ -86,17 +97,9 @@ export class LoginComponent implements OnInit {
setTimeout(() => {
co.style.display = 'none';
this.submit_element.classList.remove('disabled');
}, 500)
this.submitElement.classList.remove('disabled');
}, 500);
}, 3000);
}
get username() {
return this.form.get('username');
}
get password() {
return this.form.get('password');
}
}
\ No newline at end of file
}
......@@ -11,15 +11,15 @@ export class ProblemService {
getProblems(): Observable<Problem[]> {
var ret: Problem[] = [];
const ret: Problem[] = [];
var arr: number[] = [6164, 6939, 9211, 4162, 7485, 5087, 1025, 4743, 5549, 9743];
const arr: number[] = [6164, 6939, 9211, 4162, 7485, 5087, 1025, 4743, 5549, 9743];
for (let i = 0; i < arr.length; i++) {
for (const item of arr) {
ret.push({
id: arr[i],
title: "DSA Problem",
details: "You could never solve it unless you invest 60% of your day and 100% of your mind you can. This problem is by our beloved Professor Mr. Ajit Diwan who specializes in posing mindfucking problems.",
id: item,
title: 'DSA Problem',
details: 'You could never solve it unless you invest 60% of your day and 100% of your mind you can. This problem is by our beloved Professor Mr. Ajit Diwan who specializes in posing mindfucking problems.',
difficulty: 0,
n_attempts: 0,
n_correct: 0,
......@@ -39,7 +39,7 @@ int main() {
return 0;
}`, `print("Hello World!")`, `class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
System.out.println("Hello, World!");
}
}`],
});
......
export interface Problem {
id: number;
title: string;
details: string;
difficulty: number;
n_attempts: number;
n_correct: number;
problem_statement: string;
default_code: string[];
}
\ No newline at end of file
id: number;
title: string;
details: string;
difficulty: number;
n_attempts: number;
n_correct: number;
problem_statement: string;
default_code: string[];
}
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import {AbstractControl, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import { first } from 'rxjs/operators';
import { ApiService } from '../api.service';
......@@ -13,7 +12,7 @@ export class RegisterComponent implements OnInit {
form: FormGroup;
timeout: any;
constructor(private fb: FormBuilder, private dataService: ApiService, private router: Router) {
constructor(private fb: FormBuilder, private dataService: ApiService) {
this.form = this.fb.group({
name: new FormControl('', [Validators.required, Validators.minLength(1), Validators.maxLength(50)]),
username: new FormControl('', [Validators.required, Validators.minLength(5), Validators.maxLength(50), Validators.pattern(/^[a-zA-Z0-9_]+$/)]),
......@@ -24,52 +23,55 @@ export class RegisterComponent implements OnInit {
}
set: NodeListOf<HTMLInputElement>;
submit_element: HTMLInputElement;
submitElement: HTMLInputElement;
ngOnInit(): void {
this.submit_element = document.getElementById('submit') as HTMLInputElement;
this.submitElement = document.getElementById('submit') as HTMLInputElement;
this.set = document.querySelectorAll('input:not([type=submit])');
this.set.forEach(function (item) {
item.addEventListener('focus', function () {
this.set.forEach(item => {
item.addEventListener('focus', () => {
item.parentElement.querySelector('label').classList.add('active');
});
item.addEventListener('blur', function () {
if (item.value == "")
item.addEventListener('blur', () => {
if (item.value === '') {
item.parentElement.querySelector('label').classList.remove('active');
}
});
})
});
}
updateLabels() {
this.set.forEach(function (item) {
if (item.value !== "" && item.value !== null)
updateLabels(): void {
this.set.forEach(item => {
if (item.value !== '' && item.value !== null) {
item.parentElement.querySelector('label').classList.add('active');
else
}
else {
item.parentElement.querySelector('label').classList.remove('active');
}
});
}
postData(frm: any) {
postData(frm: any): void {
if (this.submit_element.classList.contains('disabled')) return;
this.submit_element.classList.add('disabled');
if (this.submitElement.classList.contains('disabled')) { return; }
this.submitElement.classList.add('disabled');
if (this.form.get('name').invalid) {
this.error("Invalid name. Please keep it 1 to 50 characters long.");
this.error('Invalid name. Please keep it 1 to 50 characters long.');
return;
} else if (this.form.get('username').invalid) {
this.error("Invalid username. Please keep it 5 to 50 characters long with only alphanumeric characters and underscores.");
this.error('Invalid username. Please keep it 5 to 50 characters long with only alphanumeric characters and underscores.');
return;
} else if (this.form.get('email').invalid) {
this.error("Invalid email address. Please recheck.");
this.error('Invalid email address. Please recheck.');
return;
} else if (this.form.get('password').invalid || this.form.get('confirm_password').invalid) {
this.error("Invalid password or the passwords don't match. Please keep the password at least 8 characters long.");
this.error('Invalid password or the passwords don\'t match. Please keep the password at least 8 characters long.');
this.form.get('password').setValue('');
this.form.get('confirm_password').setValue('');
return;
} else if (this.form.get('password').value != this.form.get('confirm_password').value) {
this.error("Passwords don't match. Please recheck.");
} else if (this.form.get('password').value !== this.form.get('confirm_password').value) {
this.error('Passwords don\'t match. Please recheck.');
this.form.get('password').setValue('');
this.form.get('confirm_password').setValue('');
return;
......@@ -78,27 +80,28 @@ export class RegisterComponent implements OnInit {
this.dataService.userReg(frm.value.name, frm.value.email, frm.value.password, frm.value.username)
.pipe(first())
.subscribe(
data => {
() => {
this.success();
},
error => {
this.error('Could not register you. Please try again after some time.');
(err: any) => {
console.log(err);
this.error('Could not register you. The username or the email ID already exists.');
});
}
error(str: string) {
let co = (document.getElementById('cover') as HTMLDivElement);
let pu = (document.getElementById('error') as HTMLDivElement);
error(str: string): void {
const co = (document.getElementById('cover') as HTMLDivElement);
const pu = (document.getElementById('error') as HTMLDivElement);
pu.querySelector('.desc').innerHTML = str;
co.querySelectorAll('.popup').forEach(function (item) {
co.querySelectorAll('.popup').forEach(item => {
item.classList.remove('active');
});
co.style.display = "block";
setTimeout(function () {
co.style.display = 'block';
setTimeout(() => {
co.classList.add('active');
pu.classList.add('active');
}, 100);
......@@ -110,21 +113,21 @@ export class RegisterComponent implements OnInit {
setTimeout(() => {
co.style.display = 'none';
this.submit_element.classList.remove('disabled');
}, 500)
this.submitElement.classList.remove('disabled');
}, 500);
}, 3000);
}
success() {
let co = (document.getElementById('cover') as HTMLDivElement);
let pu = (document.getElementById('success') as HTMLDivElement);
success(): void {
const co = (document.getElementById('cover') as HTMLDivElement);
const pu = (document.getElementById('success') as HTMLDivElement);
co.querySelectorAll('.popup').forEach(function (item) {
co.querySelectorAll('.popup').forEach(item => {
item.classList.remove('active');
});
co.style.display = "block";
setTimeout(function () {
co.style.display = 'block';
setTimeout(() => {
co.classList.add('active');
pu.classList.add('active');
}, 100);
......@@ -134,8 +137,9 @@ export class RegisterComponent implements OnInit {
pu.classList.remove('active');
co.classList.remove('active');
setTimeout(function () {
setTimeout(() => {
co.style.display = 'none';
this.submitElement.classList.remove('disabled');
}, 500);
this.form.reset();
......@@ -144,10 +148,10 @@ export class RegisterComponent implements OnInit {
}
get email() { return this.form.get('email'); }
get password() { return this.form.get('password'); }
get name() { return this.form.get('name'); }
get username() { return this.form.get('name'); }
get email(): AbstractControl { return this.form.get('email'); }
get password(): AbstractControl { return this.form.get('password'); }
get name(): AbstractControl { return this.form.get('name'); }
get username(): AbstractControl { return this.form.get('name'); }
}
import {Problem} from './problem';
export interface User {
id: number;
username: string;
name: string;
password: string;
email: string;
img_url: string;
n_attempts: number;
correct_timeline: string;
rating: number;
}
\ No newline at end of file
id: number;
username: string;
name: string;
password: string;
email: string;
img_url: string;
n_attempts: number;
correct_timeline: string;
rating: number;
my_questions: Problem;
}
import { Component, OnInit } from '@angular/core';
import { User } from '../user';
import { ApiService } from '../api.service';
import {Component, OnInit} from '@angular/core';
import {User} from '../user';
import {ApiService} from '../api.service';
declare var Chart: any;
@Component({
......@@ -15,7 +16,9 @@ export class UserComponent implements OnInit {
hist: number[] = [];
timeline: number[];
constructor(private dataService: ApiService) { }
constructor(private dataService: ApiService) {
}
ngOnInit(): void {
this.dataService.getLoggedInState.subscribe(state => this.changeState(state));
this.loggedIn = this.dataService.isLoggedIn();
......@@ -34,9 +37,9 @@ export class UserComponent implements OnInit {
Chart.defaults.global.defaultFontFamily = 'Lato';
Chart.defaults.global.defaultFontColor = '#888';
var ctx = (document.getElementById('myChart') as HTMLCanvasElement).getContext('2d');
const ctx = (document.getElementById('myChart') as HTMLCanvasElement).getContext('2d');
let myChart = new Chart(ctx, {
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from(this.timeline.keys()),
......@@ -46,12 +49,12 @@ export class UserComponent implements OnInit {
fill: true,
backgroundColor: '#0f08'
},
{
label: 'Total Incorrect',
data: Array.from(this.timeline.keys()),
fill: true,
backgroundColor: '#f008'
}]
{
label: 'Total Incorrect',
data: Array.from(this.timeline.keys()),
fill: true,
backgroundColor: '#f008'
}]
},
options: {
responsive: true,
......@@ -94,13 +97,12 @@ export class UserComponent implements OnInit {
});
}
private changeState(state: boolean): void {
this.loggedIn = state;
logout(): void {
this.dataService.deleteToken();
}
logout() {
this.dataService.deleteToken();
window.location.href = window.location.href;
private changeState(state: boolean): void {
this.loggedIn = state;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment