Refactor card management to support asynchronous operations and improve error handling
Java CI / build (push) Successful in 47s
.NET+Docker CI/CD / Unit and Integration tests (push) Failing after 23s
.NET+Docker CI/CD / Push Docker image to ghcr.io (push) Has been skipped

Signed-off-by: Dmitrii <computer@yawaflua.tech>

Took 3 minutes
This commit is contained in:
Dmitrii
2026-06-29 03:03:59 +03:00
parent 25929286c6
commit 77e525b890
9 changed files with 279 additions and 260 deletions
+3 -3
View File
@@ -20,11 +20,11 @@ jobs:
dotnet-version: 10.0.x dotnet-version: 10.0.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore --project backend/SpMega.Backend/SpMega.Backend.csproj
- name: Build - name: Build
run: dotnet build --no-restore run: dotnet build --no-restore --project backend/SpMega.Backend/SpMega.Backend.csproj
- name: Test - name: Test
run: dotnet test --no-build --verbosity normal run: dotnet test --no-build --verbosity normal --project backend/SpMega.Backend/SpMega.Backend.csproj
compose: compose:
name: Push Docker image to ghcr.io name: Push Docker image to ghcr.io
@@ -9,7 +9,6 @@ import net.minecraft.text.Text;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ChatListener { public class ChatListener {
@@ -57,8 +56,7 @@ public class ChatListener {
if (client.player != null) { if (client.player != null) {
String playerUuid = client.player.getUuidAsString(); String playerUuid = client.player.getUuidAsString();
CompletableFuture BankUiService.instance().addCardAsync(cardId, tokenId, playerUuid)
.supplyAsync(() -> BankUiService.instance().addCard(cardId, tokenId, playerUuid))
.thenAccept(msg -> { .thenAccept(msg -> {
if (client == null) { if (client == null) {
return; return;
@@ -26,6 +26,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -166,25 +167,13 @@ public class SPMegaClient implements ClientModInitializer {
} }
UiNotifications.instance().render(drawContext, client.textRenderer, scaledWidth, scaledHeight); UiNotifications.instance().render(drawContext, client.textRenderer, scaledWidth, scaledHeight);
}); });
// CompletableFuture.supplyAsync(() -> BackendAuthenticator.authenticate(client))
// .thenAccept(authResult -> {
// if (authResult) {
// System.out.println("SPMega: Authenticated on backend successfully.");
// } else {
// System.err.println("SPMega: Authentication on backend failed");
// }
// })
// .exceptionally(ex -> {
// System.err.println("SPMega: Authentication error: " + ex.getMessage());
// return null;
// });
} }
); );
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
if (client.player != null) { if (client.player != null) {
BankUiService.instance().refreshOnServerJoin(client.player.getUuidAsString()); BankUiService.instance().refreshOnServerJoinAsync(client.player.getUuidAsString());
} }
}); });
@@ -238,8 +227,13 @@ public class SPMegaClient implements ClientModInitializer {
}); });
new ChatListener().register(); new ChatListener().register();
BackendAuthenticator.authenticate(MinecraftClient.getInstance()); CompletableFuture.runAsync(() -> BackendAuthenticator.authenticate(MinecraftClient.getInstance()))
.exceptionally(throwable -> {
System.err.println("[SPMEGA] Error during async authenticate: " + throwable.getMessage());
return null;
});
System.out.println("Author of SPMega make it with 4 cans of monster"); System.out.println("Author of SPMega make it with 4 cans of monster");
System.out.println("If u want to see more updates - give me like 10 shekels for monster plzzz");
System.out.println("Initialized beshalom! Tieie tovim!"); System.out.println("Initialized beshalom! Tieie tovim!");
} }
} }
@@ -7,7 +7,6 @@ import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
public class AddCardScreen extends Screen { public class AddCardScreen extends Screen {
@@ -85,8 +84,7 @@ public class AddCardScreen extends Screen {
addButton.active = false; addButton.active = false;
cancelButton.active = false; cancelButton.active = false;
CompletableFuture bankUiService.addCardAsync(cardId, cardToken, playerUuid)
.supplyAsync(() -> bankUiService.addCard(cardId, cardToken, playerUuid))
.thenAccept(message -> { .thenAccept(message -> {
if (this.client == null) { if (this.client == null) {
return; return;
@@ -9,7 +9,6 @@ import net.minecraft.text.Text;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
public class CardScreen extends Screen { public class CardScreen extends Screen {
private final Screen parent; private final Screen parent;
@@ -46,25 +45,30 @@ public class CardScreen extends Screen {
} }
this.addDrawableChild(ButtonWidget.builder(Text.literal("Удалить"), button -> { this.addDrawableChild(ButtonWidget.builder(Text.literal("Удалить"), button -> {
CompletableFuture.supplyAsync(() -> { bankUiService.removeSelectedCardAsync()
bankUiService.removeSelectedCard(); .thenAccept(message -> {
return true; if (this.client != null) {
}); this.client.execute(() -> {
notifications.showMessage(bankUiService.getLastMessage()); notifications.showMessage(message);
this.clearAndInit(); this.clearAndInit();
});
}
});
}).dimensions(rightX, startY, columnWidth, 20).build()); }).dimensions(rightX, startY, columnWidth, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.literal("Обновить"), button -> { this.addDrawableChild(ButtonWidget.builder(Text.literal("Обновить"), button -> {
String playerUuid = this.client != null && this.client.player != null String playerUuid = this.client != null && this.client.player != null
? this.client.player.getUuidAsString() ? this.client.player.getUuidAsString()
: ""; : "";
CompletableFuture.supplyAsync(() -> { bankUiService.refreshSelectedCardAsync(playerUuid)
bankUiService.refreshSelectedCard(playerUuid); .thenAccept(message -> {
return true; if (this.client != null) {
}); this.client.execute(() -> {
String message = bankUiService.getLastMessage().isBlank() ? "Карта обновлена" : bankUiService.getLastMessage();
notifications.showMessage(message); notifications.showMessage(message);
this.clearAndInit(); this.clearAndInit();
});
}
});
}).dimensions(rightX, startY + 24, columnWidth, 20).build()); }).dimensions(rightX, startY + 24, columnWidth, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.literal("Добавить новую"), button -> { this.addDrawableChild(ButtonWidget.builder(Text.literal("Добавить новую"), button -> {
@@ -12,7 +12,6 @@ import net.minecraft.text.Text;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -382,8 +381,7 @@ public class PaymentScreen extends Screen {
} }
lastRecipientLookup = username; lastRecipientLookup = username;
CompletableFuture bankUiService.loadRecipientCardsAsync(username)
.supplyAsync(() -> bankUiService.loadRecipientCards(username))
.thenAccept(cards -> { .thenAccept(cards -> {
if (this.client == null) { if (this.client == null) {
return; return;
@@ -84,12 +84,11 @@ public class BackendAuthenticator {
} }
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
if (json.has("sessionId")) { if (!json.has("sessionId")) {
return json.get("sessionId").getAsString();
} else {
System.err.println("[SPMEGA] Invalid response from start endpoint: " + response.body()); System.err.println("[SPMEGA] Invalid response from start endpoint: " + response.body());
throw new IOException("Invalid response from start endpoint: " + response.body()); throw new IOException("Invalid response from start endpoint: " + response.body());
} }
return json.get("sessionId").getAsString();
} }
private static boolean sendAuthRequestToBackend(UUID uuid, String serverId) throws IOException, InterruptedException { private static boolean sendAuthRequestToBackend(UUID uuid, String serverId) throws IOException, InterruptedException {
@@ -123,9 +122,16 @@ public class BackendAuthenticator {
} }
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
if (json.has("token")) { if (!json.has("token")) {
System.err.println("[SPMEGA] Invalid response from validate endpoint: " + response.body());
throw new IOException("Invalid response from validate endpoint: " + response.body());
}
if (config == null) {
System.err.println("[SPMEGA] Config is null, cannot save token.");
throw new IOException("Config is null, cannot save token.");
}
String token = json.get("token").getAsString(); String token = json.get("token").getAsString();
if (config != null) {
ModConfig updated = new ModConfig( ModConfig updated = new ModConfig(
config.apiDomain(), config.apiDomain(),
token, token,
@@ -137,14 +143,6 @@ public class BackendAuthenticator {
SPMega.setConfig(updated); SPMega.setConfig(updated);
System.out.println("[SPMEGA] Backend auth successful, saved token."); System.out.println("[SPMEGA] Backend auth successful, saved token.");
return true; return true;
} else {
System.err.println("[SPMEGA] Config is null, cannot save token.");
throw new IOException("Config is null, cannot save token.");
}
} else {
System.err.println("[SPMEGA] Invalid response from validate endpoint: " + response.body());
throw new IOException("Invalid response from validate endpoint: " + response.body());
}
} }
public static void sendCardToBackend(String cardId, String cardToken) { public static void sendCardToBackend(String cardId, String cardToken) {
@@ -223,19 +221,13 @@ public class BackendAuthenticator {
List<CardCredentials> cards = new ArrayList<>(); List<CardCredentials> cards = new ArrayList<>();
for (com.google.gson.JsonElement el : cardsArray) { for (com.google.gson.JsonElement el : cardsArray) {
JsonObject cardJson = el.getAsJsonObject(); JsonObject cardJson = el.getAsJsonObject();
String cardId = ""; String cardId = cardJson.has("cardId") && !cardJson.get("cardId").isJsonNull()
if (cardJson.has("cardId") && !cardJson.get("cardId").isJsonNull()) { ? cardJson.get("cardId").getAsString()
cardId = cardJson.get("cardId").getAsString(); : (cardJson.has("id") && !cardJson.get("id").isJsonNull() ? cardJson.get("id").getAsString() : "");
} else if (cardJson.has("id") && !cardJson.get("id").isJsonNull()) {
cardId = cardJson.get("id").getAsString();
}
String cardToken = ""; String cardToken = cardJson.has("cardToken") && !cardJson.get("cardToken").isJsonNull()
if (cardJson.has("cardToken") && !cardJson.get("cardToken").isJsonNull()) { ? cardJson.get("cardToken").getAsString()
cardToken = cardJson.get("cardToken").getAsString(); : (cardJson.has("token") && !cardJson.get("token").isJsonNull() ? cardJson.get("token").getAsString() : "");
} else if (cardJson.has("token") && !cardJson.get("token").isJsonNull()) {
cardToken = cardJson.get("token").getAsString();
}
if (!cardId.isEmpty() && !cardToken.isEmpty()) { if (!cardId.isEmpty() && !cardToken.isEmpty()) {
cards.add(new CardCredentials(cardId, cardToken)); cards.add(new CardCredentials(cardId, cardToken));
@@ -299,10 +291,10 @@ public class BackendAuthenticator {
String url = apiDomain + "/api/v1/transactions"; String url = apiDomain + "/api/v1/transactions";
JsonObject jsonPayload = new JsonObject(); JsonObject jsonPayload = new JsonObject();
jsonPayload.addProperty("senderCardId", senderCardId); jsonPayload.addProperty("cardToUse", senderCardId);
jsonPayload.addProperty("cardId", senderCardId); jsonPayload.addProperty("cardId", senderCardId);
jsonPayload.addProperty("receiver", receiver); jsonPayload.addProperty("receiverCard", receiver);
jsonPayload.addProperty("recipient", receiver); jsonPayload.addProperty("receiverName", receiver);
jsonPayload.addProperty("amount", amount); jsonPayload.addProperty("amount", amount);
jsonPayload.addProperty("comment", comment); jsonPayload.addProperty("comment", comment);
String requestBody = jsonPayload.toString(); String requestBody = jsonPayload.toString();
@@ -316,11 +308,10 @@ public class BackendAuthenticator {
.build(); .build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200 || response.statusCode() == 201 || response.statusCode() == 204) { if (response.statusCode() != 200 && response.statusCode() != 201 && response.statusCode() != 204) {
return true;
} else {
throw new IOException("Server returned status code " + response.statusCode() + ": " + response.body()); throw new IOException("Server returned status code " + response.statusCode() + ": " + response.body());
} }
return true;
} }
public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId) throws IOException, InterruptedException { public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId) throws IOException, InterruptedException {
@@ -5,6 +5,8 @@ import git.yawaflua.tech.spmega.SPMega;
import git.yawaflua.tech.spmega.api.SPWorldsApiClient; import git.yawaflua.tech.spmega.api.SPWorldsApiClient;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -17,7 +19,7 @@ public final class BankUiService {
private final String apiDomain = "https://spworlds.ru"; private final String apiDomain = "https://spworlds.ru";
private int selectedCardIndex; private int selectedCardIndex;
private String lastMessage = ""; private volatile String lastMessage = "";
private BankUiService() { private BankUiService() {
this.database = new BankDatabase(FabricLoader.getInstance().getConfigDir().resolve("spmega.db")); this.database = new BankDatabase(FabricLoader.getInstance().getConfigDir().resolve("spmega.db"));
@@ -54,11 +56,20 @@ public final class BankUiService {
return normalized.substring(normalized.length() - 5); return normalized.substring(normalized.length() - 5);
} }
public synchronized List<CardViewModel> getCards() { private void runOnMainThread(Runnable runnable) {
MinecraftClient client = MinecraftClient.getInstance();
if (client == null || client.isOnThread()) {
runnable.run();
} else {
client.execute(runnable);
}
}
public List<CardViewModel> getCards() {
return Collections.unmodifiableList(cards); return Collections.unmodifiableList(cards);
} }
public synchronized CardViewModel getSelectedCard() { public CardViewModel getSelectedCard() {
if (cards.isEmpty()) { if (cards.isEmpty()) {
return null; return null;
} }
@@ -66,7 +77,7 @@ public final class BankUiService {
return cards.get(selectedCardIndex); return cards.get(selectedCardIndex);
} }
public synchronized int getSelectedCardIndex() { public int getSelectedCardIndex() {
if (cards.isEmpty()) { if (cards.isEmpty()) {
return 0; return 0;
} }
@@ -74,7 +85,7 @@ public final class BankUiService {
return selectedCardIndex; return selectedCardIndex;
} }
public synchronized void setSelectedCardIndex(int index) { public void setSelectedCardIndex(int index) {
if (cards.isEmpty()) { if (cards.isEmpty()) {
selectedCardIndex = 0; selectedCardIndex = 0;
return; return;
@@ -82,7 +93,7 @@ public final class BankUiService {
selectedCardIndex = Math.floorMod(index, cards.size()); selectedCardIndex = Math.floorMod(index, cards.size());
} }
public synchronized CardViewModel cycleSelectedCard(int direction) { public CardViewModel cycleSelectedCard(int direction) {
if (cards.isEmpty()) { if (cards.isEmpty()) {
return null; return null;
} }
@@ -90,18 +101,19 @@ public final class BankUiService {
return cards.get(selectedCardIndex); return cards.get(selectedCardIndex);
} }
public synchronized String getLastMessage() { public String getLastMessage() {
return lastMessage; return lastMessage;
} }
public synchronized void syncCardsWithBackend(String playerUuid) { public CompletableFuture<Void> syncCardsWithBackendAsync(String playerUuid) {
return CompletableFuture.runAsync(() -> {
try { try {
List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend(); List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend();
boolean changed = false; boolean changed = false;
for (CardCredentials creds : backendCards) { for (CardCredentials creds : backendCards) {
if (database.getCredentials(creds.cardId()) == null) { if (database.getCredentials(creds.cardId()) == null) {
database.upsertCardCredentials(creds.cardId(), creds.cardToken()); database.upsertCardCredentials(creds.cardId(), creds.cardToken());
refreshCard(creds.cardId(), creds.cardToken(), playerUuid, false, false); refreshCardSync(creds.cardId(), creds.cardToken(), playerUuid, false, false);
changed = true; changed = true;
} }
} }
@@ -111,24 +123,38 @@ public final class BankUiService {
} catch (Exception e) { } catch (Exception e) {
System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage()); System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage());
} }
});
} }
public synchronized void refreshOnServerJoin(String playerUuid) { public CompletableFuture<Void> refreshOnServerJoinAsync(String playerUuid) {
syncCardsWithBackend(playerUuid); return CompletableFuture.runAsync(() -> {
try {
List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend();
for (CardCredentials creds : backendCards) {
if (database.getCredentials(creds.cardId()) == null) {
database.upsertCardCredentials(creds.cardId(), creds.cardToken());
refreshCardSync(creds.cardId(), creds.cardToken(), playerUuid, false, false);
}
}
} catch (Exception e) {
System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage());
}
List<StoredCard> storedCards = database.loadCards(); List<StoredCard> storedCards = database.loadCards();
for (StoredCard card : storedCards) { for (StoredCard card : storedCards) {
refreshCard(card.cardId(), card.cardToken(), playerUuid, false, false); refreshCardSync(card.cardId(), card.cardToken(), playerUuid, false, false);
} }
reloadCardsFromDb(); reloadCardsFromDb();
});
} }
public synchronized List<String> loadRecipientCards(String username) { public CompletableFuture<List<String>> loadRecipientCardsAsync(String username) {
CardCredentials credentials = getSelectedCredentials(); CardCredentials credentials = getSelectedCredentials();
if (credentials == null || username == null || username.isBlank()) { if (credentials == null || username == null || username.isBlank()) {
return List.of(); return CompletableFuture.completedFuture(List.of());
} }
return CompletableFuture.supplyAsync(() -> {
try { try {
List<SPWorldsApiClient.PlayerCard> apiCards = apiClient.getPlayerCards(username, toApiAuth(credentials)); List<SPWorldsApiClient.PlayerCard> apiCards = apiClient.getPlayerCards(username, toApiAuth(credentials));
List<String> numbers = new ArrayList<>(); List<String> numbers = new ArrayList<>();
@@ -143,75 +169,82 @@ public final class BankUiService {
lastMessage = "Не удалось получить карты игрока: " + exception.getMessage(); lastMessage = "Не удалось получить карты игрока: " + exception.getMessage();
return List.of(); return List.of();
} }
});
} }
public synchronized String addCard(String cardIdRaw, String cardTokenRaw, String playerUuid) { public CompletableFuture<String> addCardAsync(String cardIdRaw, String cardTokenRaw, String playerUuid) {
String cardId = cardIdRaw == null ? "" : cardIdRaw.trim(); String cardId = cardIdRaw == null ? "" : cardIdRaw.trim();
String cardToken = cardTokenRaw == null ? "" : cardTokenRaw.trim(); String cardToken = cardTokenRaw == null ? "" : cardTokenRaw.trim();
if (cardId.isEmpty() || cardToken.isEmpty()) { if (cardId.isEmpty() || cardToken.isEmpty()) {
lastMessage = "Укажи cardId и cardToken"; return CompletableFuture.completedFuture("Укажи cardId и cardToken");
return lastMessage;
} }
try { try {
UUID.fromString(cardId); UUID.fromString(cardId);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
lastMessage = "cardId должен быть UUID"; return CompletableFuture.completedFuture("cardId должен быть UUID");
return lastMessage;
} }
return CompletableFuture.supplyAsync(() -> {
database.upsertCardCredentials(cardId, cardToken); database.upsertCardCredentials(cardId, cardToken);
boolean refreshed = refreshCard(cardId, cardToken, playerUuid, true, true); boolean refreshed = refreshCardSync(cardId, cardToken, playerUuid, true, true);
if (!refreshed) { if (!refreshed) {
database.deleteCard(cardId); database.deleteCard(cardId);
} }
reloadCardsFromDb(); reloadCardsFromDb();
if (refreshed && lastMessage.isBlank()) { if (refreshed && lastMessage.isBlank()) {
lastMessage = "Карта добавлена"; return "Карта добавлена";
} }
return lastMessage; return lastMessage;
});
} }
public synchronized void removeSelectedCard() { public CompletableFuture<String> removeSelectedCardAsync() {
CardViewModel selected = getSelectedCard(); CardViewModel selected = getSelectedCard();
if (selected == null) { if (selected == null) {
lastMessage = "Нет карты для удаления"; return CompletableFuture.completedFuture("Нет карты для удаления");
return;
} }
String cardId = selected.id(); String cardId = selected.id();
return CompletableFuture.supplyAsync(() -> {
database.deleteCard(cardId); database.deleteCard(cardId);
ModConfig config = SPMega.getConfig(); ModConfig config = SPMega.getConfig();
if (config.allowBackend()) if (config != null && config.allowBackend()) {
BackendAuthenticator.deleteCardOnBackend(cardId); BackendAuthenticator.deleteCardOnBackend(cardId);
}
reloadCardsFromDb(); reloadCardsFromDb();
runOnMainThread(() -> {
if (selectedCardIndex >= cards.size()) { if (selectedCardIndex >= cards.size()) {
selectedCardIndex = Math.max(0, cards.size() - 1); selectedCardIndex = Math.max(0, cards.size() - 1);
} }
lastMessage = "Карта удалена"; });
return "Карта удалена";
});
} }
public synchronized void refreshSelectedCard(String playerUuid) { public CompletableFuture<String> refreshSelectedCardAsync(String playerUuid) {
CardViewModel selected = getSelectedCard(); CardViewModel selected = getSelectedCard();
if (selected == null) { if (selected == null) {
lastMessage = "Нет карты для обновления"; return CompletableFuture.completedFuture("Нет карты для обновления");
return;
} }
return CompletableFuture.supplyAsync(() -> {
CardCredentials credentials = database.getCredentials(selected.id()); CardCredentials credentials = database.getCredentials(selected.id());
if (credentials == null) { if (credentials == null) {
lastMessage = "Не найдены креды карты"; return "Не найдены креды карты";
return;
} }
refreshCard(credentials.cardId(), credentials.cardToken(), playerUuid, false, false); refreshCardSync(credentials.cardId(), credentials.cardToken(), playerUuid, false, false);
reloadCardsFromDb(); reloadCardsFromDb();
return lastMessage.isBlank() ? "Карта обновлена" : lastMessage;
});
} }
public synchronized boolean submitPayment(PaymentDraft draft) { public CompletableFuture<Boolean> submitPaymentAsync(PaymentDraft draft) {
return CompletableFuture.supplyAsync(() -> {
CardCredentials credentials = database.getCredentials(draft.senderCardId()); CardCredentials credentials = database.getCredentials(draft.senderCardId());
if (credentials == null) { if (credentials == null) {
lastMessage = "Не найдены креды карты отправителя"; lastMessage = "Не найдены креды карты отправителя";
@@ -231,7 +264,7 @@ public final class BankUiService {
draft.comment() draft.comment()
); );
try { try {
refreshCard(credentials.cardId(), credentials.cardToken(), "", false, false); refreshCardSync(credentials.cardId(), credentials.cardToken(), "", false, false);
StoredCard updatedCard = database.loadCards().stream() StoredCard updatedCard = database.loadCards().stream()
.filter(c -> c.cardId().equals(credentials.cardId())) .filter(c -> c.cardId().equals(credentials.cardId()))
.findFirst() .findFirst()
@@ -277,13 +310,10 @@ public final class BankUiService {
lastMessage = "Ошибка перевода: " + exception.getMessage(); lastMessage = "Ошибка перевода: " + exception.getMessage();
return false; return false;
} }
});
} }
public CompletableFuture<Boolean> submitPaymentAsync(PaymentDraft draft) { private boolean refreshCardSync(
return CompletableFuture.supplyAsync(() -> submitPayment(draft));
}
private boolean refreshCard(
String cardId, String cardId,
String cardToken, String cardToken,
String playerUuid, String playerUuid,
@@ -319,12 +349,9 @@ public final class BankUiService {
} }
} }
git.yawaflua.tech.spmega.ModConfig config = git.yawaflua.tech.spmega.SPMega.getConfig(); git.yawaflua.tech.spmega.ModConfig config = git.yawaflua.tech.spmega.SPMega.getConfig();
if (config.allowBackend()) if (config != null && config.allowBackend()) {
CompletableFuture.supplyAsync(() -> {
BackendAuthenticator.sendCardToBackend(cardId, cardToken); BackendAuthenticator.sendCardToBackend(cardId, cardToken);
return true;
} }
);
lastMessage = ""; lastMessage = "";
return true; return true;
@@ -336,8 +363,8 @@ public final class BankUiService {
private void reloadCardsFromDb() { private void reloadCardsFromDb() {
List<StoredCard> storedCards = database.loadCards(); List<StoredCard> storedCards = database.loadCards();
runOnMainThread(() -> {
cards.clear(); cards.clear();
for (StoredCard stored : storedCards) { for (StoredCard stored : storedCards) {
String cardNumber = stored.cardNumber() == null || stored.cardNumber().isBlank() String cardNumber = stored.cardNumber() == null || stored.cardNumber().isBlank()
? extractLastDigits(stored.cardId()) ? extractLastDigits(stored.cardId())
@@ -352,6 +379,7 @@ public final class BankUiService {
} else { } else {
selectedCardIndex = Math.floorMod(selectedCardIndex, cards.size()); selectedCardIndex = Math.floorMod(selectedCardIndex, cards.size());
} }
});
} }
private CardCredentials getSelectedCredentials() { private CardCredentials getSelectedCredentials() {
+11 -3
View File
@@ -3,12 +3,20 @@
"id": "spmega", "id": "spmega",
"version": "${version}", "version": "${version}",
"name": "SPMega", "name": "SPMega",
"description": "yawaflua`s SPRadar+SPMHelper mod, thats make a lot!", "description": "yawaflua's SPRadar+SPMHelper mod, that's make a lot!",
"authors": [ "authors": [
"Dmitri 'yawaflua' Shimanski aka DOLBAYEB" {
"name": "Dmitri 'yawaflua' Shimanski aka DOLBAYEB",
"contact": {
"homepage": "https://yawaflua.tech",
"email": "spmega@yawaflua.tech"
}
}
], ],
"contact": { "contact": {
"homepage": "https://yawaflua.tech",
"issues": "https://git.yawaflua.tech/spmega",
"sources": "https://git.yawaflua.tech/spmega"
}, },
"license": "CC BY-NC-ND 4.0", "license": "CC BY-NC-ND 4.0",
"icon": "icon.png", "icon": "icon.png",