5 Commits

Author SHA1 Message Date
bdf413d494 Release 0.14.0 2022-09-08 03:23:04 +03:00
7a1a73592e bump dependencies 2022-09-08 03:21:14 +03:00
b1ac894ccf add action button to suggest table
Also replace functions with lambdas
2022-07-15 14:15:10 +03:00
6023e86570 Release 0.13.6 2022-06-23 04:41:51 +03:00
a4ab1e49be reset bis links for empty strings 2022-06-23 04:39:21 +03:00
15 changed files with 87 additions and 75 deletions

View File

@ -25,7 +25,7 @@ jobs:
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 18
- name: create dist - name: create dist
run: make dist run: make dist
- name: release - name: release

View File

@ -17,6 +17,6 @@ jobs:
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 18
- name: run tests - name: run tests
run: make tests run: make tests

View File

@ -1,3 +0,0 @@
* [x] items improvements
* [x] multiple parties support
* [ ] pretty UI

View File

@ -2,7 +2,7 @@ organization := "me.arcanis"
name := "ffxivbis" name := "ffxivbis"
scalaVersion := "2.13.6" scalaVersion := "2.13.8"
scalacOptions ++= Seq("-deprecation", "-feature") scalacOptions ++= Seq("-deprecation", "-feature")

View File

@ -1,5 +1,5 @@
val AkkaVersion = "2.6.19" val AkkaVersion = "2.6.20"
val AkkaHttpVersion = "10.2.9" val AkkaHttpVersion = "10.2.10"
val ScalaTestVersion = "3.2.12" val ScalaTestVersion = "3.2.12"
val SlickVersion = "3.3.3" val SlickVersion = "3.3.3"
@ -10,7 +10,7 @@ libraryDependencies += "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion
libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.7.0" libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.8.0"
libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0" libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0"
libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.3" libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.3"
@ -18,9 +18,9 @@ libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
libraryDependencies += "org.playframework.anorm" %% "anorm" % "2.6.10" libraryDependencies += "org.playframework.anorm" %% "anorm" % "2.6.10"
libraryDependencies += "com.zaxxer" % "HikariCP" % "5.0.1" exclude("org.slf4j", "slf4j-api") libraryDependencies += "com.zaxxer" % "HikariCP" % "5.0.1" exclude("org.slf4j", "slf4j-api")
libraryDependencies += "org.flywaydb" % "flyway-core" % "8.5.12" libraryDependencies += "org.flywaydb" % "flyway-core" % "9.2.2"
libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.36.0.3" libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.39.2.1"
libraryDependencies += "org.postgresql" % "postgresql" % "42.3.6" libraryDependencies += "org.postgresql" % "postgresql" % "42.5.0"
libraryDependencies += "org.mindrot" % "jbcrypt" % "0.4" libraryDependencies += "org.mindrot" % "jbcrypt" % "0.4"
libraryDependencies += "com.google.guava" % "guava" % "31.0.1-jre" libraryDependencies += "com.google.guava" % "guava" % "31.0.1-jre"

View File

@ -1 +1 @@
sbt.version = 1.5.8 sbt.version = 1.7.1

View File

@ -0,0 +1 @@
update players set bis_link = null where bis_link = '';

View File

@ -207,8 +207,8 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
updateBisDialog.modal("hide"); updateBisDialog.modal("hide");
return true; // action expects boolean result return true; // action expects boolean result
@ -247,9 +247,9 @@
url: `/api/v1/party/${partyId}`, url: `/api/v1/party/${partyId}`,
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: function (data) { success: response => {
const items = data.map(function (player) { const items = response.map(player => {
return player.bis.map(function (loot) { return player.bis.map(loot => {
return { return {
nick: player.nick, nick: player.nick,
job: player.job, job: player.job,
@ -258,12 +258,12 @@
}; };
}); });
}); });
const payload = items.reduce(function (left, right) { return left.concat(right); }, []); const payload = items.reduce((left, right) => { return left.concat(right); }, []);
table.bootstrapTable("load", payload); table.bootstrapTable("load", payload);
table.bootstrapTable("uncheckAll"); table.bootstrapTable("uncheckAll");
table.bootstrapTable("hideLoading"); table.bootstrapTable("hideLoading");
const options = data.map(function (player) { const options = response.map(player => {
const option = document.createElement("option"); const option = document.createElement("option");
option.innerText = formatPlayerId(player); option.innerText = formatPlayerId(player);
option.dataset.nick = player.nick; option.dataset.nick = player.nick;
@ -272,13 +272,13 @@
}); });
playerInput.empty().append(options); playerInput.empty().append(options);
}, },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
} }
function removePiece() { function removePiece() {
const pieces = table.bootstrapTable("getSelections"); const pieces = table.bootstrapTable("getSelections");
pieces.map(function (loot) { pieces.map(loot => {
$.ajax({ $.ajax({
url: `/api/v1/party/${partyId}/bis`, url: `/api/v1/party/${partyId}/bis`,
data: JSON.stringify({ data: JSON.stringify({
@ -296,8 +296,8 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
}); });
} }
@ -325,8 +325,8 @@
}), }),
type: "PUT", type: "PUT",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
updateBisDialog.modal("hide"); updateBisDialog.modal("hide");
return true; // action expects boolean result return true; // action expects boolean result
@ -342,7 +342,7 @@
return false; // should not happen return false; // should not happen
} }
$(function () { $(() => {
setupFormClear(updateBisDialog, reset); setupFormClear(updateBisDialog, reset);
setupRemoveButton(table, removeButton); setupRemoveButton(table, removeButton);
@ -353,8 +353,8 @@
hideControls(); hideControls();
updateBisButton.click(function () { reset(); }); updateBisButton.click(() => { reset(); });
addPieceButton.click(function () { reset(); }); addPieceButton.click(() => { reset(); });
table.bootstrapTable({}); table.bootstrapTable({});
reload(); reload();

View File

@ -88,7 +88,7 @@
<div id="add-loot-dialog" tabindex="-1" role="dialog" class="modal fade"> <div id="add-loot-dialog" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
<form class="modal-content" action="javascript:" onsubmit="addLoot()"> <form class="modal-content" action="javascript:" onsubmit="addLootModal()">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">add looted piece</h4> <h4 class="modal-title">add looted piece</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
@ -132,6 +132,7 @@
<table id="stats" class="table table-striped table-hover"> <table id="stats" class="table table-striped table-hover">
<thead class="table-primary"> <thead class="table-primary">
<tr> <tr>
<th data-formatter="addLootFormatter"></th>
<th data-field="nick">nick</th> <th data-field="nick">nick</th>
<th data-field="job">job</th> <th data-field="job">job</th>
<th data-field="isRequired">required</th> <th data-field="isRequired">required</th>
@ -198,30 +199,40 @@
const pieceTypeInput = $("#piece-type"); const pieceTypeInput = $("#piece-type");
const playerInput = $("#player"); const playerInput = $("#player");
function addLoot() { function addLoot(nick, job) {
const player = getCurrentOption(playerInput);
$.ajax({ $.ajax({
url: `/api/v1/party/${partyId}/loot`, url: `/api/v1/party/${partyId}/loot`,
data: JSON.stringify({ data: JSON.stringify({
action: "add", action: "add",
piece: { piece: {
pieceType: pieceTypeInput.val(), pieceType: pieceTypeInput.val(),
job: player.dataset.job, job: job,
piece: pieceInput.val(), piece: pieceInput.val(),
}, },
playerId: { playerId: {
partyId: partyId, partyId: partyId,
nick: player.dataset.nick, nick: nick,
job: player.dataset.job, job: job,
}, },
isFreeLoot: freeLootInput.is(":checked"), isFreeLoot: freeLootInput.is(":checked"),
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => {
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
});
addLootDialog.modal("hide"); addLootDialog.modal("hide");
reload();
},
error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
});
}
function addLootFormatter(value, row, index) {
return `<button type="button" class="btn btn-primary" onclick="addLoot('${row.nick}', '${row.job}')"><i class="bi bi-plus"></i></button>`;
}
function addLootModal() {
const player = getCurrentOption(playerInput);
addLoot(player.dataset.nick, player.dataset.job);
return true; // action expects boolean result return true; // action expects boolean result
} }
@ -236,9 +247,9 @@
url: `/api/v1/party/${partyId}`, url: `/api/v1/party/${partyId}`,
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: function (data) { success: response => {
const items = data.map(function (player) { const items = response.map(player => {
return player.loot.map(function (loot) { return player.loot.map(loot => {
return { return {
nick: player.nick, nick: player.nick,
job: player.job, job: player.job,
@ -249,12 +260,12 @@
}; };
}); });
}); });
const payload = items.reduce(function (left, right) { return left.concat(right); }, []); const payload = items.reduce((left, right) => { return left.concat(right); }, []);
table.bootstrapTable("load", payload); table.bootstrapTable("load", payload);
table.bootstrapTable("uncheckAll"); table.bootstrapTable("uncheckAll");
table.bootstrapTable("hideLoading"); table.bootstrapTable("hideLoading");
const options = data.map(function (player) { const options = response.map(player => {
const option = document.createElement("option"); const option = document.createElement("option");
option.innerText = formatPlayerId(player); option.innerText = formatPlayerId(player);
option.dataset.nick = player.nick; option.dataset.nick = player.nick;
@ -263,13 +274,13 @@
}); });
playerInput.empty().append(options); playerInput.empty().append(options);
}, },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
} }
function removeLoot() { function removeLoot() {
const pieces = table.bootstrapTable("getSelections"); const pieces = table.bootstrapTable("getSelections");
pieces.map(function (loot) { pieces.map(loot => {
$.ajax({ $.ajax({
url: `/api/v1/party/${partyId}/loot`, url: `/api/v1/party/${partyId}/loot`,
data: JSON.stringify({ data: JSON.stringify({
@ -288,8 +299,8 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
}); });
} }
@ -306,8 +317,8 @@
type: "PUT", type: "PUT",
contentType: "application/json", contentType: "application/json",
dataType: "json", dataType: "json",
success: function (data) { success: response => {
const payload = data.map(function (stat) { const payload = response.map(stat => {
return { return {
nick: stat.nick, nick: stat.nick,
job: stat.job, job: stat.job,
@ -321,11 +332,11 @@
stats.bootstrapTable("uncheckAll"); stats.bootstrapTable("uncheckAll");
stats.bootstrapTable("hideLoading"); stats.bootstrapTable("hideLoading");
}, },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
} }
$(function () { $(() => {
setupFormClear(addLootDialog); setupFormClear(addLootDialog);
setupRemoveButton(table, removeButton); setupRemoveButton(table, removeButton);

View File

@ -184,8 +184,8 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
addPlayerDialog.modal("hide"); addPlayerDialog.modal("hide");
return true; // action expects boolean result return true; // action expects boolean result
@ -210,18 +210,18 @@
url: `/api/v1/party/${partyId}`, url: `/api/v1/party/${partyId}`,
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: function (data) { success: response => {
table.bootstrapTable("load", data); table.bootstrapTable("load", response);
table.bootstrapTable("uncheckAll"); table.bootstrapTable("uncheckAll");
table.bootstrapTable("hideLoading"); table.bootstrapTable("hideLoading");
}, },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
} }
function removePlayers() { function removePlayers() {
const players = table.bootstrapTable("getSelections"); const players = table.bootstrapTable("getSelections");
players.map(function (player) { players.map(player => {
$.ajax({ $.ajax({
url: `/api/v1/party/${partyId}`, url: `/api/v1/party/${partyId}`,
data: JSON.stringify({ data: JSON.stringify({
@ -234,13 +234,13 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
}); });
} }
$(function () { $(() => {
setupFormClear(addPlayerDialog); setupFormClear(addPlayerDialog);
setupRemoveButton(table, removeButton); setupRemoveButton(table, removeButton);

View File

@ -173,8 +173,8 @@
}), }),
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
addUserDialog.modal("hide"); addUserDialog.modal("hide");
return true; // action expects boolean result return true; // action expects boolean result
@ -191,28 +191,28 @@
url: `/api/v1/party/${partyId}/users`, url: `/api/v1/party/${partyId}/users`,
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: function (data) { success: response => {
table.bootstrapTable("load", data); table.bootstrapTable("load", response);
table.bootstrapTable("uncheckAll"); table.bootstrapTable("uncheckAll");
table.bootstrapTable("hideLoading"); table.bootstrapTable("hideLoading");
}, },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
} }
function removeUsers() { function removeUsers() {
const users = table.bootstrapTable("getSelections"); const users = table.bootstrapTable("getSelections");
users.map(function (user) { users.map(user => {
$.ajax({ $.ajax({
url: `/api/v1/party/${partyId}/users/${user.username}`, url: `/api/v1/party/${partyId}/users/${user.username}`,
type: "DELETE", type: "DELETE",
success: function (_) { reload(); }, success: _ => { reload(); },
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); }, error: (jqXHR, _, errorThrown) => { requestAlert(jqXHR, errorThrown); },
}); });
}); });
} }
$(function () { $(() => {
setupFormClear(addUserDialog); setupFormClear(addUserDialog);
setupRemoveButton(table, removeButton); setupRemoveButton(table, removeButton);

View File

@ -12,5 +12,6 @@
</logger> </logger>
<logger name="org.flywaydb.core.internal" level="INFO" /> <logger name="org.flywaydb.core.internal" level="INFO" />
<logger name="com.zaxxer.hikari.pool" level="INFO" /> <logger name="com.zaxxer.hikari.pool" level="INFO" />
<logger name="io.swagger" level="INFO" />
</configuration> </configuration>

View File

@ -16,12 +16,12 @@ import java.time.Instant
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
private def enumFormat[E <: Enumeration](enum: E): RootJsonFormat[E#Value] = private def enumFormat[E <: Enumeration](enumeration: E): RootJsonFormat[E#Value] =
new RootJsonFormat[E#Value] { new RootJsonFormat[E#Value] {
override def write(obj: E#Value): JsValue = obj.toString.toJson override def write(obj: E#Value): JsValue = obj.toString.toJson
override def read(json: JsValue): E#Value = json match { override def read(json: JsValue): E#Value = json match {
case JsNumber(value) => enum(value.toInt) case JsNumber(value) => enumeration(value.toInt)
case JsString(name) => enum.withName(name) case JsString(name) => enumeration.withName(name)
case other => deserializationError(s"String or number expected, got $other") case other => deserializationError(s"String or number expected, got $other")
} }
} }

View File

@ -75,6 +75,8 @@ trait XivApi extends RequestExecutor {
object XivApi { object XivApi {
private val defaultShop = JsObject("IsUnique" -> JsNumber(1), "StackSize" -> JsNumber(999))
private def parseXivapiJsonToShop( private def parseXivapiJsonToShop(
js: JsObject js: JsObject
)(implicit executionContext: ExecutionContext): Future[Map[Long, (String, Long)]] = { )(implicit executionContext: ExecutionContext): Future[Map[Long, (String, Long)]] = {
@ -128,7 +130,7 @@ object XivApi {
if (index == "crafted" && shopId == -1L) PieceType.Crafted if (index == "crafted" && shopId == -1L) PieceType.Crafted
else else
Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject) Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject)
.getOrElse(throw new Exception(s"${shopMap(shopId).fields(s"ItemCost$index")}, $index")) .getOrElse(defaultShop)
.getFields("IsUnique", "StackSize") match { .getFields("IsUnique", "StackSize") match {
case Seq(JsNumber(isUnique), JsNumber(stackSize)) => case Seq(JsNumber(isUnique), JsNumber(stackSize)) =>
if (isUnique == 1 || stackSize.toLong != 999) PieceType.Tome // either upgraded gear or tomes found if (isUnique == 1 || stackSize.toLong != 999) PieceType.Tome // either upgraded gear or tomes found

View File

@ -1 +1 @@
version := "0.13.5" version := "0.14.0"