Adding the capability of create nodes.
This commit is contained in:
parent
85a104caa5
commit
8c27095ad1
@ -21,6 +21,8 @@ import CreateNode from '@burguillosinfo/conquer/create-node'
|
||||
import MapState from '@burguillosinfo/conquer/map-state'
|
||||
import MapNode from '@burguillosinfo/conquer/map-node'
|
||||
import NewNodeUI from '@burguillosinfo/conquer/interface/new-node'
|
||||
import WebSocket from '@burguillosinfo/conquer/websocket'
|
||||
import JsonSerializer from '@burguillosinfo/conquer/serializer';
|
||||
|
||||
type StylesInterface = Record<string, Style>
|
||||
|
||||
@ -28,6 +30,7 @@ export default class Conquer {
|
||||
private conquerContainer: HTMLDivElement
|
||||
private map: Map
|
||||
private currentLongitude: number
|
||||
private intervalSendCoordinates: number | null = null;
|
||||
private currentLatitude: number
|
||||
private rotationOffset = 0
|
||||
private heading = 0
|
||||
@ -45,6 +48,8 @@ export default class Conquer {
|
||||
private state: MapState = MapState.NOTHING
|
||||
private createNodeObject: CreateNode
|
||||
private serverNodes: Record<string, MapNode> = {}
|
||||
private coordinate_1 = 0;
|
||||
private coordinate_2 = 0;
|
||||
|
||||
public getServerNodes(): Record<string, MapNode> {
|
||||
return this.serverNodes
|
||||
@ -78,8 +83,11 @@ export default class Conquer {
|
||||
const conquer = new Conquer(conquerContainer)
|
||||
conquer.run()
|
||||
}
|
||||
|
||||
setCenterDisplaced(lat: number, lon: number) {
|
||||
if (this.firstSetCenter || !(this.state & MapState.FREE_MOVE)) {
|
||||
this.coordinate_1 = lon;
|
||||
this.coordinate_2 = lat;
|
||||
const olCoordinates = this.realCoordinatesToOl(lat, lon)
|
||||
const size = this.map.getSize()
|
||||
if (size === undefined) {
|
||||
@ -112,24 +120,35 @@ export default class Conquer {
|
||||
const feature = new Feature({
|
||||
geometry: new Point(coordinates)
|
||||
})
|
||||
console.log(coordinates)
|
||||
const style = new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 14,
|
||||
fill: new Fill({color: 'white'}),
|
||||
stroke: new Stroke({
|
||||
color: 'gray',
|
||||
width: 2,
|
||||
})
|
||||
})
|
||||
const newNodeUI = new NewNodeUI(coordinates)
|
||||
const oldState = this.getState();
|
||||
newNodeUI.on('close', () => {
|
||||
this.interfaceManager.remove(newNodeUI)
|
||||
this.setState(oldState);
|
||||
})
|
||||
const mapNode = new MapNode(style, feature, `server-node-${++this.createNodeCounter}`)
|
||||
this.getServerNodes()[mapNode.getId()] = mapNode
|
||||
this.removeState(MapState.SELECT_WHERE_TO_CREATE_NODE)
|
||||
this.refreshLayers()
|
||||
this.interfaceManager.push(newNodeUI)
|
||||
this.setState(MapState.FILLING_FORM_CREATE_NODE);
|
||||
// const style = new Style({
|
||||
// image: new CircleStyle({
|
||||
// radius: 14,
|
||||
// fill: new Fill({color: 'white'}),
|
||||
// stroke: new Stroke({
|
||||
// color: 'gray',
|
||||
// width: 2,
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
// const mapNode = new MapNode(style, feature, `server-node-${++this.createNodeCounter}`)
|
||||
// this.getServerNodes()[mapNode.getId()] = mapNode
|
||||
// this.removeState(MapState.SELECT_WHERE_TO_CREATE_NODE)
|
||||
// this.refreshLayers()
|
||||
|
||||
}
|
||||
|
||||
private isStateFillingFormCreateNode(): boolean {
|
||||
return !!(this.getState() & MapState.FILLING_FORM_CREATE_NODE)
|
||||
}
|
||||
|
||||
async onClickMap(event: MapEvent): Promise<void> {
|
||||
if (this.isStateCreatingNode() && this.isStateSelectWhereToCreateNode()) {
|
||||
this.onClickWhereToCreateNode(event)
|
||||
@ -194,13 +213,89 @@ export default class Conquer {
|
||||
}
|
||||
|
||||
async onLoginSuccess(): Promise<void> {
|
||||
const currentPositionFeature = this.currentPositionFeature
|
||||
if (currentPositionFeature === null) {
|
||||
return
|
||||
this.clearIntervalSendCoordinates();
|
||||
this.createIntervalSendCoordinates();
|
||||
this.clearIntervalPollNearbyNodes();
|
||||
this.createIntervalPollNearbyNodes();
|
||||
}
|
||||
|
||||
private intervalPollNearbyNodes: number | null = null;
|
||||
|
||||
private clearIntervalPollNearbyNodes(): void {
|
||||
if (this.intervalPollNearbyNodes !== null) {
|
||||
window.clearInterval(this.intervalPollNearbyNodes)
|
||||
this.intervalPollNearbyNodes = null;
|
||||
}
|
||||
this.map.on('click', (event: MapEvent) => {
|
||||
this.onClickMap(event)
|
||||
})
|
||||
}
|
||||
|
||||
private getNearbyNodes(): void {
|
||||
const urlNodes = new URL('/conquer/node/near', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port)
|
||||
fetch(urlNodes).then(async (response) => {
|
||||
let responseBody;
|
||||
try {
|
||||
responseBody = await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error parseando json: ' + responseBody);
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
if (response.status !== 200) {
|
||||
console.error(responseBody.error);
|
||||
return;
|
||||
}
|
||||
const serverNodes: Record<string, MapNode> = {};
|
||||
const nodes = JsonSerializer.deserialize(responseBody, MapNode);
|
||||
if (!(nodes instanceof Array)) {
|
||||
console.error('Received null instead of node list.');
|
||||
return;
|
||||
}
|
||||
for (const node of nodes) {
|
||||
if (!(node instanceof MapNode)) {
|
||||
console.error('Received node is not a MapNode.');
|
||||
continue;
|
||||
}
|
||||
serverNodes[node.getId()] = node;
|
||||
}
|
||||
this.serverNodes = serverNodes;
|
||||
this.refreshLayers();
|
||||
});
|
||||
}
|
||||
|
||||
private createIntervalPollNearbyNodes(): void {
|
||||
this.intervalPollNearbyNodes = window.setInterval(() => {
|
||||
this.getNearbyNodes();
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
private createIntervalSendCoordinates(): void {
|
||||
this.intervalSendCoordinates = window.setInterval(() => {
|
||||
this.sendCoordinatesToServer();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
private sendCoordinatesToServer(): void {
|
||||
const urlLog = new URL('/conquer/user/coordinates', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port)
|
||||
fetch(urlLog, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([
|
||||
this.coordinate_1,
|
||||
this.coordinate_2,
|
||||
]),
|
||||
}).then(async (res) => {
|
||||
let responseBody;
|
||||
try {
|
||||
responseBody = await res.json();
|
||||
} catch(error) {
|
||||
console.error('Error parseando json: ' + responseBody);
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
console.error(responseBody.error);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
});
|
||||
}
|
||||
|
||||
private runPreStartState(): void {
|
||||
@ -210,12 +305,27 @@ export default class Conquer {
|
||||
this.interfaceManager = interfaceManager
|
||||
const conquerLogin = new ConquerLogin(interfaceManager)
|
||||
conquerLogin.on('login', () => {
|
||||
this.onLoginSuccess()
|
||||
})
|
||||
this.onLoginSuccess();
|
||||
});
|
||||
conquerLogin.on('logout', () => {
|
||||
this.onLogout();
|
||||
});
|
||||
conquerLogin.start()
|
||||
this.conquerLogin = conquerLogin
|
||||
}
|
||||
|
||||
private onLogout(): void {
|
||||
this.clearIntervalSendCoordinates();
|
||||
this.clearIntervalPollNearbyNodes();
|
||||
}
|
||||
|
||||
private clearIntervalSendCoordinates(): void {
|
||||
if (this.intervalSendCoordinates !== null) {
|
||||
window.clearInterval(this.intervalSendCoordinates);
|
||||
this.intervalSendCoordinates = null;
|
||||
}
|
||||
}
|
||||
|
||||
async run() {
|
||||
this.runPreStartState()
|
||||
this.setState(MapState.NORMAL)
|
||||
@ -286,7 +396,7 @@ export default class Conquer {
|
||||
this.refreshLayers()
|
||||
}
|
||||
|
||||
private refreshLayers(): void {
|
||||
private async refreshLayers(): Promise<void> {
|
||||
if (this.currentPositionFeature === null) {
|
||||
return
|
||||
}
|
||||
@ -305,7 +415,7 @@ export default class Conquer {
|
||||
const features = [this.currentPositionFeature]
|
||||
for (const key in this.getServerNodes()) {
|
||||
styles[key] = this.getServerNodes()[key].getStyle()
|
||||
features.push(this.getServerNodes()[key].getNode())
|
||||
features.push(this.getServerNodes()[key].getFeature())
|
||||
}
|
||||
const vectorLayer = new VectorLayer<VectorSource>({
|
||||
source: new VectorSource({
|
||||
@ -314,6 +424,7 @@ export default class Conquer {
|
||||
})
|
||||
if (this.vectorLayer !== null) {
|
||||
this.map.removeLayer(this.vectorLayer)
|
||||
this.vectorLayer = null;
|
||||
}
|
||||
vectorLayer.setStyle((feature) => {
|
||||
return styles[feature.getProperties().type]
|
||||
|
@ -15,12 +15,14 @@ export default abstract class ConquerInterface {
|
||||
public run(): void {
|
||||
return
|
||||
}
|
||||
|
||||
public prune(): void {
|
||||
this.callbacks = {};
|
||||
return
|
||||
}
|
||||
|
||||
protected getNodeFromTemplateId(id: string): HTMLElement {
|
||||
const template = document.getElementById(id)
|
||||
let template = document.getElementById(id)
|
||||
if (template === null) {
|
||||
Conquer.fail(`Unable to find template id ${id}.`)
|
||||
}
|
||||
@ -28,6 +30,7 @@ export default abstract class ConquerInterface {
|
||||
if (!(finalNode instanceof HTMLElement)) {
|
||||
Conquer.fail('The node is not an Element.')
|
||||
}
|
||||
finalNode.classList.remove('conquer-display-none')
|
||||
return finalNode
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,9 @@ export default class LoginUI extends ConquerInterface {
|
||||
}
|
||||
|
||||
public run() {
|
||||
this.conquerLogin.on('login', () => {
|
||||
this.runCallbacks('close');
|
||||
});
|
||||
this.storeRegisterElements()
|
||||
this.storeLoginElements()
|
||||
}
|
||||
|
@ -3,8 +3,102 @@ import Conquer from '@burguillosinfo/conquer'
|
||||
|
||||
export default class NewNodeUI extends AbstractTopBarInterface {
|
||||
private coordinates: number[];
|
||||
public getSubmitButton(): HTMLElement {
|
||||
const submitButton = this.getMainNode().querySelector('button.new-node-form-submit')
|
||||
if (submitButton === null || !(submitButton instanceof HTMLElement)) {
|
||||
Conquer.fail('SubmitButton is null');
|
||||
}
|
||||
return submitButton;
|
||||
}
|
||||
public getErrorElement(): HTMLElement {
|
||||
const errorElement = this.getMainNode().querySelector('p.conquer-error');
|
||||
if (errorElement === null || !(errorElement instanceof HTMLElement)) {
|
||||
Conquer.fail('No error element set');
|
||||
}
|
||||
return errorElement;
|
||||
}
|
||||
public getSelectNodeType(): HTMLSelectElement {
|
||||
const selectElement = this.getMainNode().querySelector('select.conquer-node-type');
|
||||
if (selectElement === null || !(selectElement instanceof HTMLSelectElement)) {
|
||||
Conquer.fail('SelectElementNodeType is null');
|
||||
}
|
||||
return selectElement
|
||||
}
|
||||
public getInputNodeName(): HTMLInputElement {
|
||||
const nodeName = this.getMainNode().querySelector('input.conquer-node-name')
|
||||
if (nodeName === null || !(nodeName instanceof HTMLInputElement)) {
|
||||
Conquer.fail('NodeName is null');
|
||||
}
|
||||
return nodeName
|
||||
}
|
||||
public getTextAreaNodeDescription(): HTMLTextAreaElement {
|
||||
const nodeDescription = this.getMainNode().querySelector('textarea.conquer-node-description')
|
||||
if (nodeDescription === null || !(nodeDescription instanceof HTMLTextAreaElement)) {
|
||||
Conquer.fail('NodeDescription is null');
|
||||
}
|
||||
return nodeDescription
|
||||
}
|
||||
constructor(coordinates: number[]) {
|
||||
super()
|
||||
this.coordinates = coordinates
|
||||
}
|
||||
public run() {
|
||||
const mainNode = this.getMainNode()
|
||||
const form = this.getNodeFromTemplateId('conquer-new-node-form-creation-template')
|
||||
mainNode.append(form)
|
||||
this.getSubmitButton().addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
this.onSubmit();
|
||||
});
|
||||
form.classList.remove('conquer-display-none')
|
||||
mainNode.classList.remove('conquer-display-none')
|
||||
}
|
||||
|
||||
private setError(error: string): void {
|
||||
const errorElement = this.getErrorElement();
|
||||
errorElement.classList.remove('conquer-display-none')
|
||||
errorElement.innerText = error
|
||||
}
|
||||
|
||||
private onSubmit(): void {
|
||||
const selectNodeType = this.getSelectNodeType();
|
||||
const inputNodeName = this.getInputNodeName();
|
||||
const textAreaNodeDescription = this.getTextAreaNodeDescription();
|
||||
const description = textAreaNodeDescription.value;
|
||||
const nodeName = inputNodeName.value;
|
||||
const selectedOptionsNodeType = selectNodeType.selectedOptions;
|
||||
if (selectedOptionsNodeType.length < 1) {
|
||||
this.setError('Debes selecionar un tipo de nodo.');
|
||||
return;
|
||||
}
|
||||
const selectedOptionNodeType = selectedOptionsNodeType[0];
|
||||
const nodeType = selectedOptionNodeType.value;
|
||||
if (nodeName.length < 5) {
|
||||
this.setError('Todos los nodos deben tener un nombre mayor a 4 caracteres.');
|
||||
return;
|
||||
}
|
||||
const urlNode = new URL('/conquer/node', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port)
|
||||
fetch(urlNode, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
description: description,
|
||||
name: nodeName,
|
||||
type: nodeType,
|
||||
coordinates: this.coordinates,
|
||||
}),
|
||||
}).then(async (res) => {
|
||||
let responseBody;
|
||||
try {
|
||||
responseBody = await res.json();
|
||||
} catch (error) {
|
||||
this.setError( 'Respuesta erronea del servidor.');
|
||||
return;
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
this.setError(responseBody.error);
|
||||
return;
|
||||
}
|
||||
this.runCallbacks('close')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ export type ConquerLoginEventCallback = () => void
|
||||
export default class Login {
|
||||
private conquerLogin: HTMLDivElement
|
||||
private conquerInterfaceManager: ConquerInterfaceManager
|
||||
private cachedIsLoggedIn = true
|
||||
private cachedIsLoggedIn: boolean | null = null
|
||||
|
||||
constructor(conquerInterfaceManager: ConquerInterfaceManager) {
|
||||
this.conquerInterfaceManager = conquerInterfaceManager
|
||||
@ -49,9 +49,16 @@ export default class Login {
|
||||
private async loopCheckLogin(): Promise<void> {
|
||||
window.setInterval(() => {
|
||||
this.isLogged().then((isLogged) => {
|
||||
if (!isLogged && this.cachedIsLoggedIn) {
|
||||
if (isLogged) {
|
||||
if (this.cachedIsLoggedIn !== true) {
|
||||
this.cachedIsLoggedIn = true;
|
||||
this.onLoginSuccess();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.cachedIsLoggedIn !== false) {
|
||||
this.cachedIsLoggedIn = false;
|
||||
this.onLogout()
|
||||
this.cachedIsLoggedIn = false
|
||||
}
|
||||
})
|
||||
}, 5000)
|
||||
@ -60,6 +67,12 @@ export default class Login {
|
||||
private async onLogout(): Promise<void> {
|
||||
const interfaceManager = this.conquerInterfaceManager
|
||||
const loginUI = new LoginUI(this)
|
||||
for (const callback of this.callbacks.logout) {
|
||||
callback();
|
||||
}
|
||||
loginUI.on('close', () => {
|
||||
interfaceManager.remove(loginUI);
|
||||
})
|
||||
interfaceManager.push(loginUI)
|
||||
}
|
||||
|
||||
@ -71,9 +84,8 @@ export default class Login {
|
||||
this.callbacks[name].push(callback)
|
||||
}
|
||||
|
||||
private async onLoginSuccess(loginUI: LoginUI): Promise<void> {
|
||||
private async onLoginSuccess(): Promise<void> {
|
||||
this.cachedIsLoggedIn = true
|
||||
this.conquerInterfaceManager.remove(loginUI)
|
||||
for (const callback of this.callbacks.login) {
|
||||
callback()
|
||||
}
|
||||
@ -105,7 +117,7 @@ export default class Login {
|
||||
loginUI.unsetLoginAndRegisterErrors()
|
||||
const isLogged = await this.isLogged()
|
||||
if (isLogged) {
|
||||
this.onLoginSuccess(loginUI)
|
||||
this.onLoginSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,66 @@
|
||||
import { JsonObject, JsonProperty } from 'typescript-json-serializer';
|
||||
import Style from 'ol/style/Style'
|
||||
import Feature from 'ol/Feature'
|
||||
import CircleStyle from 'ol/style/Circle'
|
||||
import Point from 'ol/geom/Point'
|
||||
import Fill from 'ol/style/Fill'
|
||||
import Stroke from 'ol/style/Stroke'
|
||||
|
||||
@JsonObject()
|
||||
export default class MapNode {
|
||||
private style: Style
|
||||
private node: Feature
|
||||
private id: string
|
||||
private feature: Feature | null = null;
|
||||
|
||||
constructor(style: Style, node: Feature, id: string) {
|
||||
this.style = style
|
||||
this.node = node.clone()
|
||||
this.id = id
|
||||
this.node.setProperties({type: this.id})
|
||||
|
||||
constructor(
|
||||
@JsonProperty() private uuid: string,
|
||||
@JsonProperty() private coordinate_1: number,
|
||||
@JsonProperty() private coordinate_2: number,
|
||||
@JsonProperty() private type: string,
|
||||
@JsonProperty() private name: string,
|
||||
@JsonProperty() private description: string,
|
||||
@JsonProperty() private kind: string,
|
||||
) {
|
||||
}
|
||||
|
||||
public getType(): string {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return this.id
|
||||
return 'node-' + this.uuid;
|
||||
}
|
||||
|
||||
public getNode(): Feature {
|
||||
return this.node
|
||||
public getFeature(): Feature {
|
||||
if (this.feature === null) {
|
||||
console.log(this.coordinate_1);
|
||||
console.log(this.coordinate_2);
|
||||
this.feature = new Feature({
|
||||
geometry: new Point([this.coordinate_1, this.coordinate_2]),
|
||||
type: 'node-' + this.uuid,
|
||||
})
|
||||
}
|
||||
return this.feature;
|
||||
}
|
||||
|
||||
public getStyle(): Style {
|
||||
return this.style
|
||||
return new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 14,
|
||||
fill: new Fill({color: 'white'}),
|
||||
stroke: new Stroke({
|
||||
color: 'gray',
|
||||
width: 2,
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ enum MapState {
|
||||
FREE_ROTATION = 0x4,
|
||||
CREATE_NODE = 0x8,
|
||||
SELECT_WHERE_TO_CREATE_NODE = 0x10,
|
||||
FILLING_FORM_CREATE_NODE = 0x20,
|
||||
}
|
||||
|
||||
export default MapState
|
||||
|
6
js-src/conquer/serializer.ts
Normal file
6
js-src/conquer/serializer.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { JsonSerializer, throwError } from 'typescript-json-serializer';
|
||||
|
||||
export default new JsonSerializer({
|
||||
errorCallback: throwError,
|
||||
additionalPropertiesPolicy: 'disallow',
|
||||
});
|
@ -3,7 +3,7 @@ export default class ConquerWebSocket {
|
||||
private socketReady = false
|
||||
|
||||
private getWebSocket(): WebSocket {
|
||||
if (this.webSocket !== null) {
|
||||
if (this.webSocket !== null && this.socketReady) {
|
||||
return this.webSocket
|
||||
}
|
||||
this.webSocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/conquer/websocket`)
|
||||
@ -18,7 +18,7 @@ export default class ConquerWebSocket {
|
||||
})
|
||||
return this.webSocket
|
||||
}
|
||||
|
||||
|
||||
private onSocketOpen(event: Event) {
|
||||
this.socketReady = true
|
||||
}
|
||||
|
29
js-src/generated/v1/packet/open-new-node_pb.d.ts
vendored
29
js-src/generated/v1/packet/open-new-node_pb.d.ts
vendored
@ -1,29 +0,0 @@
|
||||
// package: proto.v1.packet
|
||||
// file: v1/packet/open-new-node.proto
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
|
||||
export class OpenNewNode extends jspb.Message {
|
||||
getLatitude(): number;
|
||||
setLatitude(value: number): void;
|
||||
|
||||
getLongitude(): number;
|
||||
setLongitude(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): OpenNewNode.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: OpenNewNode): OpenNewNode.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: OpenNewNode, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): OpenNewNode;
|
||||
static deserializeBinaryFromReader(message: OpenNewNode, reader: jspb.BinaryReader): OpenNewNode;
|
||||
}
|
||||
|
||||
export namespace OpenNewNode {
|
||||
export type AsObject = {
|
||||
latitude: number,
|
||||
longitude: number,
|
||||
}
|
||||
}
|
||||
|
@ -1,206 +0,0 @@
|
||||
// source: v1/packet/open-new-node.proto
|
||||
/**
|
||||
* @fileoverview
|
||||
* @enhanceable
|
||||
* @suppress {missingRequire} reports error on implicit type usages.
|
||||
* @suppress {messageConventions} JS Compiler reports an error if a variable or
|
||||
* field starts with 'MSG_' and isn't a translatable message.
|
||||
* @public
|
||||
*/
|
||||
// GENERATED CODE -- DO NOT EDIT!
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
var jspb = require('google-protobuf');
|
||||
var goog = jspb;
|
||||
var global =
|
||||
(typeof globalThis !== 'undefined' && globalThis) ||
|
||||
(typeof window !== 'undefined' && window) ||
|
||||
(typeof global !== 'undefined' && global) ||
|
||||
(typeof self !== 'undefined' && self) ||
|
||||
(function () { return this; }).call(null) ||
|
||||
Function('return this')();
|
||||
|
||||
goog.exportSymbol('proto.proto.v1.packet.OpenNewNode', null, global);
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.proto.v1.packet.OpenNewNode, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.displayName = 'proto.proto.v1.packet.OpenNewNode';
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.proto.v1.packet.OpenNewNode.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.proto.v1.packet.OpenNewNode} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
latitude: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0),
|
||||
longitude: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.proto.v1.packet.OpenNewNode}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.proto.v1.packet.OpenNewNode;
|
||||
return proto.proto.v1.packet.OpenNewNode.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.proto.v1.packet.OpenNewNode} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.proto.v1.packet.OpenNewNode}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readDouble());
|
||||
msg.setLatitude(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {number} */ (reader.readDouble());
|
||||
msg.setLongitude(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.proto.v1.packet.OpenNewNode.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.proto.v1.packet.OpenNewNode} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getLatitude();
|
||||
if (f !== 0.0) {
|
||||
writer.writeDouble(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getLongitude();
|
||||
if (f !== 0.0) {
|
||||
writer.writeDouble(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional double latitude = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.getLatitude = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @return {!proto.proto.v1.packet.OpenNewNode} returns this
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.setLatitude = function(value) {
|
||||
return jspb.Message.setProto3FloatField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional double longitude = 2;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.getLongitude = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @return {!proto.proto.v1.packet.OpenNewNode} returns this
|
||||
*/
|
||||
proto.proto.v1.packet.OpenNewNode.prototype.setLongitude = function(value) {
|
||||
return jspb.Message.setProto3FloatField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto.proto.v1.packet);
|
@ -88,10 +88,12 @@ sub startup ($self) {
|
||||
$r->get('/sitemap.xml')->to('Sitemap#sitemap');
|
||||
$r->get('/robots.txt')->to('Robots#robots');
|
||||
|
||||
# $r->get('/:post')->to('Page#post');
|
||||
$r->get('/stats')->to('Metrics#stats');
|
||||
$r->get('/conquer')->to('Conquer#index');
|
||||
$r->put('/conquer/user')->to('UserConquer#create');
|
||||
$r->post('/conquer/user/coordinates')->to('UserConquer#setCoordinates');
|
||||
$r->put('/conquer/node')->to('ConquerNode#create');
|
||||
$r->get('/conquer/node/near')->to('ConquerNode#nearbyNodes');
|
||||
$r->get('/conquer/user')->to('UserConquer#get_self');
|
||||
$r->post('/conquer/user/login')->to('UserConquer#login');
|
||||
$r->get('/conquer/tile/<zoom>/<x>/<y>.png')->to('ConquerTile#tile');
|
||||
|
135
lib/BurguillosInfo/Controller/ConquerNode.pm
Normal file
135
lib/BurguillosInfo/Controller/ConquerNode.pm
Normal file
@ -0,0 +1,135 @@
|
||||
package BurguillosInfo::Controller::ConquerNode;
|
||||
|
||||
use v5.34.1;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use utf8;
|
||||
|
||||
use Mojo::Base 'Mojolicious::Controller', '-signatures';
|
||||
use UUID::URandom qw/create_uuid_string/;
|
||||
|
||||
sub create ($self) {
|
||||
my $user = $self->current_user;
|
||||
if ( !defined $user ) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => {
|
||||
error => 'No estás autenticado.',
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( !$user->is_admin ) {
|
||||
return $self->render(
|
||||
status => 403,
|
||||
json => {
|
||||
error => 'No tienes permiso para hacer eso.',
|
||||
}
|
||||
);
|
||||
}
|
||||
my $input = $self->_expectJson;
|
||||
if ( !defined $input ) {
|
||||
return;
|
||||
}
|
||||
my $name = $input->{name};
|
||||
my $coordinates = $input->{coordinates};
|
||||
my $type = $input->{type};
|
||||
my $description = $input->{description};
|
||||
if ( ref $coordinates ne 'ARRAY' || scalar $coordinates->@* != 2 ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Formato erroneo de coordenadas.',
|
||||
}
|
||||
);
|
||||
}
|
||||
my ($coordinate_1, $coordinate_2) = $coordinates->@*;
|
||||
if ( !defined $name && length $name < 5 ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error =>
|
||||
'Número incorrecto de carácteres en el nombre del nodo.',
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( !defined $description ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'La descripción puede estar vacía, '
|
||||
. 'pero debe existir, si ves este error '
|
||||
. 'desde la aplicación es un error de programación.',
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( !defined $type ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Los nodos deben tener un tipo.'
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( $type ne 'normal' ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Tipo de nodo no soportado.',
|
||||
}
|
||||
);
|
||||
}
|
||||
my $uuid_node = create_uuid_string();
|
||||
my $node;
|
||||
eval {
|
||||
$node = BurguillosInfo::Schema->Schema->resultset('ConquerNode')->new(
|
||||
{
|
||||
uuid => $uuid_node,
|
||||
description => $description,
|
||||
name => $name,
|
||||
type => $type,
|
||||
coordinate_1 => $coordinate_1,
|
||||
coordinate_2 => $coordinate_2
|
||||
}
|
||||
);
|
||||
$node->insert;
|
||||
};
|
||||
if ($@) {
|
||||
warn $@;
|
||||
return $self->render(
|
||||
status => 500,
|
||||
json => {
|
||||
error => 'El servidor no pudo almacenar el nodo, reporta este error.',
|
||||
}
|
||||
);
|
||||
}
|
||||
return $self->render(
|
||||
status => 200,
|
||||
json => $node->serialize,
|
||||
);
|
||||
}
|
||||
|
||||
sub nearbyNodes($self) {
|
||||
my $user = $self->current_user;
|
||||
if (!defined $user) {
|
||||
return $self->render(status => 401, json => {
|
||||
error => 'No estás loggeado.',
|
||||
});
|
||||
}
|
||||
my @nodes = BurguillosInfo::Schema->Schema->resultset('ConquerNode')->search({});
|
||||
@nodes = map { $_->serialize } @nodes;
|
||||
return $self->render(json => \@nodes);
|
||||
}
|
||||
|
||||
sub _expectJson ($self) {
|
||||
my $input;
|
||||
eval { $input = $self->req->json; };
|
||||
if ($@) {
|
||||
say STDERR $@;
|
||||
$self->_renderError( 400, 'Se esperaba JSON.' );
|
||||
return;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
1;
|
@ -21,12 +21,12 @@ my $username_maximum_chars = 15;
|
||||
my $password_minimum_chars = 8;
|
||||
my $password_maximum_chars = 4096;
|
||||
|
||||
sub get_self($self) {
|
||||
sub get_self ($self) {
|
||||
my $user = $self->current_user;
|
||||
if (!defined $user) {
|
||||
return $self->_renderError(401, 'No estás loggeado.');
|
||||
if ( !defined $user ) {
|
||||
return $self->_renderError( 401, 'No estás loggeado.' );
|
||||
}
|
||||
return $self->render(json => $user->serialize_to_owner, status => 200);
|
||||
return $self->render( json => $user->serialize_to_owner, status => 200 );
|
||||
}
|
||||
|
||||
sub create ($self) {
|
||||
@ -47,7 +47,7 @@ sub _expectJson ($self) {
|
||||
eval { $input = $self->req->json; };
|
||||
if ($@) {
|
||||
say STDERR $@;
|
||||
$self->_renderError(400, 'Se esperaba JSON.');
|
||||
$self->_renderError( 400, 'Se esperaba JSON.' );
|
||||
return;
|
||||
}
|
||||
return $input;
|
||||
@ -66,8 +66,8 @@ sub login ($self) {
|
||||
my @tentative_users =
|
||||
$resultset_conquer_user->search( { username => $username } );
|
||||
my $tentative_user = $tentative_users[0];
|
||||
if (!defined $tentative_user) {
|
||||
$self->_renderError(401, 'El usuario especificado no existe.');
|
||||
if ( !defined $tentative_user ) {
|
||||
$self->_renderError( 401, 'El usuario especificado no existe.' );
|
||||
return;
|
||||
}
|
||||
if ( !bcrypt_check( $password, $tentative_user->encrypted_password ) ) {
|
||||
@ -84,6 +84,40 @@ sub login ($self) {
|
||||
);
|
||||
}
|
||||
|
||||
sub setCoordinates ($self) {
|
||||
my $input = $self->_expectJson;
|
||||
my $user = $self->current_user;
|
||||
if ( !defined $user ) {
|
||||
return $self->render(
|
||||
status => 401,
|
||||
json => {
|
||||
error => 'Debes estar loggeado para cambiar tus'
|
||||
. ' coordenadas.',
|
||||
}
|
||||
);
|
||||
}
|
||||
if ( !defined $input ) {
|
||||
return;
|
||||
}
|
||||
if ( ref $input ne 'ARRAY' && scalar $input->@* == 2 ) {
|
||||
return $self->render(
|
||||
status => 400,
|
||||
json => {
|
||||
error => 'Mal formato de coordenadas, debe ser '
|
||||
. 'un array de exactamente 2 números reales.',
|
||||
}
|
||||
);
|
||||
}
|
||||
$user->coordinates($input);
|
||||
$user->update;
|
||||
return $self->render(
|
||||
status => 200,
|
||||
json => {
|
||||
ok => $JSON::true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub _createUser ( $self, $username, $password ) {
|
||||
my $user;
|
||||
my $uuid = create_uuid_string();
|
||||
@ -97,6 +131,7 @@ sub _createUser ( $self, $username, $password ) {
|
||||
username => $username
|
||||
}
|
||||
);
|
||||
$user->coordinates( [ 0, 0 ] );
|
||||
$user->insert;
|
||||
};
|
||||
if ($@) {
|
||||
|
@ -57,6 +57,20 @@ sub MIGRATIONS {
|
||||
is_admin BOOLEAN NOT NULL DEFAULT false,
|
||||
registration_date TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);',
|
||||
'CREATE TABLE conquer_node (
|
||||
uuid UUID NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
coordinate_1 REAL NOT NULL,
|
||||
coordinate_2 REAL NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
);',
|
||||
'CREATE INDEX index_conquer_node_coordinate_1 on conquer_node (coordinate_1);',
|
||||
'CREATE INDEX index_conquer_node_coordinate_2 on conquer_node (coordinate_2);',
|
||||
'ALTER TABLE conquer_user ADD COLUMN last_coordinate_1 REAL NOT NULL DEFAULT 0;',
|
||||
'ALTER TABLE conquer_user ALTER COLUMN last_coordinate_1 DROP DEFAULT;',
|
||||
'ALTER TABLE conquer_user ADD COLUMN last_coordinate_2 REAL NOT NULL DEFAULT 0;',
|
||||
'ALTER TABLE conquer_user ALTER COLUMN last_coordinate_2 DROP DEFAULT;',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ my $schema;
|
||||
|
||||
sub Schema ($class) {
|
||||
if ( !defined $schema ) {
|
||||
use BurguillosInfo::DB;
|
||||
BurguillosInfo::DB->connect;
|
||||
my $app = BurguillosInfo->new;
|
||||
my $config = $app->{config};
|
||||
my $database_config = $config->{db};
|
||||
|
56
lib/BurguillosInfo/Schema/Result/ConquerNode.pm
Normal file
56
lib/BurguillosInfo/Schema/Result/ConquerNode.pm
Normal file
@ -0,0 +1,56 @@
|
||||
package BurguillosInfo::Schema::Result::ConquerNode;
|
||||
|
||||
use v5.36.0;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use parent 'DBIx::Class::Core';
|
||||
|
||||
use feature 'signatures';
|
||||
|
||||
__PACKAGE__->table('conquer_node');
|
||||
__PACKAGE__->load_components("TimeStamp");
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
uuid => {
|
||||
data_type => 'uuid',
|
||||
is_nullable => 0,
|
||||
},
|
||||
name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
default_value => \'0',
|
||||
},
|
||||
coordinate_1 => {
|
||||
data_type => 'real',
|
||||
is_nullable => 0,
|
||||
},
|
||||
coordinate_2 => {
|
||||
data_type => 'real',
|
||||
is_nullable => 0,
|
||||
},
|
||||
type => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
description => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
}
|
||||
);
|
||||
|
||||
sub serialize ($self) {
|
||||
$self = $self->get_from_storage();
|
||||
return {
|
||||
kind => 'ConquerNode',
|
||||
uuid => $self->uuid,
|
||||
name => $self->name,
|
||||
description => $self->description,
|
||||
type => $self->type,
|
||||
coordinate_1 => $self->coordinate_1,
|
||||
coordinate_2 => $self->coordinate_2,
|
||||
};
|
||||
}
|
||||
__PACKAGE__->set_primary_key('uuid');
|
||||
1;
|
@ -39,9 +39,31 @@ __PACKAGE__->add_columns(
|
||||
data_type => 'boolean',
|
||||
is_nullable => 0,
|
||||
default_value => \'0',
|
||||
}
|
||||
},
|
||||
last_coordinate_1 => {
|
||||
data_type => 'real',
|
||||
is_nullable => 0,
|
||||
default_value => \'0',
|
||||
},
|
||||
last_coordinate_2 => {
|
||||
data_type => 'real',
|
||||
is_nullable => 0,
|
||||
default_value => \'0',
|
||||
},
|
||||
);
|
||||
|
||||
sub coordinates($self, $coordinates = undef) {
|
||||
if (defined $coordinates) {
|
||||
if (ref $coordinates ne 'ARRAY' || scalar $coordinates->@* != 2) {
|
||||
die 'The second parameter of this subroutine '
|
||||
. 'must be an ARRAYREF of exactly two elements.';
|
||||
}
|
||||
$self->last_coordinate_1($coordinates->[0]);
|
||||
$self->last_coordinate_2($coordinates->[1]);
|
||||
}
|
||||
return [$self->last_coordinate_1, $self->last_coordinate_2];
|
||||
}
|
||||
|
||||
sub serialize_to_owner ($self) {
|
||||
$self = $self->get_from_storage();
|
||||
return {
|
||||
|
@ -28,6 +28,7 @@
|
||||
"protoc-gen-js": "^3.21.2",
|
||||
"tablesort": "^5.3.0",
|
||||
"ts-loader": "^9.5.0",
|
||||
"ts-protoc-gen": "^0.15.0"
|
||||
"ts-protoc-gen": "^0.15.0",
|
||||
"typescript-json-serializer": "^6.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package proto.v1.packet;
|
||||
|
||||
message OpenNewNode {
|
||||
double latitude = 1;
|
||||
double longitude = 2;
|
||||
}
|
@ -9,20 +9,41 @@ body {
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%; }
|
||||
body p.conquer-register-error, body p.conquer-login-error, body p.conquer-login-success {
|
||||
body p.conquer-register-error, body p.conquer-login-error, body p.conquer-login-success, body p.conquer-error {
|
||||
color: red;
|
||||
margin: 3px;
|
||||
font-size: 1.3rem;
|
||||
background: blanchedalmond;
|
||||
padding: 3px;
|
||||
border-radius: 10px;
|
||||
border: solid 1px black; }
|
||||
border: solid 1px black;
|
||||
overflow-y: scroll; }
|
||||
body form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%; }
|
||||
body form label {
|
||||
width: 100%; }
|
||||
body form label input, body form label textarea, body form label select {
|
||||
width: 100%;
|
||||
border: none;
|
||||
background-image: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
background: white;
|
||||
box-shadow: none;
|
||||
min-height: 2rem;
|
||||
border-radius: 0.5rem; }
|
||||
body form label textarea {
|
||||
height: 100px; }
|
||||
body div.conquer-interface-element-padded {
|
||||
width: calc(100% - 60px);
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
display: flex;
|
||||
justify-content: center; }
|
||||
body div.conquer-interface-element-padded.conquer-display-none {
|
||||
display: none; }
|
||||
body div.create-node-slide {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
|
@ -17,7 +17,7 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
p.conquer-register-error, p.conquer-login-error, p.conquer-login-success {
|
||||
p.conquer-register-error, p.conquer-login-error, p.conquer-login-success,p.conquer-error {
|
||||
color: red;
|
||||
margin: 3px;
|
||||
font-size: 1.3rem;
|
||||
@ -25,6 +25,29 @@ body {
|
||||
padding: 3px;
|
||||
border-radius: 10px;
|
||||
border: solid 1px black;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
label {
|
||||
width: 100%;
|
||||
input, textarea, select {
|
||||
width: 100%;
|
||||
border:none;
|
||||
background-image:none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
background: white;
|
||||
box-shadow: none;
|
||||
min-height: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
textarea {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
div.conquer-interface-element-padded {
|
||||
width: calc(100% - 60px);
|
||||
@ -32,6 +55,9 @@ body {
|
||||
padding-right: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
&.conquer-display-none {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
div.create-node-slide {
|
||||
display: flex;
|
||||
|
File diff suppressed because one or more lines are too long
@ -10,11 +10,30 @@
|
||||
<body>
|
||||
<div id="conquer-overlay-transparent-template" class="conquer-overlay-transparent conquer-display-none">
|
||||
</div>
|
||||
<div id="conquer-new-node-form-creation-template" class="conquer-new-node-form-creation conquer-display-none conquer-interface-element-padded">
|
||||
<form>
|
||||
<p class="conquer-error conquer-display-none"></p>
|
||||
<label>Tipo de Nodo<br/>
|
||||
<select class="conquer-node-type">
|
||||
<option value="normal">Normal (Conquistable)</option>
|
||||
</select>
|
||||
</label>
|
||||
<div>
|
||||
<label>Nuevo Nombre de Nodo<br/>
|
||||
<input type="text" class="conquer-node-name"/></label>
|
||||
</div>
|
||||
<label>Descripción de la Zona<br/>
|
||||
<textarea class="conquer-node-description"></textarea></label>
|
||||
<div>
|
||||
<button class="new-node-form-submit">Finalizar creación de nodo.</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="create-node-slide conquer-display-none" id="create-node-slide">
|
||||
<button id="create-node-new-node">Autogenerado.</button>
|
||||
<button id="create-node-exit">Salir del modo crear nodo.</button>
|
||||
</div>
|
||||
<div id="conquer-interface-element-padded-template" class="conquer-interface-element-padded">
|
||||
<div id="conquer-interface-element-padded-template" class="conquer-interface-element-padded conquer-display-none">
|
||||
</div>
|
||||
<div id="conquer-interface-with-top-bar-template" class="conquer-self-player conquer-display-none">
|
||||
<div class="conquer-top-bar">
|
||||
|
@ -1,5 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"outDir": "./public/js/",
|
||||
"noImplicitAny": true,
|
||||
"module": "es2020",
|
||||
|
Loading…
Reference in New Issue
Block a user