Show profile on UI
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
package org.to.telegramfinalproject.Client;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import java.nio.file.*;
|
||||||
|
|
||||||
|
public final class AvatarLocalResolver {
|
||||||
|
private static final Path UPLOADS_ROOT = Paths.get(
|
||||||
|
System.getProperty("app.uploads.root", "uploads")
|
||||||
|
).toAbsolutePath().normalize();
|
||||||
|
|
||||||
|
private static boolean isHttp(String s) {
|
||||||
|
return s.startsWith("http://") || s.startsWith("https://");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String resolve(String serverValue) {
|
||||||
|
if (serverValue == null || serverValue.isBlank()) return null;
|
||||||
|
if (isHttp(serverValue) || serverValue.startsWith("file:")) return serverValue;
|
||||||
|
|
||||||
|
String rel = serverValue.startsWith("/") ? serverValue.substring(1) : serverValue; // "avatars/..."
|
||||||
|
Path p = UPLOADS_ROOT.resolve(rel).normalize();
|
||||||
|
if (!p.startsWith(UPLOADS_ROOT)) return null; // امنیت مسیر
|
||||||
|
if (!Files.exists(p)) { // مهم: واقعاً وجود دارد؟
|
||||||
|
System.err.println("Avatar resolve: NOT FOUND -> " + p);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String url = p.toUri().toString(); // file:///.../uploads/avatars/....
|
||||||
|
System.out.println("Avatar resolve: " + serverValue + " -> " + url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image load(String serverValue) {
|
||||||
|
String url = resolve(serverValue);
|
||||||
|
if (url == null) return null;
|
||||||
|
|
||||||
|
// فقط برای http/https کشبریکر
|
||||||
|
if (isHttp(url)) {
|
||||||
|
url += (url.contains("?") ? "&" : "?") + "v=" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
Image img = new Image(url, false); // sync load تا خطا را همانجا بفهمیم
|
||||||
|
if (img.isError()) {
|
||||||
|
System.err.println("Avatar load failed: " + url + " -> " + img.getException());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
|
||||||
|
public final class AvatarFX {
|
||||||
|
|
||||||
|
public static void circleClip(ImageView iv, double sizePx) {
|
||||||
|
iv.setFitWidth(sizePx);
|
||||||
|
iv.setFitHeight(sizePx);
|
||||||
|
iv.setPreserveRatio(true);
|
||||||
|
iv.setSmooth(true);
|
||||||
|
|
||||||
|
Circle c = new Circle();
|
||||||
|
c.radiusProperty().bind(Bindings.min(iv.fitWidthProperty(), iv.fitHeightProperty()).divide(2));
|
||||||
|
c.centerXProperty().bind(iv.fitWidthProperty().divide(2));
|
||||||
|
c.centerYProperty().bind(iv.fitHeightProperty().divide(2));
|
||||||
|
iv.setClip(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import javafx.scene.image.Image;
|
|||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -68,7 +69,27 @@ public class ChatItemController {
|
|||||||
systemAvatar.setManaged(true);
|
systemAvatar.setManaged(true);
|
||||||
|
|
||||||
} else if (imageUrl != null && !imageUrl.isEmpty()) {
|
} else if (imageUrl != null && !imageUrl.isEmpty()) {
|
||||||
profileImageUser.setImage(new Image(imageUrl, true));
|
// profileImageUser.setImage(new Image(imageUrl, true));
|
||||||
|
// profileImageUser.setVisible(true);
|
||||||
|
// profileImageUser.setManaged(true);
|
||||||
|
|
||||||
|
Image img = AvatarLocalResolver.load(imageUrl);
|
||||||
|
if (img != null) {
|
||||||
|
profileImageUser.setImage(img);
|
||||||
|
AvatarFX.circleClip(profileImageUser, 40);
|
||||||
|
} else {
|
||||||
|
String path;
|
||||||
|
if ("group".equalsIgnoreCase(chatType)) {
|
||||||
|
path = "/org/to/telegramfinalproject/Avatars/default_group_profile.png";
|
||||||
|
} else if ("channel".equalsIgnoreCase(chatType)) {
|
||||||
|
path = "/org/to/telegramfinalproject/Avatars/default_channel_profile.png";
|
||||||
|
} else {
|
||||||
|
path = "/org/to/telegramfinalproject/Avatars/default_user_profile.png";
|
||||||
|
}
|
||||||
|
profileImageUser.setImage(new Image(
|
||||||
|
java.util.Objects.requireNonNull(getClass().getResourceAsStream(path))
|
||||||
|
));
|
||||||
|
}
|
||||||
profileImageUser.setVisible(true);
|
profileImageUser.setVisible(true);
|
||||||
profileImageUser.setManaged(true);
|
profileImageUser.setManaged(true);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import javafx.stage.FileChooser;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.to.telegramfinalproject.Client.ActionHandler;
|
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||||
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
import org.to.telegramfinalproject.Client.Session;
|
import org.to.telegramfinalproject.Client.Session;
|
||||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
@@ -538,25 +539,22 @@ public class ChatPageController {
|
|||||||
chatTitle.setText(entry.getName());
|
chatTitle.setText(entry.getName());
|
||||||
|
|
||||||
// آواتار پیشفرض بر اساس نوع
|
// آواتار پیشفرض بر اساس نوع
|
||||||
|
// ChatPageController.showChat(...)
|
||||||
if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
||||||
userAvatar.setImage(new Image(entry.getImageUrl()));
|
Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
||||||
} else if ("channel".equals(entry.getType())) {
|
if (img != null) {
|
||||||
userAvatar.setImage(new Image(
|
userAvatar.setImage(img);
|
||||||
Objects.requireNonNull(getClass().getResourceAsStream(
|
} else {
|
||||||
"/org/to/telegramfinalproject/Avatars/default_channel_profile.png"))
|
// ⬇️ فالبک بر اساس نوع
|
||||||
));
|
setDefaultHeaderAvatarByType(entry.getType());
|
||||||
} else if ("group".equals(entry.getType())) {
|
}
|
||||||
userAvatar.setImage(new Image(
|
|
||||||
Objects.requireNonNull(getClass().getResourceAsStream(
|
|
||||||
"/org/to/telegramfinalproject/Avatars/default_group_profile.png"))
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
userAvatar.setImage(new Image(
|
setDefaultHeaderAvatarByType(entry.getType());
|
||||||
Objects.requireNonNull(getClass().getResourceAsStream(
|
|
||||||
"/org/to/telegramfinalproject/Avatars/default_user_profile.png"))
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
// userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
|
AvatarFX.circleClip(userAvatar, 36);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fetchAndRenderHeader(entry);
|
fetchAndRenderHeader(entry);
|
||||||
|
|
||||||
@@ -1016,10 +1014,12 @@ public class ChatPageController {
|
|||||||
String img = data.optString("image_url", "");
|
String img = data.optString("image_url", "");
|
||||||
if (hasVal(img)) {
|
if (hasVal(img)) {
|
||||||
try {
|
try {
|
||||||
userAvatar.setImage(new Image(img, true));
|
Image im = AvatarLocalResolver.load(img); // ⬅️
|
||||||
|
if (im != null) userAvatar.setImage(im);
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
chatStatus.setText(userStatusText(
|
chatStatus.setText(userStatusText(
|
||||||
data.optBoolean("online", false),
|
data.optBoolean("online", false),
|
||||||
data.optString("last_seen", null)
|
data.optString("last_seen", null)
|
||||||
@@ -1033,11 +1033,13 @@ public class ChatPageController {
|
|||||||
String img = data.optString("image_url", "");
|
String img = data.optString("image_url", "");
|
||||||
if (hasVal(img)) {
|
if (hasVal(img)) {
|
||||||
try {
|
try {
|
||||||
userAvatar.setImage(new Image(img, true));
|
Image im = AvatarLocalResolver.load(img); // ⬅️
|
||||||
|
if (im != null) userAvatar.setImage(im);
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int members = data.optInt("member_count", 0);
|
int members = data.optInt("member_count", 0);
|
||||||
int online = data.optInt("online_count", -1);
|
int online = data.optInt("online_count", -1);
|
||||||
chatStatus.setText(online >= 0 ? (members + " members, " + online + " online")
|
chatStatus.setText(online >= 0 ? (members + " members, " + online + " online")
|
||||||
@@ -1050,11 +1052,13 @@ public class ChatPageController {
|
|||||||
String img = data.optString("image_url", "");
|
String img = data.optString("image_url", "");
|
||||||
if (hasVal(img)) {
|
if (hasVal(img)) {
|
||||||
try {
|
try {
|
||||||
userAvatar.setImage(new Image(img, true));
|
Image im = AvatarLocalResolver.load(img); // ⬅️
|
||||||
|
if (im != null) userAvatar.setImage(im);
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int subs = data.optInt("member_count", 0);
|
int subs = data.optInt("member_count", 0);
|
||||||
chatStatus.setText(subs + " subscribers");
|
chatStatus.setText(subs + " subscribers");
|
||||||
}
|
}
|
||||||
@@ -1097,4 +1101,15 @@ public class ChatPageController {
|
|||||||
}
|
}
|
||||||
return "recently";
|
return "recently";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setDefaultHeaderAvatarByType(String type){
|
||||||
|
String path = switch (type == null ? "" : type.toLowerCase()) {
|
||||||
|
case "channel" -> "/org/to/telegramfinalproject/Avatars/default_channel_profile.png";
|
||||||
|
case "group" -> "/org/to/telegramfinalproject/Avatars/default_group_profile.png";
|
||||||
|
default -> "/org/to/telegramfinalproject/Avatars/default_user_profile.png";
|
||||||
|
};
|
||||||
|
userAvatar.setImage(new Image(
|
||||||
|
java.util.Objects.requireNonNull(getClass().getResourceAsStream(path))
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import javafx.scene.layout.Region;
|
|||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -49,6 +50,8 @@ public class SidebarMenuController {
|
|||||||
// Load default profile image
|
// Load default profile image
|
||||||
Image profile = loadImage("/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
Image profile = loadImage("/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
if (profile != null) profileImage.setImage(profile);
|
if (profile != null) profileImage.setImage(profile);
|
||||||
|
AvatarFX.circleClip(profileImage, 56);
|
||||||
|
|
||||||
|
|
||||||
setupButtonActions();
|
setupButtonActions();
|
||||||
setupToggleAction();
|
setupToggleAction();
|
||||||
@@ -202,17 +205,37 @@ public class SidebarMenuController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// کمککننده: هم URL و هم ریسورس کلاسپث را امتحان میکند
|
// کمککننده: هم URL و هم ریسورس کلاسپث را امتحان میکند
|
||||||
|
// private Image tryLoadImage(String src) {
|
||||||
|
// if (src == null || src.isBlank()) return null;
|
||||||
|
// try {
|
||||||
|
// // اگر ریسورس داخل پروژه است (با / شروع شود یا در resources موجود باشد)
|
||||||
|
// var res = getClass().getResource(src);
|
||||||
|
// if (res != null) return new Image(res.toExternalForm(), true);
|
||||||
|
// // در غیر این صورت فرض کن URL است (http/https/file)
|
||||||
|
// return new Image(src, true);
|
||||||
|
// } catch (Exception ignored) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
private Image tryLoadImage(String src) {
|
private Image tryLoadImage(String src) {
|
||||||
if (src == null || src.isBlank()) return null;
|
if (src == null || src.isBlank()) return null;
|
||||||
try {
|
try {
|
||||||
// اگر ریسورس داخل پروژه است (با / شروع شود یا در resources موجود باشد)
|
// اگر مسیر داخل resources است
|
||||||
var res = getClass().getResource(src);
|
var res = getClass().getResource(src);
|
||||||
if (res != null) return new Image(res.toExternalForm(), true);
|
if (res != null) return new Image(res.toExternalForm(), true);
|
||||||
// در غیر این صورت فرض کن URL است (http/https/file)
|
|
||||||
|
// اگر مسیر نسبی سرور (مثل /avatars/...) است
|
||||||
|
String fileUri = AvatarLocalResolver.resolve(src);
|
||||||
|
if (fileUri != null) return new Image(fileUri, true);
|
||||||
|
|
||||||
|
// در غیر این صورت، فرض URL کامل
|
||||||
return new Image(src, true);
|
return new Image(src, true);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user