From 3cba6de9f11031a30538d0234ada898bc047dd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Z=C3=B6ller?= Date: Mon, 6 Apr 2026 21:31:53 +0200 Subject: [PATCH] Better Post Formatting --- .../controller/ActivityPubController.java | 44 +++++++++++------- .../ActivityPostProcessingService.java | 45 ++++++++----------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/main/java/net/javahippie/fitpub/controller/ActivityPubController.java b/src/main/java/net/javahippie/fitpub/controller/ActivityPubController.java index 8369cce..861b3c8 100644 --- a/src/main/java/net/javahippie/fitpub/controller/ActivityPubController.java +++ b/src/main/java/net/javahippie/fitpub/controller/ActivityPubController.java @@ -291,52 +291,64 @@ public class ActivityPubController { } /** - * Format activity content for ActivityPub. - * Uses plain text with Unicode symbols for maximum compatibility. + * Format activity content as HTML for ActivityPub. + * Mastodon and most Fediverse software expect HTML in the content field. */ private String formatActivityContent(Activity activity) { StringBuilder content = new StringBuilder(); - // Title (if present) + // Title if (activity.getTitle() != null && !activity.getTitle().isEmpty()) { - content.append(activity.getTitle()).append("\n\n"); + content.append("

").append(escapeHtml(activity.getTitle())).append("

"); } - // Description (if present) + // Description if (activity.getDescription() != null && !activity.getDescription().isEmpty()) { - content.append(activity.getDescription()).append("\n\n"); + content.append("

").append(escapeHtml(activity.getDescription())).append("

"); } // Activity type with emoji String activityEmoji = getActivityEmoji(activity.getActivityType()); String formattedType = ActivityFormatter.formatActivityType(activity.getActivityType()); - content.append(activityEmoji).append(" ").append(formattedType); + content.append("

").append(activityEmoji).append(" ").append(escapeHtml(formattedType)).append("

"); // Metrics + StringBuilder metrics = new StringBuilder(); if (activity.getTotalDistance() != null) { - content.append("\n📏 ") - .append(String.format("%.2f km", activity.getTotalDistance().doubleValue() / 1000.0)); + metrics.append("📏 ") + .append(String.format("%.2f km", activity.getTotalDistance().doubleValue() / 1000.0)) + .append("
"); } - if (activity.getTotalDurationSeconds() != null) { long hours = activity.getTotalDurationSeconds() / 3600; long minutes = (activity.getTotalDurationSeconds() % 3600) / 60; long seconds = activity.getTotalDurationSeconds() % 60; - content.append("\n⏱️ "); + metrics.append("⏱️ "); if (hours > 0) { - content.append(hours).append("h "); + metrics.append(hours).append("h "); } - content.append(minutes).append("m ").append(seconds).append("s"); + metrics.append(minutes).append("m ").append(seconds).append("s").append("
"); } - if (activity.getElevationGain() != null) { - content.append("\n⛰️ ") - .append(String.format("%.0f m", activity.getElevationGain().doubleValue())); + metrics.append("⛰️ ") + .append(String.format("%.0f m", activity.getElevationGain().doubleValue())) + .append("
"); + } + if (metrics.length() > 0) { + content.append("

").append(metrics).append("

"); } return content.toString(); } + private static String escapeHtml(String text) { + if (text == null) return ""; + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + /** * Get emoji for activity type. */ diff --git a/src/main/java/net/javahippie/fitpub/service/ActivityPostProcessingService.java b/src/main/java/net/javahippie/fitpub/service/ActivityPostProcessingService.java index 4f89ce9..8960b5b 100644 --- a/src/main/java/net/javahippie/fitpub/service/ActivityPostProcessingService.java +++ b/src/main/java/net/javahippie/fitpub/service/ActivityPostProcessingService.java @@ -216,47 +216,33 @@ public class ActivityPostProcessingService { } /** - * Format activity content for ActivityPub Note. - * Uses plain text with Unicode symbols for maximum compatibility across Fediverse platforms. - * - * Format: - * - Title (if present) - * - Description (if present) - * - Activity type with emoji - * - Distance (if present) - * - Duration (if present) - * - Elevation gain (if present) + * Format activity content as HTML for ActivityPub Note. + * Mastodon and most Fediverse software expect HTML in the content field. * * @param activity the activity to format - * @return formatted content string + * @return formatted HTML content string */ private String formatActivityContent(Activity activity) { StringBuilder content = new StringBuilder(); - // Title (if present) if (activity.getTitle() != null && !activity.getTitle().isEmpty()) { - content.append(activity.getTitle()).append("\n\n"); + content.append("

").append(escapeHtml(activity.getTitle())).append("

"); } - // Description (if present) if (activity.getDescription() != null && !activity.getDescription().isEmpty()) { - content.append(activity.getDescription()).append("\n\n"); + content.append("

").append(escapeHtml(activity.getDescription())).append("

"); } - // Activity type with emoji String activityEmoji = getActivityEmoji(activity.getActivityType()); String formattedType = ActivityFormatter.formatActivityType(activity.getActivityType()); - content.append(activityEmoji).append(" ").append(formattedType); + content.append("

").append(activityEmoji).append(" ").append(escapeHtml(formattedType)).append("

"); - // Metrics, each on its own line with a blank line separating from above StringBuilder metrics = new StringBuilder(); - if (activity.getTotalDistance() != null) { metrics.append("📏 ") .append(String.format("%.2f km", activity.getTotalDistance().doubleValue() / 1000.0)) - .append("\n"); + .append("
"); } - if (activity.getTotalDurationSeconds() != null) { long hours = activity.getTotalDurationSeconds() / 3600; long minutes = (activity.getTotalDurationSeconds() % 3600) / 60; @@ -265,23 +251,28 @@ public class ActivityPostProcessingService { if (hours > 0) { metrics.append(hours).append("h "); } - metrics.append(minutes).append("m ").append(seconds).append("s") - .append("\n"); + metrics.append(minutes).append("m ").append(seconds).append("s").append("
"); } - if (activity.getElevationGain() != null) { metrics.append("⛰️ ") .append(String.format("%.0f m", activity.getElevationGain())) - .append("\n"); + .append("
"); } - if (metrics.length() > 0) { - content.append("\n\n").append(metrics.toString().stripTrailing()); + content.append("

").append(metrics).append("

"); } return content.toString(); } + private static String escapeHtml(String text) { + if (text == null) return ""; + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + /** * Get an emoji for the activity type. *