Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions wallet-ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,53 @@ test {
useJUnitPlatform()
}

// The version shown in Settings > About comes from gradle.properties at BUILD
// time. A hand-edited constant is wrong the moment someone forgets to bump it,
// and the version is the first thing a bug report quotes.
//
// Generated as Java SOURCE, not a properties resource: native-image includes
// only resources that are explicitly registered, so a resource would work in the
// jar build and silently come back null in the native one — the version would be
// missing exactly where it is hardest to notice.
def buildInfoDir = layout.buildDirectory.dir('generated/sources/buildinfo')

def generateBuildInfo = tasks.register('generateBuildInfo') {
description = 'Generate BuildInfo.java from gradle.properties'
def walletVersion = project.version.toString()
def nodeVersion = (rootProject.findProperty('yanoVersion') ?: 'unknown').toString()
def outDir = buildInfoDir
inputs.property('walletVersion', walletVersion)
inputs.property('nodeVersion', nodeVersion)
outputs.dir(outDir)
doLast {
def pkg = new File(outDir.get().asFile, 'com/bloxbean/cardano/yano/wallet/ui')
pkg.mkdirs()
new File(pkg, 'BuildInfo.java').text = """package com.bloxbean.cardano.yano.wallet.ui;

/**
* Build-time facts, generated by :wallet-ui:generateBuildInfo from
* gradle.properties. Do not edit — your changes are overwritten on every build.
*/
public final class BuildInfo {

/** The wallet's version, e.g. "0.1.0-pre4". */
public static final String WALLET_VERSION = "${walletVersion}";

/** The managed Yano node this build is pinned to. */
public static final String NODE_VERSION = "${nodeVersion}";

private BuildInfo() {
}
}
"""
}
}

// Registering the provider (not the directory) makes compileJava depend on the
// generating task, so a plain `./gradlew build` cannot compile a stale copy.
sourceSets.main.java.srcDir generateBuildInfo


def detectJavafxPlatform() {
def osName = System.getProperty('os.name').toLowerCase()
def osArch = System.getProperty('os.arch').toLowerCase()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import com.bloxbean.cardano.yano.wallet.ui.screens.HistoryScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.ReceiveScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.SendScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.AboutScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.SettingsScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.GovernanceScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.ProposalsScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.LiveScreen;
import com.bloxbean.cardano.yano.wallet.ui.screens.StakingScreen;
import com.bloxbean.cardano.yano.wallet.ui.live.LiveBlocksView;
import com.bloxbean.cardano.yano.wallet.ui.live.LiveChainModel;
import com.bloxbean.cardano.yano.wallet.ui.live.LivePrefs;
import com.bloxbean.cardano.yano.wallet.ui.util.Icons;
import com.bloxbean.cardano.yano.wallet.ui.util.Ui;
import javafx.animation.KeyFrame;
Expand Down Expand Up @@ -64,7 +63,6 @@ public class Shell {
private final Tooltip syncTooltip = new Tooltip();
private final Timeline statusPoller;
private final LiveChainModel liveModel = new LiveChainModel();
private LiveBlocksView ambient;
private String active;
// Delta tracking to tell "advancing" from "stalled" during catch-up without
// any network-tip math: remember the highest block seen and when it last rose.
Expand Down Expand Up @@ -110,6 +108,9 @@ public Shell(WalletUiController controller, Runnable onLock,
registry.put("Proposals", () -> new ProposalsScreen(controller, overlay));
registry.put("Live", () -> new LiveScreen(controller, overlay, liveModel));
registry.put("Settings", () -> new SettingsScreen(controller, overlay, this::changeNetwork));
// Takes no controller: build facts come from BuildInfo, generated at build
// time, so this screen works even when nothing else does.
registry.put("About", () -> new AboutScreen(overlay));

statusPoller = new Timeline(new KeyFrame(Duration.seconds(5), e -> tick()));
statusPoller.setCycleCount(Timeline.INDEFINITE);
Expand All @@ -119,14 +120,8 @@ public StackPane root() {
BorderPane frame = new BorderPane();
frame.getStyleClass().add("shell");
frame.setLeft(buildSidebar());
// Ambient live-chain background sits behind every screen; screens have
// transparent gaps, so it shows through without touching their styling.
ambient = new LiveBlocksView(liveModel, true);
StackPane center = new StackPane(ambient, content);
frame.setCenter(center);
frame.setCenter(content);
overlay.getChildren().add(frame);
syncAmbient();
LivePrefs.setOnChange(() -> javafx.application.Platform.runLater(this::syncAmbient));
navigate("Dashboard");
statusPoller.play();
pollStatus();
Expand All @@ -135,27 +130,11 @@ public StackPane root() {

public void dispose() {
statusPoller.stop();
LivePrefs.setOnChange(null);
if (ambient != null) {
ambient.dispose();
}
}

/** Reflect the ambient-background toggle: show/hide + start/stop the animation. */
private void syncAmbient() {
boolean on = LivePrefs.ambientEnabled();
if (ambient != null) {
ambient.setVisible(on);
ambient.setManaged(on);
ambient.setActive(on);
}
if (on) {
pollLiveChain();
}
}

/** The Live page is the only thing that consumes chain-tip data now. */
private boolean liveNeeded() {
return LivePrefs.ambientEnabled() || "Live".equals(active);
return "Live".equals(active);
}

private void pollLiveChain() {
Expand Down Expand Up @@ -216,6 +195,7 @@ private Node buildSidebar() {
addNav(side, "Proposals", Icons.PROPOSALS);
addNav(side, "Live", Icons.LIVE);
addNav(side, "Settings", Icons.SETTINGS);
addNav(side, "About", Icons.ABOUT);

Region grow = new Region();
VBox.setVgrow(grow, Priority.ALWAYS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public interface WalletUiController {
String dataDir();

/**
* Live chain-tip snapshot for the animated "Live" view / ambient background.
* Live chain-tip snapshot for the animated "Live" view.
* Best-effort: returns {@link LiveChainView#unreachable()} rather than throwing
* when the node is unavailable, so the visualization degrades quietly.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
/**
* A Canvas that animates the chain tip as a train of block tiles sliding in from
* the right — newest at the leading edge, older easing left, each tile brightening
* with its transaction count. Reused in two modes: {@code ambient} (dim, behind
* the shell content) and full (the dedicated Live page).
* with its transaction count.
*
* <p>Runs an {@link AnimationTimer} only while it is {@link #setActive active},
* attached to a scene, AND its window is focused — so it costs nothing in the
* background or when the feature is off. All listeners are removed on detach /
* {@link #dispose}, so it does not leak across shell rebuilds.
* <p>The {@code ambient} mode (dim, mouse-transparent, drawn behind every
* screen) is no longer reachable: the wallet dropped the animated background
* setting it existed for. The flag is kept because unpicking it means editing
* the drawing code the Live page depends on, for no user-visible gain.
*/
public final class LiveBlocksView extends Region {
private static final double TILE_W = 104; // full-mode block card (px)
Expand Down Expand Up @@ -76,7 +75,7 @@ public void handle(long now) {
sceneProperty().addListener((o, oldScene, newScene) -> onSceneChanged(newScene));
}

/** External gate: the ambient toggle, or the Live page being on screen. */
/** External gate: whether the Live page is on screen. */
public void setActive(boolean on) {
if (on == active) {
return;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.bloxbean.cardano.yano.wallet.ui.screens;

import com.bloxbean.cardano.yano.wallet.ui.BuildInfo;
import com.bloxbean.cardano.yano.wallet.ui.Shell;
import com.bloxbean.cardano.yano.wallet.ui.util.Icons;
import com.bloxbean.cardano.yano.wallet.ui.util.Ui;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

/**
* Which build this is.
*
* <p>A top-level screen rather than a card inside Settings: the first question
* on any bug report is "which version?", and someone answering it should not
* have to know that versions live under Settings.
*
* <p>Every value comes from {@link BuildInfo}, generated from gradle.properties
* at build time, so this cannot drift from the release it shipped in.
*/
public class AboutScreen implements Shell.Screen {
private static final String REPO = "https://github.com/bloxbean/yano-wallet";

private final StackPane overlay;
private final ScrollPane root;

public AboutScreen(StackPane overlay) {
this.overlay = overlay;
this.root = build();
}

private ScrollPane build() {
Label title = new Label("About");
title.getStyleClass().add("screen-title");

VBox versionCard = Ui.card("Version",
mono("Yano Wallet " + BuildInfo.WALLET_VERSION),
mono("Managed node: Yano " + BuildInfo.NODE_VERSION),
mono("Runtime: " + runtimeDescription()),
mono("Platform: " + platformDescription()),
Ui.muted("Include these details in a bug report — they identify the exact "
+ "build, which nothing else in the app can tell you."),
copyButton());

Hyperlink repo = new Hyperlink(REPO);
repo.setOnAction(e -> {
Ui.copyToClipboard(REPO);
Ui.toast(overlay, "Link copied", false);
});

VBox projectCard = Ui.card("Project",
Ui.muted("A full-node Cardano wallet: it runs its own Yano node, so balances "
+ "and history come from a chain you validated yourself."),
repo,
Ui.muted("MIT licensed. Under active development and not audited — use test "
+ "networks, and never a recovery phrase holding real funds."));

VBox column = new VBox(16, title, versionCard, projectCard);
column.setPadding(new Insets(24));
column.setMaxWidth(860);

ScrollPane scroll = new ScrollPane(column);
scroll.setFitToWidth(true);
scroll.getStyleClass().add("screen-scroll");
return scroll;
}

private Button copyButton() {
Button copy = new Button("Copy version details", Icons.icon(Icons.COPY, 14, "nav-icon"));
copy.getStyleClass().add("primary-button");
copy.setOnAction(e -> {
Ui.copyToClipboard(report());
Ui.toast(overlay, "Version details copied", false);
});
return copy;
}

private static Label mono(String text) {
Label label = new Label(text);
label.getStyleClass().add("mono");
label.setWrapText(true);
return label;
}

/**
* A native image has no JVM to report, and printing a Java version there
* would send a bug report down the wrong path — several native-image
* failures reproduce only in that build.
*/
private static String runtimeDescription() {
if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
return "native image (no JVM)";
}
return "Java " + Runtime.version().feature() + " (" + System.getProperty("java.vendor") + ")";
}

private static String platformDescription() {
return System.getProperty("os.name") + " " + System.getProperty("os.version")
+ " (" + System.getProperty("os.arch") + ")";
}

private static String report() {
return "Yano Wallet " + BuildInfo.WALLET_VERSION
+ "\nManaged node: Yano " + BuildInfo.NODE_VERSION
+ "\nRuntime: " + runtimeDescription()
+ "\nPlatform: " + platformDescription();
}

@Override
public Node root() {
return root;
}

@Override
public void refresh() {
// Build facts are fixed for the life of the process.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.bloxbean.cardano.yano.wallet.ui.Shell;
import com.bloxbean.cardano.yano.wallet.ui.contract.WalletUiController;
import com.bloxbean.cardano.yano.wallet.ui.live.LivePrefs;
import com.bloxbean.cardano.yano.wallet.ui.util.ThemeManager;
import com.bloxbean.cardano.yano.wallet.ui.util.Ui;
import javafx.application.Platform;
Expand Down Expand Up @@ -294,15 +293,9 @@ private VBox appearanceCard() {
}
});
});
CheckBox liveBackground = new CheckBox("Animated live-chain background");
liveBackground.setSelected(LivePrefs.ambientEnabled());
liveBackground.setOnAction(e -> LivePrefs.setAmbientEnabled(liveBackground.isSelected()));
return Ui.card("Appearance",
Ui.muted("Color theme — applies instantly and is remembered."),
themePicker,
liveBackground,
Ui.muted("A subtle animation of live blocks behind the app. Opt-in; it pauses when the "
+ "window isn't focused. Open the Live page for the full view."));
themePicker);
}

private void loadNodeLog() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ private Icons() {
+ " 2-2h12M16 13h3";
public static final String LOCK = "M7 11V7a5 5 0 0 1 10 0v4M5 11h14v10H5V11z";
// Filled equalizer bars — reads as live activity/stats (must be solid, not stroked).
/** "i" in a circle. */
public static final String ABOUT = "M12 3a9 9 0 1 0 9 9 9 9 0 0 0-9-9zm0 4h.01M11 11h1v6h1";

public static final String LIVE = "M4 13h3v7H4v-7z M10 8h3v12h-3V8z M16 3h3v17h-3V3z";

public static Region icon(String svgPath, double size, String styleClass) {
Expand Down
Loading