Enhance transaction fetching with pagination support and improved error handling
Signed-off-by: Dmitrii <computer@yawaflua.tech> Took 22 minutes
This commit is contained in:
@@ -3,6 +3,7 @@ package git.yawaflua.tech.spmega.client.ui;
|
||||
import git.yawaflua.tech.spmega.client.ui.service.BackendAuthenticator;
|
||||
import git.yawaflua.tech.spmega.client.ui.service.BankDatabase.LocalTransaction;
|
||||
import git.yawaflua.tech.spmega.client.ui.service.BankUiService;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
@@ -19,6 +20,8 @@ public class TransactionHistoryScreen extends Screen {
|
||||
private final List<LocalTransaction> transactions = new ArrayList<>();
|
||||
private boolean loading = true;
|
||||
private String errorMessage = "";
|
||||
private int scrollOffset = 0;
|
||||
private int currentPage = 1;
|
||||
|
||||
public TransactionHistoryScreen(Screen parent, String cardId, String cardTitle) {
|
||||
super(Text.literal("История транзакций"));
|
||||
@@ -36,40 +39,112 @@ public class TransactionHistoryScreen extends Screen {
|
||||
.dimensions(centerX - 60, startY + 10, 120, 20)
|
||||
.build());
|
||||
|
||||
// Scroll Up/Down buttons
|
||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("▲"), button -> scrollUp())
|
||||
.dimensions(centerX + 140, 65, 20, 20)
|
||||
.build());
|
||||
|
||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("▼"), button -> scrollDown())
|
||||
.dimensions(centerX + 140, 155, 20, 20)
|
||||
.build());
|
||||
|
||||
// Prev Page button
|
||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("<"), button -> {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
scrollOffset = 0;
|
||||
loadTransactions();
|
||||
}
|
||||
}).dimensions(centerX - 100, startY - 15, 30, 20).build());
|
||||
|
||||
// Next Page button
|
||||
this.addDrawableChild(ButtonWidget.builder(Text.literal(">"), button -> {
|
||||
currentPage++;
|
||||
scrollOffset = 0;
|
||||
loadTransactions();
|
||||
}).dimensions(centerX + 70, startY - 15, 30, 20).build());
|
||||
|
||||
loadTransactions();
|
||||
}
|
||||
|
||||
private void scrollUp() {
|
||||
if (scrollOffset > 0) {
|
||||
scrollOffset--;
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollDown() {
|
||||
if (scrollOffset < Math.max(0, transactions.size() - 6)) {
|
||||
scrollOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// Support pre-1.20.2 mouse scroll
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
|
||||
if (amount > 0) {
|
||||
scrollUp();
|
||||
} else if (amount < 0) {
|
||||
scrollDown();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Support 1.20.2+ mouse scroll
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
|
||||
if (verticalAmount > 0) {
|
||||
scrollUp();
|
||||
} else if (verticalAmount < 0) {
|
||||
scrollDown();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void loadTransactions() {
|
||||
loading = true;
|
||||
errorMessage = "";
|
||||
transactions.clear();
|
||||
|
||||
UiNotifications.instance().show(Text.literal("Загрузка..."));
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
System.out.println("[SPMEGA] Transaction history loading started for card: " + cardId + ", page: " + currentPage);
|
||||
List<LocalTransaction> list = null;
|
||||
git.yawaflua.tech.spmega.ModConfig config = git.yawaflua.tech.spmega.SPMega.getConfig();
|
||||
if (config != null && config.allowBackend()) {
|
||||
try {
|
||||
list = BackendAuthenticator.fetchTransactionsFromBackend(cardId);
|
||||
list = BackendAuthenticator.fetchTransactionsFromBackend(cardId, currentPage);
|
||||
System.out.println("[SPMEGA] Fetched " + (list != null ? list.size() : "null") + " transactions from backend.");
|
||||
} catch (Exception e) {
|
||||
System.err.println("[SPMEGA] Failed to fetch transactions from server, falling back to DB: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (list == null) {
|
||||
list = BankUiService.instance().getDatabase().loadTransferHistory(cardId);
|
||||
System.out.println("[SPMEGA] Loaded " + (list != null ? list.size() : "null") + " transactions from database.");
|
||||
}
|
||||
|
||||
List<LocalTransaction> finalList = list;
|
||||
if (this.client != null) {
|
||||
this.client.execute(() -> {
|
||||
List<LocalTransaction> finalList = list != null ? list : new ArrayList<>();
|
||||
MinecraftClient minecraftClient = MinecraftClient.getInstance();
|
||||
if (minecraftClient != null) {
|
||||
minecraftClient.execute(() -> {
|
||||
System.out.println("[SPMEGA] Setting transactions list on main thread. Count: " + finalList.size());
|
||||
this.transactions.addAll(finalList);
|
||||
this.scrollOffset = 0;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
System.out.println("[SPMEGA] MinecraftClient.getInstance() is null in loadTransactions callback!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (this.client != null) {
|
||||
this.client.execute(() -> {
|
||||
System.err.println("[SPMEGA] Outer exception in loadTransactions: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
MinecraftClient minecraftClient = MinecraftClient.getInstance();
|
||||
if (minecraftClient != null) {
|
||||
minecraftClient.execute(() -> {
|
||||
this.errorMessage = e.getMessage();
|
||||
this.loading = false;
|
||||
});
|
||||
@@ -91,30 +166,38 @@ public class TransactionHistoryScreen extends Screen {
|
||||
|
||||
int centerX = this.width / 2;
|
||||
int startY = 50;
|
||||
int bottomStartY = this.height / 2 + 50;
|
||||
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, this.title, centerX, 20, 0xFFFFFF);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Карта: " + cardTitle), centerX, 35, 0xBFBFBF);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, this.title, centerX, 20, 0xFFFFFFFF);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Карта: " + cardTitle), centerX, 35, 0xFFBFBFBF);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Стр. " + currentPage), centerX, bottomStartY - 9, 0xFFFFFFFF);
|
||||
|
||||
if (loading) {
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Загрузка транзакций..."), centerX, this.height / 2 - 10, 0xCCCCCC);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Загрузка транзакций..."), centerX, this.height / 2 - 10, 0xFFCCCCCC);
|
||||
} else if (!errorMessage.isEmpty()) {
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Ошибка: " + errorMessage), centerX, this.height / 2 - 10, 0xFF5555);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Ошибка: " + errorMessage), centerX, this.height / 2 - 10, 0xFFFF5555);
|
||||
} else if (transactions.isEmpty()) {
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Транзакций не найдено"), centerX, this.height / 2 - 10, 0xCCCCCC);
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal("Транзакций не найдено"), centerX, this.height / 2 - 10, 0xFFCCCCCC);
|
||||
} else {
|
||||
int y = startY + 15;
|
||||
for (int i = 0; i < Math.min(6, transactions.size()); i++) {
|
||||
LocalTransaction tx = transactions.get(i);
|
||||
int limit = Math.min(6, transactions.size() - scrollOffset);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
LocalTransaction tx = transactions.get(scrollOffset + i);
|
||||
|
||||
String amountText = tx.amount() + " АР";
|
||||
String dateText = tx.createdAt();
|
||||
if (dateText == null) {
|
||||
dateText = "";
|
||||
}
|
||||
if (dateText.length() > 19) {
|
||||
dateText = dateText.substring(0, 19).replace("T", " ");
|
||||
}
|
||||
String commentText = tx.comment().isEmpty() ? "" : " (" + tx.comment() + ")";
|
||||
|
||||
String line = String.format("%s -> %s | %s%s", dateText, tx.receiver(), amountText, commentText);
|
||||
int color = tx.status().equalsIgnoreCase("SUCCESS") ? 0x55FF55 : 0xFF5555;
|
||||
|
||||
String status = tx.status();
|
||||
int color = (status != null && status.equalsIgnoreCase("SUCCESS")) ? 0xFF55FF55 : 0xFFFF5555;
|
||||
|
||||
context.drawCenteredTextWithShadow(this.textRenderer, Text.literal(line), centerX, y, color);
|
||||
y += 18;
|
||||
|
||||
+77
-10
@@ -1,5 +1,6 @@
|
||||
package git.yawaflua.tech.spmega.client.ui.service;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
@@ -314,9 +315,11 @@ public class BackendAuthenticator {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId) throws IOException, InterruptedException {
|
||||
public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId, int page) throws IOException, InterruptedException {
|
||||
System.out.println("[SPMEGA] fetchTransactionsFromBackend called for cardId: " + cardId + ", page: " + page);
|
||||
ModConfig config = SPMega.getConfig();
|
||||
if (config == null || !config.allowBackend()) {
|
||||
System.out.println("[SPMEGA] fetchTransactionsFromBackend aborted: config is null or backend not allowed.");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@@ -328,7 +331,8 @@ public class BackendAuthenticator {
|
||||
apiDomain = apiDomain.substring(0, apiDomain.length() - 1);
|
||||
}
|
||||
|
||||
String url = apiDomain + "/api/v1/transactions?cardId=" + cardId;
|
||||
String url = apiDomain + "/api/v1/transactions?p=" + page;
|
||||
System.out.println("[SPMEGA] Requesting transactions from URL: " + url);
|
||||
|
||||
HttpClient httpClient = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
|
||||
@@ -338,22 +342,85 @@ public class BackendAuthenticator {
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println("[SPMEGA] Response status code: " + response.statusCode());
|
||||
if (response.statusCode() != 200) {
|
||||
System.err.println("[SPMEGA] Failed response body: " + response.body());
|
||||
throw new IOException("Server returned status code " + response.statusCode() + " on fetch transactions.");
|
||||
}
|
||||
|
||||
com.google.gson.JsonArray transactionsArray = JsonParser.parseString(response.body()).getAsJsonArray();
|
||||
String body = response.body();
|
||||
System.out.println("[SPMEGA] Response body: " + (body.length() > 500 ? body.substring(0, 500) + "..." : body));
|
||||
|
||||
JsonElement parsed = JsonParser.parseString(body);
|
||||
com.google.gson.JsonArray transactionsArray = null;
|
||||
|
||||
if (parsed.isJsonArray()) {
|
||||
transactionsArray = parsed.getAsJsonArray();
|
||||
} else if (parsed.isJsonObject()) {
|
||||
JsonObject obj = parsed.getAsJsonObject();
|
||||
if (obj.has("$values") && obj.get("$values").isJsonArray()) {
|
||||
transactionsArray = obj.getAsJsonArray("$values");
|
||||
} else if (obj.has("transactions") && obj.get("transactions").isJsonArray()) {
|
||||
transactionsArray = obj.getAsJsonArray("transactions");
|
||||
} else if (obj.has("values") && obj.get("values").isJsonArray()) {
|
||||
transactionsArray = obj.getAsJsonArray("values");
|
||||
}
|
||||
}
|
||||
|
||||
if (transactionsArray == null) {
|
||||
System.err.println("[SPMEGA] Response body is not a JSON array or wrapped array: " + body);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
System.out.println("[SPMEGA] Total transactions returned from server: " + transactionsArray.size());
|
||||
List<BankDatabase.LocalTransaction> list = new ArrayList<>();
|
||||
for (com.google.gson.JsonElement el : transactionsArray) {
|
||||
JsonObject json = el.getAsJsonObject();
|
||||
String receiver = json.has("receiver") ? json.get("receiver").getAsString() : (json.has("recipient") ? json.get("recipient").getAsString() : "unknown");
|
||||
long amount = json.has("amount") ? json.get("amount").getAsLong() : 0L;
|
||||
String comment = json.has("comment") && !json.get("comment").isJsonNull() ? json.get("comment").getAsString() : "";
|
||||
String status = json.has("status") ? json.get("status").getAsString() : "SUCCESS";
|
||||
String createdAt = json.has("createdAt") ? json.get("createdAt").getAsString() : (json.has("created_at") ? json.get("created_at").getAsString() : "");
|
||||
try {
|
||||
JsonObject json = el.getAsJsonObject();
|
||||
|
||||
list.add(new BankDatabase.LocalTransaction(receiver, amount, comment, status, createdAt));
|
||||
// Check cardId filter
|
||||
String senderCardNumber = "";
|
||||
if (json.has("senderCardNumber") && !json.get("senderCardNumber").isJsonNull()) {
|
||||
senderCardNumber = json.get("senderCardNumber").getAsString();
|
||||
}
|
||||
|
||||
if (!senderCardNumber.equalsIgnoreCase(cardId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String receiver = "unknown";
|
||||
if (json.has("receiver") && !json.get("receiver").isJsonNull()) {
|
||||
receiver = json.get("receiver").getAsString();
|
||||
} else if (json.has("receiverName") && !json.get("receiverName").isJsonNull()) {
|
||||
receiver = json.get("receiverName").getAsString();
|
||||
} else if (json.has("receiverCardNumber") && !json.get("receiverCardNumber").isJsonNull()) {
|
||||
receiver = json.get("receiverCardNumber").getAsString();
|
||||
} else if (json.has("recipient") && !json.get("recipient").isJsonNull()) {
|
||||
receiver = json.get("recipient").getAsString();
|
||||
}
|
||||
|
||||
long amount = json.has("amount") ? json.get("amount").getAsLong() : 0L;
|
||||
String comment = json.has("comment") && !json.get("comment").isJsonNull() ? json.get("comment").getAsString() : "";
|
||||
String status = json.has("status") && !json.get("status").isJsonNull() ? json.get("status").getAsString() : "SUCCESS";
|
||||
|
||||
String createdAt = "";
|
||||
if (json.has("transactionDate") && !json.get("transactionDate").isJsonNull()) {
|
||||
createdAt = json.get("transactionDate").getAsString();
|
||||
} else if (json.has("createdAt") && !json.get("createdAt").isJsonNull()) {
|
||||
createdAt = json.get("createdAt").getAsString();
|
||||
} else if (json.has("created_at") && !json.get("created_at").isJsonNull()) {
|
||||
createdAt = json.get("created_at").getAsString();
|
||||
}
|
||||
|
||||
System.out.printf("[SPMEGA] Parsed transaction: %s -> %s, amount=%d, comment=%s, status=%s%n",
|
||||
createdAt, receiver, amount, comment, status);
|
||||
|
||||
list.add(new BankDatabase.LocalTransaction(receiver, amount, comment, status, createdAt));
|
||||
} catch (Exception parseEx) {
|
||||
System.err.println("[SPMEGA] Error parsing transaction element: " + parseEx.getMessage());
|
||||
}
|
||||
}
|
||||
System.out.println("[SPMEGA] Returning " + list.size() + " filtered transactions for card " + cardId);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user