diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorView.java index b4c70b1fcd..cd943fa3ae 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorView.java @@ -459,7 +459,7 @@ static class Trader { Optional optionalProfileAge = reputationService.getProfileAgeService().getProfileAge(userProfile); profileAge = optionalProfileAge.orElse(0L); profileAgeString = optionalProfileAge - .map(TimeFormatter::formatAgeInDays) + .map(TimeFormatter::formatAgeInDaysAndYears) .orElse(Res.get("data.na")); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/offerbook_list/OfferbookListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/offerbook_list/OfferbookListItem.java index 4afb85018d..156915e0af 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/offerbook_list/OfferbookListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/offerbook_list/OfferbookListItem.java @@ -35,7 +35,9 @@ import bisq.offer.price.PriceUtil; import bisq.offer.price.spec.FixPriceSpec; import bisq.offer.price.spec.PriceSpecFormatter; +import bisq.presentation.formatters.DateFormatter; import bisq.presentation.formatters.PercentageFormatter; +import bisq.presentation.formatters.TimeFormatter; import bisq.user.profile.UserProfile; import bisq.user.reputation.ReputationScore; import bisq.user.reputation.ReputationService; @@ -60,15 +62,17 @@ public class OfferbookListItem { private final ReputationService reputationService; private final UserProfile senderUserProfile; private final String userNickname, formattedRangeQuoteAmount, bitcoinPaymentMethodsAsString, - fiatPaymentMethodsAsString, authorUserProfileId, marketCurrencyCode, offerType; + fiatPaymentMethodsAsString, authorUserProfileId, marketCurrencyCode, offerType, + formattedOfferAge, offerAgeTooltipText; private final ReputationScore reputationScore; private final List fiatPaymentMethods; private final List bitcoinPaymentMethods; private final boolean isFixPrice; private final Monetary quoteSideMinAmount; private final long totalScore; - private double priceSpecAsPercent; private final Pin marketPriceByCurrencyMapPin; + private final long offerAgeInDays; + private double priceSpecAsPercent; private String formattedPercentagePrice, priceTooltipText; public OfferbookListItem(BisqEasyOfferbookMessage bisqEasyOfferbookMessage, @@ -101,6 +105,10 @@ public OfferbookListItem(BisqEasyOfferbookMessage bisqEasyOfferbookMessage, reputationScore = reputationService.getReputationScore(senderUserProfile); totalScore = reputationScore.getTotalScore(); + offerAgeInDays = TimeFormatter.getAgeInDays(bisqEasyOffer.getDate()); + formattedOfferAge = TimeFormatter.formatAgeInDays(bisqEasyOffer.getDate()); + offerAgeTooltipText = Res.get("user.profileCard.offers.table.columns.offerAge.tooltip", + DateFormatter.formatDateTime(bisqEasyOffer.getDate())); marketPriceByCurrencyMapPin = marketPriceService.getMarketPriceByCurrencyMap().addObserver(() -> UIThread.run(this::updatePriceSpecAsPercent)); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java index e5494ff60c..a65c30cf8e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java @@ -323,7 +323,7 @@ public ListItem(TwoPartyPrivateChatChannel channel, Optional optionalProfileAge = reputationService.getProfileAgeService().getProfileAge(peersUserProfile); profileAge = optionalProfileAge.orElse(0L); profileAgeString = optionalProfileAge - .map(TimeFormatter::formatAgeInDays) + .map(TimeFormatter::formatAgeInDaysAndYears) .orElse(Res.get("data.na")); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationDetailsPopup.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationDetailsPopup.java index fb88604992..cab71733fc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationDetailsPopup.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationDetailsPopup.java @@ -200,7 +200,7 @@ public ListItem(ReputationSource reputationSource, dateString = DateFormatter.formatDate(blockTime); timeString = DateFormatter.formatTime(blockTime); age = TimeFormatter.getAgeInDays(blockTime); - ageString = TimeFormatter.formatAgeInDays(blockTime); + ageString = TimeFormatter.formatAgeInDaysAndYears(blockTime); sourceString = reputationSource.getDisplayString(); amountString = optionalAmount.map(amount -> AmountFormatter.formatAmountWithCode(Coin.fromValue(amount, "BSQ"))).orElse("-"); scoreString = String.valueOf(score); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationRankingView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationRankingView.java index eb2f030e39..81d1486b16 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationRankingView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/reputation/ranking/ReputationRankingView.java @@ -335,7 +335,7 @@ public static class ListItem { Optional optionalProfileAge = reputationService.getProfileAgeService().getProfileAge(userProfile); profileAge = optionalProfileAge.orElse(0L); profileAgeString = optionalProfileAge - .map(TimeFormatter::formatAgeInDays) + .map(TimeFormatter::formatAgeInDaysAndYears) .orElse(Res.get("data.na")); // applyReputationScore gets called from selectedToggleChanged @@ -402,7 +402,7 @@ private String formatReputationSourceValue(ReputationSource reputationSource, lo return switch (reputationSource) { case BURNED_BSQ, BSQ_BOND -> AmountFormatter.formatAmount(Coin.asBsqFromValue(value)); case PROFILE_AGE, BISQ1_ACCOUNT_AGE, BISQ1_SIGNED_ACCOUNT_AGE_WITNESS -> - value > 0 ? TimeFormatter.formatAgeInDays(value) : ""; + value > 0 ? TimeFormatter.formatAgeInDaysAndYears(value) : ""; }; } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/details/ProfileCardDetailsController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/details/ProfileCardDetailsController.java index 48f686161e..0dd235d37c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/details/ProfileCardDetailsController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/details/ProfileCardDetailsController.java @@ -67,7 +67,7 @@ public void updateUserProfileData(UserProfile userProfile) { model.getTotalReputationScore().set(String.valueOf(reputationScore.getTotalScore())); })); model.getProfileAge().set(reputationService.getProfileAgeService().getProfileAge(userProfile) - .map(TimeFormatter::formatAgeInDays) + .map(TimeFormatter::formatAgeInDaysAndYears) .orElse(Res.get("data.na"))); if (livenessUpdateScheduler != null) { livenessUpdateScheduler.stop(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/offers/ProfileCardOffersView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/offers/ProfileCardOffersView.java index 427aa1f972..2e7f5831ca 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/offers/ProfileCardOffersView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/offers/ProfileCardOffersView.java @@ -70,15 +70,25 @@ protected void onViewDetached() { } private void configTableView() { - tableView.getColumns().add(new BisqTableColumn.Builder() + BisqTableColumn marketColumn = new BisqTableColumn.Builder() .title(Res.get("user.profileCard.offers.table.columns.market")) .left() - .comparator(Comparator.comparing(OfferbookListItem::getMarketCurrencyCode)) + .comparator(Comparator.comparing(OfferbookListItem::getMarketCurrencyCode) + .thenComparing(OfferbookListItem::getOfferAgeInDays)) .setCellFactory(getMarketCellFactory()) + .build(); + tableView.getColumns().add(marketColumn); + tableView.getSortOrder().add(marketColumn); + + tableView.getColumns().add(new BisqTableColumn.Builder() + .title(Res.get("user.profileCard.offers.table.columns.offerAge")) + .left() + .comparator(Comparator.comparing(OfferbookListItem::getOfferAgeInDays)) + .setCellFactory(getOfferAgeCellFactory()) .build()); tableView.getColumns().add(new BisqTableColumn.Builder() - .title(Res.get("user.profileCard.offers.table.columns.offerType")) + .title(Res.get("user.profileCard.offers.table.columns.offer")) .left() .comparator(Comparator.comparing(OfferbookListItem::getOfferType)) .valueSupplier(OfferbookListItem::getOfferType) @@ -136,6 +146,30 @@ protected void updateItem(OfferbookListItem item, boolean empty) { }; } + private Callback, + TableCell> getOfferAgeCellFactory() { + return column -> new TableCell<>() { + private final Label offerAgeLabel = new Label(); + private final BisqTooltip tooltip = new BisqTooltip(); + + @Override + protected void updateItem(OfferbookListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + tooltip.setText(item.getOfferAgeTooltipText()); + offerAgeLabel.setText(item.getFormattedOfferAge()); + offerAgeLabel.setTooltip(tooltip); + setGraphic(offerAgeLabel); + } else { + offerAgeLabel.setText(""); + offerAgeLabel.setTooltip(null); + setGraphic(null); + } + } + }; + } + private Callback, TableCell> getPriceCellFactory() { return column -> new TableCell<>() { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/reputation/ProfileCardReputationView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/reputation/ProfileCardReputationView.java index 0e04cf0b70..afa1a7cbc3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/reputation/ProfileCardReputationView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/profile_card/reputation/ProfileCardReputationView.java @@ -133,7 +133,7 @@ public ListItem(ReputationSource reputationSource, dateString = DateFormatter.formatDate(blockTime); timeString = DateFormatter.formatTime(blockTime); age = TimeFormatter.getAgeInDays(blockTime); - ageString = TimeFormatter.formatAgeInDays(blockTime); + ageString = TimeFormatter.formatAgeInDaysAndYears(blockTime); sourceString = reputationSource.getDisplayString(); amountString = optionalAmount.map(amount -> AmountFormatter.formatAmountWithCode(Coin.fromValue(amount, "BSQ"))).orElse("-"); scoreString = String.valueOf(score); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/user_profile/UserProfileController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/user_profile/UserProfileController.java index 644fe216ee..6015f648d4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/user_profile/UserProfileController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/user_profile/UserProfileController.java @@ -91,7 +91,7 @@ public void onActivate() { model.getTerms().set(userProfile.getTerms()); model.getProfileAge().set(profileAgeService.getProfileAge(userIdentity.getUserProfile()) - .map(TimeFormatter::formatAgeInDays) + .map(TimeFormatter::formatAgeInDaysAndYears) .orElse(Res.get("data.na"))); if (livenessUpateScheduler != null) { diff --git a/i18n/src/main/resources/default.properties b/i18n/src/main/resources/default.properties index 4af9f477e5..91c57458b8 100644 --- a/i18n/src/main/resources/default.properties +++ b/i18n/src/main/resources/default.properties @@ -89,6 +89,7 @@ temporal.year.1={0} year # suppress inspection "UnusedProperty" temporal.year.*={0} years temporal.at=at +temporal.today=Today diff --git a/i18n/src/main/resources/user.properties b/i18n/src/main/resources/user.properties index 86aa949900..884f73abb7 100644 --- a/i18n/src/main/resources/user.properties +++ b/i18n/src/main/resources/user.properties @@ -154,7 +154,9 @@ user.profileCard.details.profileAge=Profile age user.profileCard.details.lastUserActivity=Last user activity user.profileCard.details.version=Software version user.profileCard.offers.table.columns.market=Market -user.profileCard.offers.table.columns.offerType=Offer +user.profileCard.offers.table.columns.offer=Offer user.profileCard.offers.table.columns.amount=Amount user.profileCard.offers.table.columns.price=Price user.profileCard.offers.table.columns.paymentMethods=Payment methods +user.profileCard.offers.table.columns.offerAge=Offer age +user.profileCard.offers.table.columns.offerAge.tooltip=Creation date:\n{0} diff --git a/presentation/src/main/java/bisq/presentation/formatters/TimeFormatter.java b/presentation/src/main/java/bisq/presentation/formatters/TimeFormatter.java index 746a643c3d..30f3dfed58 100644 --- a/presentation/src/main/java/bisq/presentation/formatters/TimeFormatter.java +++ b/presentation/src/main/java/bisq/presentation/formatters/TimeFormatter.java @@ -78,7 +78,7 @@ public static String formatAge(long duration) { } } - public static String formatAgeInDays(long date) { + public static String formatAgeInDaysAndYears(long date) { long totalDays = getAgeInDays(date); long years = totalDays / 365; long days = totalDays - years * 365; @@ -90,4 +90,11 @@ public static String formatAgeInDays(long date) { return dayString; } } + + public static String formatAgeInDays(long date) { + long days = getAgeInDays(date); + return days == 0 + ? Res.get("temporal.today") + : Res.getPluralization("temporal.day", days); + } }