Implement backend and frontend API code for merits.

This commit is contained in:
projectmoon 2021-01-17 13:09:17 +00:00
parent 4980b2e33b
commit 95ad1c6e28
7 changed files with 337 additions and 6 deletions

View File

@ -57,6 +57,13 @@ message UpdateSkillSpecializationsRequest {
repeated string specializations = 2; repeated string specializations = 2;
} }
//Update all merits on the character sheet by overwriting them.
//Primarily for the web UI.
message UpdateMeritsRequest {
CharacterIdentifier character = 1;
repeated CofdSheet.Merit merits = 2;
}
//Add a Condition to a Chronicles of Darkness character sheet. //Add a Condition to a Chronicles of Darkness character sheet.
message AddConditionRequest { message AddConditionRequest {
string character_username = 1; string character_username = 1;

View File

@ -208,6 +208,34 @@ export namespace UpdateSkillSpecializationsRequest {
} }
} }
export class UpdateMeritsRequest extends jspb.Message {
hasCharacter(): boolean;
clearCharacter(): void;
getCharacter(): CharacterIdentifier | undefined;
setCharacter(value?: CharacterIdentifier): void;
clearMeritsList(): void;
getMeritsList(): Array<cofd_pb.CofdSheet.Merit>;
setMeritsList(value: Array<cofd_pb.CofdSheet.Merit>): void;
addMerits(value?: cofd_pb.CofdSheet.Merit, index?: number): cofd_pb.CofdSheet.Merit;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateMeritsRequest.AsObject;
static toObject(includeInstance: boolean, msg: UpdateMeritsRequest): UpdateMeritsRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UpdateMeritsRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UpdateMeritsRequest;
static deserializeBinaryFromReader(message: UpdateMeritsRequest, reader: jspb.BinaryReader): UpdateMeritsRequest;
}
export namespace UpdateMeritsRequest {
export type AsObject = {
character?: CharacterIdentifier.AsObject,
meritsList: Array<cofd_pb.CofdSheet.Merit.AsObject>,
}
}
export class AddConditionRequest extends jspb.Message { export class AddConditionRequest extends jspb.Message {
getCharacterUsername(): string; getCharacterUsername(): string;
setCharacterUsername(value: string): void; setCharacterUsername(value: string): void;

View File

@ -22,6 +22,7 @@ goog.exportSymbol('proto.models.proto.cofd.api.CharacterIdentifier', null, globa
goog.exportSymbol('proto.models.proto.cofd.api.RemoveConditionRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.RemoveConditionRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateAttributeRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.UpdateAttributeRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateBasicInfoRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.UpdateBasicInfoRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateMeritsRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest', null, global);
goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillValueRequest', null, global); goog.exportSymbol('proto.models.proto.cofd.api.UpdateSkillValueRequest', null, global);
@ -172,6 +173,27 @@ if (goog.DEBUG && !COMPILED) {
*/ */
proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest.displayName = 'proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest'; proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest.displayName = 'proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest';
} }
/**
* 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.models.proto.cofd.api.UpdateMeritsRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, proto.models.proto.cofd.api.UpdateMeritsRequest.repeatedFields_, null);
};
goog.inherits(proto.models.proto.cofd.api.UpdateMeritsRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.displayName = 'proto.models.proto.cofd.api.UpdateMeritsRequest';
}
/** /**
* Generated by JsPbCodeGenerator. * Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a * @param {Array=} opt_data Optional initial data array, typically from a
@ -1646,6 +1668,217 @@ proto.models.proto.cofd.api.UpdateSkillSpecializationsRequest.prototype.clearSpe
/**
* List of repeated fields within this message type.
* @private {!Array<number>}
* @const
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.repeatedFields_ = [2];
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.models.proto.cofd.api.UpdateMeritsRequest.prototype.toObject = function(opt_includeInstance) {
return proto.models.proto.cofd.api.UpdateMeritsRequest.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.models.proto.cofd.api.UpdateMeritsRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.toObject = function(includeInstance, msg) {
var f, obj = {
character: (f = msg.getCharacter()) && proto.models.proto.cofd.api.CharacterIdentifier.toObject(includeInstance, f),
meritsList: jspb.Message.toObjectList(msg.getMeritsList(),
cofd_pb.CofdSheet.Merit.toObject, includeInstance)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.models.proto.cofd.api.UpdateMeritsRequest;
return proto.models.proto.cofd.api.UpdateMeritsRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.models.proto.cofd.api.UpdateMeritsRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new proto.models.proto.cofd.api.CharacterIdentifier;
reader.readMessage(value,proto.models.proto.cofd.api.CharacterIdentifier.deserializeBinaryFromReader);
msg.setCharacter(value);
break;
case 2:
var value = new cofd_pb.CofdSheet.Merit;
reader.readMessage(value,cofd_pb.CofdSheet.Merit.deserializeBinaryFromReader);
msg.addMerits(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.models.proto.cofd.api.UpdateMeritsRequest.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.models.proto.cofd.api.UpdateMeritsRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getCharacter();
if (f != null) {
writer.writeMessage(
1,
f,
proto.models.proto.cofd.api.CharacterIdentifier.serializeBinaryToWriter
);
}
f = message.getMeritsList();
if (f.length > 0) {
writer.writeRepeatedMessage(
2,
f,
cofd_pb.CofdSheet.Merit.serializeBinaryToWriter
);
}
};
/**
* optional CharacterIdentifier character = 1;
* @return {?proto.models.proto.cofd.api.CharacterIdentifier}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.getCharacter = function() {
return /** @type{?proto.models.proto.cofd.api.CharacterIdentifier} */ (
jspb.Message.getWrapperField(this, proto.models.proto.cofd.api.CharacterIdentifier, 1));
};
/**
* @param {?proto.models.proto.cofd.api.CharacterIdentifier|undefined} value
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest} returns this
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.setCharacter = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest} returns this
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.clearCharacter = function() {
return this.setCharacter(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.hasCharacter = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* repeated models.proto.cofd.CofdSheet.Merit merits = 2;
* @return {!Array<!proto.models.proto.cofd.CofdSheet.Merit>}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.getMeritsList = function() {
return /** @type{!Array<!proto.models.proto.cofd.CofdSheet.Merit>} */ (
jspb.Message.getRepeatedWrapperField(this, cofd_pb.CofdSheet.Merit, 2));
};
/**
* @param {!Array<!proto.models.proto.cofd.CofdSheet.Merit>} value
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest} returns this
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.setMeritsList = function(value) {
return jspb.Message.setRepeatedWrapperField(this, 2, value);
};
/**
* @param {!proto.models.proto.cofd.CofdSheet.Merit=} opt_value
* @param {number=} opt_index
* @return {!proto.models.proto.cofd.CofdSheet.Merit}
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.addMerits = function(opt_value, opt_index) {
return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.models.proto.cofd.CofdSheet.Merit, opt_index);
};
/**
* Clears the list making it empty but non-null.
* @return {!proto.models.proto.cofd.api.UpdateMeritsRequest} returns this
*/
proto.models.proto.cofd.api.UpdateMeritsRequest.prototype.clearMeritsList = function() {
return this.setMeritsList([]);
};
if (jspb.Message.GENERATE_TO_OBJECT) { if (jspb.Message.GENERATE_TO_OBJECT) {

View File

@ -1,12 +1,8 @@
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import { ApiResult, UpdateBasicInfoRequest, UpdateAttributeRequest, UpdateSkillValueRequest } from "../_proto/cofd_api_pb"; import { ApiResult, UpdateBasicInfoRequest, UpdateAttributeRequest, UpdateSkillValueRequest, UpdateMeritsRequest } from "../_proto/cofd_api_pb";
const PROTOBUF_CONTENT_TYPE = { 'Content-Type': 'application/x-protobuf' }; const PROTOBUF_CONTENT_TYPE = { 'Content-Type': 'application/x-protobuf' };
function staticImplements<T>() {
return <U extends T>(constructor: U) => { constructor };
}
async function makeRequest<T extends jspb.Message>(uri: string, params: T): Promise<Uint8Array> { async function makeRequest<T extends jspb.Message>(uri: string, params: T): Promise<Uint8Array> {
let resp = await fetch(uri, { let resp = await fetch(uri, {
method: 'POST', method: 'POST',
@ -32,3 +28,8 @@ export async function updateBasicInfo(params: UpdateBasicInfoRequest): Promise<A
let data = await makeRequest('/api/rpc/cofd/update_basic_info', params); let data = await makeRequest('/api/rpc/cofd/update_basic_info', params);
return ApiResult.deserializeBinary(data); return ApiResult.deserializeBinary(data);
} }
export async function updateMerits(params: UpdateMeritsRequest): Promise<ApiReslut> {
let data = await makeRequest('/api/rpc/cofd/update_merits', params);
return ApiResult.deserializeBinary(data);
}

View File

@ -1,4 +1,5 @@
import { CharacterIdentifier, UpdateBasicInfoRequest, UpdateSkillValueRequest, UpdateAttributeRequest } from "../../_proto/cofd_api_pb"; import { CharacterIdentifier, UpdateBasicInfoRequest, UpdateSkillValueRequest, UpdateAttributeRequest, UpdateMeritsRequest } from "../../_proto/cofd_api_pb";
import { CofdSheet } from "../../_proto/cofd_pb";
import * as api from "../api"; import * as api from "../api";
// This is the scripting for the edit character page, which submits // This is the scripting for the edit character page, which submits
@ -94,9 +95,45 @@ import * as api from "../api";
}); });
} }
function setupMerits() {
async function updateMerits() {
let merits: CofdSheet.Merit[] =
Array.from(document.querySelectorAll<HTMLInputElement>('#merits input[class="merit"]')).map(input => {
const dotsInput = input.parentElement?.querySelector<HTMLInputElement>('input[class="merit-dots"]');
const merit = new CofdSheet.Merit();
merit.setName(input.value);
merit.setDots(parseInt(dotsInput?.value ?? "0"));
return merit;
});
const params = new UpdateMeritsRequest();
params.setCharacter(characterId());
params.setMeritsList(merits);
let resp = await api.updateMerits(params);
console.log("got a response back", resp);
}
const meritNames = document.querySelectorAll('#merits input[class="merit"]');
const meritDots = document.querySelectorAll('#merits input[class="merit-dots"]');
meritNames.forEach(input => {
console.log('got an input', input);
input.addEventListener('blur', updateMerits);
});
meritDots.forEach(input => {
console.log('got an input', input);
input.addEventListener('change', updateMerits);
});
}
setupAttributes(); setupAttributes();
setupSkills(); setupSkills();
setupBasicInfo(); setupBasicInfo();
setupMerits();
})().catch(e => { })().catch(e => {
alert(e); alert(e);
}); });

View File

@ -11,6 +11,7 @@ pub(crate) fn routes() -> Vec<rocket::Route> {
cofd::update_attribute_value, cofd::update_attribute_value,
cofd::update_skill, cofd::update_skill,
cofd::update_skill_value, cofd::update_skill_value,
cofd::update_merits,
cofd::add_condition, cofd::add_condition,
cofd::remove_condition cofd::remove_condition
] ]

View File

@ -163,6 +163,30 @@ pub(super) async fn update_skill_value<'a>(
})) }))
} }
#[post("/rpc/cofd/update_skill_value", data = "<req>")]
pub(super) async fn update_merits<'a>(
req: Proto<UpdateMeritsRequest>,
conn: TenebrousDbConn<'_>,
logged_in_user: Option<&User>,
) -> Result<Proto<ApiResult>, Error> {
let mut character = load_character(
&conn,
logged_in_user,
&req.character.owner(),
req.character.id(),
)
.await?;
let mut sheet: CofdSheet = character.try_deserialize()?;
sheet.merits = req.merits.clone();
println!("updated merits");
character.update_data(&sheet)?;
conn.update_character_sheet(&character).await?;
Ok(Proto(ApiResult::success()))
}
#[put("/rpc/cofd/add_condition", data = "<info>")] #[put("/rpc/cofd/add_condition", data = "<info>")]
pub(super) fn add_condition<'a>(info: Proto<AddConditionRequest>) -> &'a str { pub(super) fn add_condition<'a>(info: Proto<AddConditionRequest>) -> &'a str {
"lol" "lol"