fix send message v0
This commit is contained in:
parent
fe8a02b742
commit
03f27d7a45
18
a.ps1
18
a.ps1
|
|
@ -3,7 +3,7 @@
|
|||
# -----------------------------
|
||||
$LocalPath = "C:\Users\Pars\Desktop\saba-python" # مسیر پروژه روی ویندوز
|
||||
$PiUser = "pars" # کاربر روی Raspberry Pi
|
||||
$PiHost = "10.65.40.150" # آیپی Raspberry Pi
|
||||
$PiHost = "192.168.1.25" # آیپی Raspberry Pi
|
||||
$RemotePath = "/home/pars/Desktop/" # مسیر پروژه روی Pi
|
||||
$MainPy = "saba-python/main.py" # فایل اصلی پایتون
|
||||
|
||||
|
|
@ -29,11 +29,19 @@ $command = "mkdir -p ~/.ssh; chmod 700 ~/.ssh; echo '$pubKey' >> ~/.ssh/authoriz
|
|||
ssh "$PiUser@$PiHost" $command
|
||||
|
||||
# -----------------------------
|
||||
# انتقال پروژه با scp
|
||||
# انتقال پروژه (بدون فایلهای مخفی)
|
||||
# -----------------------------
|
||||
Write-Host "Transferring project to Raspberry Pi..."
|
||||
$scpTarget = "$PiUser@$PiHost`:$RemotePath"
|
||||
scp -r $LocalPath $scpTarget
|
||||
Write-Host "Transferring project to Raspberry Pi (excluding .git)..."
|
||||
$ParentDir = Split-Path $LocalPath -Parent
|
||||
$FolderName = Split-Path $LocalPath -Leaf
|
||||
$DeployTar = "$ParentDir\deploy.tar"
|
||||
|
||||
Set-Location $ParentDir
|
||||
tar.exe -cf deploy.tar --exclude=".git" --exclude="__pycache__" $FolderName
|
||||
scp deploy.tar "$PiUser@$PiHost`:$RemotePath/deploy.tar"
|
||||
ssh "$PiUser@$PiHost" "cd $RemotePath && tar -xf deploy.tar && rm deploy.tar"
|
||||
Remove-Item $DeployTar
|
||||
Set-Location $LocalPath
|
||||
|
||||
# -----------------------------
|
||||
# اجرای برنامه روی Raspberry Pi
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -46,7 +46,8 @@ class SecureMessagingService:
|
|||
public_key_enc=self.cipher.encrypt_text(public_key),
|
||||
fingerprint=fingerprint,
|
||||
)
|
||||
self.db.set_connection_settings("COM1", 115200)
|
||||
import os
|
||||
self.db.set_connection_settings("/dev/ttyS0" if os.name != "nt" else "COM1", 115200)
|
||||
self.identity = {
|
||||
"private_key": private_key,
|
||||
"public_key": public_key,
|
||||
|
|
|
|||
BIN
secure_sms/infrastructure/__pycache__/database.cpython-313.pyc
Normal file
BIN
secure_sms/infrastructure/__pycache__/database.cpython-313.pyc
Normal file
Binary file not shown.
|
|
@ -137,7 +137,11 @@ class Database:
|
|||
return row["value"] if row else default
|
||||
|
||||
def get_connection_settings(self) -> tuple[str, int]:
|
||||
port = self.get_config("gsm_port", "COM1") or "COM1"
|
||||
import os
|
||||
default_port = "/dev/ttyS0" if os.name != "nt" else "COM1"
|
||||
port = self.get_config("gsm_port")
|
||||
if not port or port == "COM1":
|
||||
port = default_port
|
||||
baudrate = int(self.get_config("gsm_baudrate", "115200") or "115200")
|
||||
return port, baudrate
|
||||
|
||||
|
|
|
|||
|
|
@ -98,34 +98,44 @@ class GSMGateway(IMessageGateway):
|
|||
def _send_single_sms(self, phone: str, body: str) -> bool:
|
||||
with self.lock:
|
||||
try:
|
||||
print(f"[GSM] Sending SMS to {phone}, payload_len={len(body)}")
|
||||
self.serial_conn.reset_input_buffer()
|
||||
self.serial_conn.write(f'AT+CMGS="{phone}"\r\n'.encode("ascii"))
|
||||
start = time.time()
|
||||
prompt_ready = False
|
||||
while time.time() - start < 2:
|
||||
while time.time() - start < 5.0:
|
||||
if self.serial_conn.in_waiting:
|
||||
char = self.serial_conn.read().decode("ascii", errors="ignore")
|
||||
if char == ">":
|
||||
prompt_ready = True
|
||||
print("[GSM] Received '>' prompt.")
|
||||
break
|
||||
time.sleep(0.05)
|
||||
if not prompt_ready:
|
||||
print("[GSM] Error: Did not receive '>' prompt in time. Canceling.")
|
||||
self.serial_conn.write(chr(27).encode("ascii"))
|
||||
return False
|
||||
self.serial_conn.write(body.encode("ascii") + chr(26).encode("ascii"))
|
||||
start = time.time()
|
||||
while time.time() - start < 10:
|
||||
while time.time() - start < 45.0:
|
||||
if self.serial_conn.in_waiting:
|
||||
line = self.serial_conn.readline().decode("ascii", errors="ignore").strip()
|
||||
if line:
|
||||
print(f"[GSM] Modem response: {line}")
|
||||
if "OK" in line:
|
||||
print("[GSM] SMS sent successfully.")
|
||||
return True
|
||||
if "ERROR" in line:
|
||||
print("[GSM] SMS encountered an ERROR.")
|
||||
return False
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
except Exception:
|
||||
print("[GSM] SMS Send Timed Out waiting for OK/ERROR (45s).")
|
||||
return False
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"[GSM] Exception during SMS transmission: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _read_loop(self):
|
||||
while self.is_running:
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -16,11 +16,11 @@ ctk.set_appearance_mode("light")
|
|||
ctk.set_default_color_theme("blue")
|
||||
|
||||
|
||||
PRIMARY = "#175B4B"
|
||||
PRIMARY_DARK = "#0E4236"
|
||||
PRIMARY_SOFT = "#DFF1E8"
|
||||
ACCENT = "#E8A04D"
|
||||
ACCENT_DARK = "#C97E2D"
|
||||
PRIMARY = "#00B4D8"
|
||||
PRIMARY_DARK = "#0096C7"
|
||||
PRIMARY_SOFT = "#E6F7FA"
|
||||
ACCENT = "#48CAE4"
|
||||
ACCENT_DARK = "#0077B6"
|
||||
BACKGROUND = "#F5EFE7"
|
||||
CARD = "#FFFDFC"
|
||||
SURFACE = "#FBF7F2"
|
||||
|
|
@ -46,21 +46,21 @@ KEYBOARD_LAYOUTS = {
|
|||
['۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹', '۰'],
|
||||
['ض', 'ص', 'ث', 'ق', 'ف', 'غ', 'ع', 'ه', 'خ', 'ح', 'ج', 'چ'],
|
||||
['ش', 'س', 'ی', 'ب', 'ل', 'ا', 'ت', 'ن', 'م', 'ک', 'گ'],
|
||||
['ظ', 'ط', 'ز', 'ر', 'ذ', 'د', 'پ', 'و', '⌫'],
|
||||
['123', 'انگلیسی', '،', 'فاصله', '.', 'تایید'],
|
||||
['ظ', 'ط', 'ز', 'ر', 'ذ', 'د', 'پ', 'و', '⌫ پاک'],
|
||||
['123', 'انگلیسی', '،', 'فاصله (Space)', '.', 'تایید ⏎'],
|
||||
],
|
||||
"en": [
|
||||
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
|
||||
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
|
||||
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
|
||||
['z', 'x', 'c', 'v', 'b', 'n', 'm', '⌫'],
|
||||
['123', 'فارسی', ',', 'فاصله', '.', 'تایید'],
|
||||
['z', 'x', 'c', 'v', 'b', 'n', 'm', '⌫ Delete'],
|
||||
['123', 'فارسی', ',', ' Space ', '.', 'Enter ⏎'],
|
||||
],
|
||||
"numeric": [
|
||||
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
|
||||
['+', '-', '/', '@', '_', '.', ':', '(', ')', '?'],
|
||||
['فارسی', 'انگلیسی', '⌫'],
|
||||
['بستن', 'فاصله', 'تایید'],
|
||||
['فارسی', 'انگلیسی', '⌫ Delete'],
|
||||
['بستن', 'فاصله (Space)', 'تایید ⏎'],
|
||||
],
|
||||
}
|
||||
KEYBOARD_ACTIONS = [
|
||||
|
|
@ -118,13 +118,73 @@ class RTLEntry(_RTLTextMixin, ctk.CTkEntry):
|
|||
kwargs.setdefault("corner_radius", 16)
|
||||
super().__init__(*args, **self._normalize_kwargs(kwargs))
|
||||
|
||||
bg_color = kwargs.get("fg_color", INPUT_BG)
|
||||
try:
|
||||
self._entry.configure(fg=bg_color, insertbackground=bg_color, selectbackground=bg_color)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._display_label = ctk.CTkLabel(
|
||||
self, text="", text_color=TEXT, justify="right",
|
||||
font=kwargs.get("font", ctk.CTkFont(family=FONT_BODY, size=15))
|
||||
)
|
||||
self._display_label.place(relwidth=0.9, relheight=0.9, relx=0.95, rely=0.5, anchor="e")
|
||||
self.bind("<KeyRelease>", lambda e: self._sync_display(), add="+")
|
||||
self._sync_display()
|
||||
|
||||
def _sync_display(self, text=None):
|
||||
raw = text if text is not None else self.get()
|
||||
show_char = self.cget("show")
|
||||
if raw:
|
||||
display_text = show_char * len(raw) if show_char else ui_text(raw)
|
||||
self._display_label.configure(text=display_text + " |")
|
||||
else:
|
||||
placeholder = self.cget("placeholder_text") or ""
|
||||
self._display_label.configure(text=ui_text(placeholder))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
return super().configure(require_redraw=require_redraw, **self._normalize_kwargs(kwargs))
|
||||
res = super().configure(require_redraw=require_redraw, **self._normalize_kwargs(kwargs))
|
||||
if "placeholder_text" in kwargs:
|
||||
self._sync_display()
|
||||
return res
|
||||
|
||||
|
||||
class RTLTextbox(ctk.CTkTextbox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
bg_color = kwargs.get("fg_color", INPUT_BG)
|
||||
try:
|
||||
self._textbox.configure(fg=bg_color, insertbackground=bg_color, selectbackground=bg_color)
|
||||
self._textbox.tag_configure("right_align", justify="right")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._display_label = ctk.CTkLabel(
|
||||
self, text="", text_color=TEXT, justify="right",
|
||||
font=kwargs.get("font", ctk.CTkFont(family=FONT_BODY, size=15)),
|
||||
anchor="ne"
|
||||
)
|
||||
self._display_label.place(relwidth=0.9, relheight=0.9, relx=0.95, rely=0.08, anchor="ne")
|
||||
self.bind("<KeyRelease>", lambda e: self._sync_display(), add="+")
|
||||
self._sync_display()
|
||||
|
||||
def _sync_display(self, text=None):
|
||||
raw = text if text is not None else self.get("1.0", "end-1c")
|
||||
if raw:
|
||||
self._display_label.configure(text=ui_text(raw) + " |")
|
||||
else:
|
||||
self._display_label.configure(text=" |")
|
||||
|
||||
def insert(self, index, text, *tags):
|
||||
return super().insert(index, ui_text(text), *tags)
|
||||
res = super().insert(index, text, *tags)
|
||||
self._textbox.tag_add("right_align", "1.0", "end")
|
||||
self._sync_display()
|
||||
return res
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
res = super().delete(index1, index2)
|
||||
self._sync_display()
|
||||
return res
|
||||
|
||||
|
||||
class RTLScrollableFrame(_RTLTextMixin, ctk.CTkScrollableFrame):
|
||||
|
|
@ -240,6 +300,11 @@ class SecureSmsApp(ctk.CTk):
|
|||
bind_target.bind("<FocusIn>", lambda _event, target=widget: self._activate_text_input(target), add="+")
|
||||
bind_target.bind("<Button-1>", lambda _event, target=widget: self.after(10, lambda: self._activate_text_input(target)), add="+")
|
||||
|
||||
if hasattr(widget, "_display_label"):
|
||||
self._input_aliases[str(widget._display_label)] = widget
|
||||
widget._display_label.bind("<Button-1>", lambda _event, target=widget: self.after(10, lambda: self._activate_text_input(target)), add="+")
|
||||
widget.bind("<Button-1>", lambda _event, target=widget: self.after(10, lambda: self._activate_text_input(target)), add="+")
|
||||
|
||||
def _focus_registered_input(self, widget):
|
||||
if widget not in self._text_inputs or not widget.winfo_exists():
|
||||
return
|
||||
|
|
@ -328,6 +393,10 @@ class SecureSmsApp(ctk.CTk):
|
|||
text = target.get("1.0", "end-1c")
|
||||
else:
|
||||
text = target.get()
|
||||
|
||||
if hasattr(widget, "_sync_display"):
|
||||
widget._sync_display(text)
|
||||
|
||||
text = text.replace('\n', ' ')
|
||||
if len(text) > 40:
|
||||
text = "..." + text[-37:]
|
||||
|
|
@ -342,7 +411,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
self._refresh_keyboard_action_bar()
|
||||
self._update_keyboard_preview()
|
||||
self._shift_layout_for_keyboard(True)
|
||||
if not self.keyboard_host.winfo_ismapped():
|
||||
if hasattr(self, 'keyboard_host') and not self.keyboard_host.winfo_ismapped():
|
||||
self.keyboard_host.grid()
|
||||
self._render_keyboard(self.current_keyboard_layout)
|
||||
self.after(0, self._hide_mouse_cursor)
|
||||
|
|
@ -350,7 +419,8 @@ class SecureSmsApp(ctk.CTk):
|
|||
def _hide_virtual_keyboard(self):
|
||||
self.active_input = None
|
||||
self._shift_layout_for_keyboard(False)
|
||||
self.keyboard_host.grid_remove()
|
||||
if hasattr(self, 'keyboard_host'):
|
||||
self.keyboard_host.grid_remove()
|
||||
|
||||
def _keyboard_title(self):
|
||||
if self.active_input in self._text_inputs:
|
||||
|
|
@ -359,14 +429,13 @@ class SecureSmsApp(ctk.CTk):
|
|||
|
||||
def _keyboard_weight(self, key):
|
||||
return {
|
||||
'فاصله': 50,
|
||||
"Space": 50,
|
||||
'تایید': 20,
|
||||
"Enter": 20,
|
||||
'فاصله (Space)': 60,
|
||||
" Space ": 60,
|
||||
'تایید ⏎': 20,
|
||||
"Enter ⏎": 20,
|
||||
'بستن': 15,
|
||||
'حذف': 15,
|
||||
"Back": 15,
|
||||
'⌫': 15,
|
||||
'⌫ پاک': 25,
|
||||
"⌫ Delete": 25,
|
||||
'فارسی': 15,
|
||||
"English": 15,
|
||||
'انگلیسی': 15,
|
||||
|
|
@ -374,9 +443,9 @@ class SecureSmsApp(ctk.CTk):
|
|||
}.get(key, 10)
|
||||
|
||||
def _keyboard_style(self, key):
|
||||
if key in {'تایید', "Enter"}:
|
||||
if key in {'تایید ⏎', "Enter ⏎"}:
|
||||
return PRIMARY, PRIMARY_DARK, "white"
|
||||
if key in {"ABC", 'فا', '۱۲۳', "123", "English", 'انگلیسی', 'فارسی', 'حذف', 'بستن', "Back", '⌫'}:
|
||||
if key in {"ABC", 'فا', '۱۲۳', "123", "English", 'انگلیسی', 'فارسی', 'بستن', "⌫ Delete", '⌫ پاک'}:
|
||||
return KEY_MUTED, "#A8AFB9", KEY_TEXT
|
||||
return KEY_FACE, "#F0F0F0", KEY_TEXT
|
||||
|
||||
|
|
@ -407,7 +476,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
key_height = 44 if self.compact_mode else 40
|
||||
key_font_size = 17 if self.compact_mode else 15
|
||||
for row_index, row in enumerate(KEYBOARD_LAYOUTS.get(layout_name, KEYBOARD_LAYOUTS["fa"])):
|
||||
row_frame = ctk.CTkFrame(self.keyboard_keys, fg_color="transparent")
|
||||
row_frame = ctk.CTkFrame(self.keyboard_keys, fg_color=KEYBOARD_BG, corner_radius=0)
|
||||
row_frame.grid(row=row_index, column=0, sticky="ew", pady=2 if self.compact_mode else 3)
|
||||
for column_index, key in enumerate(row):
|
||||
row_frame.grid_columnconfigure(column_index, weight=self._keyboard_weight(key))
|
||||
|
|
@ -415,6 +484,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
RTLButton(
|
||||
row_frame,
|
||||
text=key,
|
||||
width=20,
|
||||
height=key_height,
|
||||
corner_radius=6,
|
||||
fg_color=fg_color,
|
||||
|
|
@ -434,16 +504,16 @@ class SecureSmsApp(ctk.CTk):
|
|||
if key in {'۱۲۳', "123"}:
|
||||
self._show_virtual_keyboard("numeric")
|
||||
return
|
||||
if key in {'فاصله', "Space"}:
|
||||
if key in {'فاصله', 'فاصله (Space)', " Space ", "Space"}:
|
||||
self._insert_into_active_input(" ")
|
||||
return
|
||||
if key in {'حذف', "Back", '⌫'}:
|
||||
if key in {'⌫ پاک', "⌫ Delete"}:
|
||||
self._backspace_active_input()
|
||||
return
|
||||
if key == 'بستن':
|
||||
self._hide_virtual_keyboard()
|
||||
return
|
||||
if key in {'تایید', "Enter"}:
|
||||
if key in {'تایید ⏎', "Enter ⏎"}:
|
||||
self._submit_active_input()
|
||||
return
|
||||
self._insert_into_active_input(key)
|
||||
|
|
@ -459,6 +529,8 @@ class SecureSmsApp(ctk.CTk):
|
|||
if len(selection) == 2:
|
||||
target.delete(selection[0], selection[1])
|
||||
target.insert("insert", text)
|
||||
target.tag_add("right_align", "1.0", "end")
|
||||
target.tag_configure("right_align", justify="right")
|
||||
target.see("insert")
|
||||
else:
|
||||
try:
|
||||
|
|
@ -467,6 +539,10 @@ class SecureSmsApp(ctk.CTk):
|
|||
except Exception:
|
||||
pass
|
||||
target.insert("insert", text)
|
||||
try:
|
||||
target.configure(justify="right")
|
||||
except Exception:
|
||||
pass
|
||||
target.focus_set()
|
||||
self._update_keyboard_preview()
|
||||
except TclError:
|
||||
|
|
@ -523,7 +599,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
|
||||
self.root_frame = ctk.CTkFrame(self, fg_color=BACKGROUND, corner_radius=0)
|
||||
self.root_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.root_frame.grid_columnconfigure(1, weight=1)
|
||||
self.root_frame.grid_columnconfigure(0, weight=1)
|
||||
self.root_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.keyboard_host = ctk.CTkFrame(self, fg_color=BACKGROUND, corner_radius=0)
|
||||
|
|
@ -540,7 +616,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.keyboard_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0)
|
||||
self.keyboard_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
keyboard_header = ctk.CTkFrame(self.keyboard_frame, fg_color="transparent")
|
||||
keyboard_header = ctk.CTkFrame(self.keyboard_frame, fg_color=KEYBOARD_BG, corner_radius=0)
|
||||
keyboard_header.grid(row=0, column=0, sticky="ew", padx=10, pady=(8, 4))
|
||||
keyboard_header.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
|
@ -577,15 +653,16 @@ class SecureSmsApp(ctk.CTk):
|
|||
)
|
||||
self.keyboard_hint.grid(row=1, column=0, padx=14, pady=(0, 2), sticky="e")
|
||||
|
||||
self.keyboard_action_bar = ctk.CTkFrame(self.keyboard_frame, fg_color="transparent", height=0)
|
||||
self.keyboard_action_bar = ctk.CTkFrame(self.keyboard_frame, fg_color=KEYBOARD_BG, height=0, corner_radius=0)
|
||||
|
||||
self.keyboard_keys = ctk.CTkFrame(self.keyboard_frame, fg_color="transparent")
|
||||
self.keyboard_keys = ctk.CTkFrame(self.keyboard_frame, fg_color=KEYBOARD_BG, corner_radius=0)
|
||||
self.keyboard_keys.grid(row=2, column=0, sticky="ew", padx=6, pady=(2, 12))
|
||||
self.keyboard_keys.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self._refresh_keyboard_action_bar()
|
||||
self._render_keyboard(self.current_keyboard_layout)
|
||||
self.keyboard_host.grid_remove()
|
||||
if hasattr(self, 'keyboard_host'):
|
||||
self.keyboard_host.grid_remove()
|
||||
|
||||
def _reset_text_input_registry(self):
|
||||
self.active_input = None
|
||||
|
|
@ -670,10 +747,12 @@ class SecureSmsApp(ctk.CTk):
|
|||
frame,
|
||||
text=action_text,
|
||||
height=44,
|
||||
corner_radius=8,
|
||||
fg_color=PRIMARY,
|
||||
hover_color=PRIMARY_DARK,
|
||||
text_color="#FFFFFF",
|
||||
corner_radius=22,
|
||||
fg_color=BACKGROUND,
|
||||
hover_color=PRIMARY_SOFT,
|
||||
text_color=PRIMARY_DARK,
|
||||
border_color=PRIMARY,
|
||||
border_width=2,
|
||||
command=self._submit_lock_screen,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
).pack(fill="x", padx=36, pady=(4, 16))
|
||||
|
|
@ -686,6 +765,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
|
||||
def _configure_root_layout(self):
|
||||
self.root_frame.grid_columnconfigure(0, weight=1)
|
||||
self.root_frame.grid_columnconfigure(1, weight=0)
|
||||
self.root_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
def _configure_profile_card_layout(self):
|
||||
|
|
@ -759,21 +839,21 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.sidebar.grid_columnconfigure(0, weight=1)
|
||||
self.sidebar.grid_rowconfigure(5, weight=1)
|
||||
|
||||
sidebar_header = ctk.CTkFrame(self.sidebar, fg_color="transparent")
|
||||
sidebar_header = ctk.CTkFrame(self.sidebar, fg_color=SIDEBAR, corner_radius=0)
|
||||
sidebar_header.grid(row=0, column=0, padx=14, pady=(12, 10), sticky="ew")
|
||||
sidebar_header.grid_columnconfigure(0, weight=1)
|
||||
sidebar_header.grid_columnconfigure(1, weight=0)
|
||||
|
||||
RTLButton(
|
||||
sidebar_header,
|
||||
text='☰',
|
||||
width=40,
|
||||
text='منو',
|
||||
width=50,
|
||||
height=40,
|
||||
corner_radius=20,
|
||||
fg_color="transparent",
|
||||
fg_color=SIDEBAR,
|
||||
hover_color=BORDER,
|
||||
text_color="white",
|
||||
font=ctk.CTkFont(family=FONT_TITLE, size=24, weight="bold"),
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
command=self._show_drawer_menu
|
||||
).grid(row=0, column=1, padx=(6, 0), sticky="e")
|
||||
|
||||
|
|
@ -832,7 +912,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
|
||||
self.contact_name_entry = RTLEntry(self.contact_form_card, placeholder_text='نام مخاطب', height=48, font=ctk.CTkFont(family=FONT_BODY, size=16))
|
||||
self.contact_name_entry.grid(row=0, column=0, pady=8, sticky="ew")
|
||||
self.contact_phone_entry = RTLEntry(self.contact_form_card, placeholder_text='شماره موبایل', height=48, font=ctk.CTkFont(family=FONT_BODY, size=16))
|
||||
self.contact_phone_entry = RTLEntry(self.contact_form_card, placeholder_text='شماره موبایل (مثبت 09)', height=48, font=ctk.CTkFont(family=FONT_BODY, size=16))
|
||||
self.contact_phone_entry.grid(row=1, column=0, pady=8, sticky="ew")
|
||||
self.contact_form_message = RTLLabel(self.contact_form_card, text="", text_color=DANGER, font=ctk.CTkFont(family=FONT_BODY, size=13))
|
||||
self.contact_form_message.grid(row=2, column=0, pady=(2, 6), sticky="e")
|
||||
|
|
@ -840,11 +920,13 @@ class SecureSmsApp(ctk.CTk):
|
|||
RTLButton(
|
||||
self.contact_form_card,
|
||||
text='ذخیره',
|
||||
fg_color=PRIMARY,
|
||||
text_color="white",
|
||||
hover_color=PRIMARY_DARK,
|
||||
corner_radius=24,
|
||||
fg_color=BACKGROUND,
|
||||
hover_color=PRIMARY_SOFT,
|
||||
text_color=PRIMARY_DARK,
|
||||
border_color=PRIMARY,
|
||||
border_width=2,
|
||||
command=self._save_contact_inline,
|
||||
corner_radius=12,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=48,
|
||||
).grid(row=3, column=0, pady=10, sticky="ew")
|
||||
|
|
@ -853,12 +935,82 @@ class SecureSmsApp(ctk.CTk):
|
|||
self._register_text_input(self.contact_name_entry, title="نام مخاطب", layout="fa")
|
||||
self._register_text_input(self.contact_phone_entry, title="شماره موبایل", layout="numeric", submit=self._save_contact_inline)
|
||||
|
||||
# Drawer Menu Panel (Separate Page)
|
||||
self.drawer_panel = ctk.CTkFrame(self.root_frame, fg_color=BACKGROUND, corner_radius=0)
|
||||
self.drawer_panel.grid_rowconfigure(1, weight=1)
|
||||
self.drawer_panel.grid_columnconfigure(0, weight=1)
|
||||
|
||||
drawer_header = ctk.CTkFrame(self.drawer_panel, fg_color=CARD, corner_radius=0, border_width=0)
|
||||
drawer_header.grid(row=0, column=0, sticky="ew")
|
||||
drawer_header.grid_columnconfigure(0, weight=1)
|
||||
drawer_header.grid_columnconfigure(1, weight=0)
|
||||
|
||||
RTLLabel(
|
||||
drawer_header,
|
||||
text='منوی اصلی',
|
||||
text_color=TEXT,
|
||||
font=ctk.CTkFont(family=FONT_TITLE, size=18, weight="bold"),
|
||||
).grid(row=0, column=0, padx=16, pady=14, sticky="e")
|
||||
|
||||
RTLButton(
|
||||
drawer_header,
|
||||
text='بازگشت',
|
||||
width=64,
|
||||
height=36,
|
||||
corner_radius=18,
|
||||
fg_color=INPUT_BG,
|
||||
hover_color=BORDER,
|
||||
text_color=TEXT,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=14, weight="bold"),
|
||||
command=self._hide_drawer_menu
|
||||
).grid(row=0, column=1, padx=(4, 14), sticky="e")
|
||||
|
||||
drawer_body = ctk.CTkFrame(self.drawer_panel, fg_color=BACKGROUND, corner_radius=0)
|
||||
drawer_body.grid(row=1, column=0, sticky="nsew", padx=20, pady=20)
|
||||
drawer_body.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.drawer_modem_label = RTLLabel(
|
||||
drawer_body,
|
||||
text="",
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
justify="right"
|
||||
)
|
||||
self.drawer_modem_label.grid(row=0, column=0, pady=(10, 30), sticky="e")
|
||||
|
||||
RTLButton(
|
||||
drawer_body,
|
||||
text='تنظیمات',
|
||||
corner_radius=27,
|
||||
fg_color=BACKGROUND,
|
||||
hover_color=PRIMARY_SOFT,
|
||||
text_color=PRIMARY_DARK,
|
||||
border_color=PRIMARY,
|
||||
border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=54,
|
||||
command=self._open_settings_panel,
|
||||
).grid(row=1, column=0, pady=8, sticky="ew")
|
||||
|
||||
RTLButton(
|
||||
drawer_body,
|
||||
text='بخش ادمین',
|
||||
corner_radius=27,
|
||||
fg_color=BACKGROUND,
|
||||
hover_color="#F4F4F4",
|
||||
text_color=TEXT,
|
||||
border_color=BORDER,
|
||||
border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=54,
|
||||
command=self._open_admin_login,
|
||||
).grid(row=2, column=0, pady=8, sticky="ew")
|
||||
|
||||
self.contacts_frame = RTLScrollableFrame(
|
||||
self.sidebar,
|
||||
height=300 if self.is_portrait else 320,
|
||||
label_text='گفتگو\u200cها',
|
||||
label_font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
fg_color="transparent",
|
||||
fg_color=SIDEBAR,
|
||||
)
|
||||
self.contacts_frame.grid(row=1, column=0, padx=8, pady=4, sticky="nsew")
|
||||
|
||||
|
|
@ -904,6 +1056,13 @@ class SecureSmsApp(ctk.CTk):
|
|||
)
|
||||
self.chat_subtitle.grid(row=1, column=1, padx=8, pady=(0, 10), sticky="e")
|
||||
|
||||
def open_contact_page(e=None):
|
||||
if self.current_contact_phone:
|
||||
self._show_contact_details_page()
|
||||
|
||||
self.chat_title.bind("<Button-1>", open_contact_page, add="+")
|
||||
self.chat_subtitle.bind("<Button-1>", open_contact_page, add="+")
|
||||
|
||||
RTLButton(
|
||||
self.header_card,
|
||||
text='بازگشت',
|
||||
|
|
@ -917,7 +1076,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
command=self._show_home_screen
|
||||
).grid(row=0, column=2, rowspan=2, padx=(4, 14), sticky="e")
|
||||
|
||||
content = ctk.CTkFrame(self.main_panel, fg_color="transparent")
|
||||
content = ctk.CTkFrame(self.main_panel, fg_color=BACKGROUND, corner_radius=0)
|
||||
content.grid(row=1, column=0, padx=outer_pad, pady=(0, inner_pad), sticky="nsew")
|
||||
if self.is_portrait:
|
||||
content.grid_rowconfigure(0, weight=1)
|
||||
|
|
@ -930,7 +1089,7 @@ class SecureSmsApp(ctk.CTk):
|
|||
|
||||
self.chat_container = RTLScrollableFrame(
|
||||
content,
|
||||
fg_color="transparent"
|
||||
fg_color=BACKGROUND
|
||||
)
|
||||
if self.is_portrait:
|
||||
self.chat_container.grid(row=0, column=0, sticky="nsew", pady=(0, inner_pad))
|
||||
|
|
@ -972,10 +1131,12 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.secure_button = RTLButton(
|
||||
self.profile_card,
|
||||
text='فعال\u200cسازی ارتباط امن',
|
||||
fg_color=PRIMARY,
|
||||
hover_color=PRIMARY_DARK,
|
||||
text_color="#FFFFFF",
|
||||
corner_radius=8,
|
||||
corner_radius=21,
|
||||
fg_color=CARD,
|
||||
hover_color=PRIMARY_SOFT,
|
||||
text_color=PRIMARY_DARK,
|
||||
border_color=PRIMARY,
|
||||
border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
command=self._toggle_secure_mode,
|
||||
height=42,
|
||||
|
|
@ -984,10 +1145,12 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.normal_button = RTLButton(
|
||||
self.profile_card,
|
||||
text='بازگشت به حالت عادی',
|
||||
fg_color=INPUT_BG,
|
||||
corner_radius=20,
|
||||
fg_color=CARD,
|
||||
hover_color="#F4F4F4",
|
||||
text_color=TEXT,
|
||||
hover_color=BORDER,
|
||||
corner_radius=8,
|
||||
border_color=BORDER,
|
||||
border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
command=self._switch_to_normal,
|
||||
height=40,
|
||||
|
|
@ -1034,16 +1197,21 @@ class SecureSmsApp(ctk.CTk):
|
|||
self._register_text_input(self.message_entry, title="متن پیام", layout="fa", multiline=True)
|
||||
|
||||
self.overlay_frame = ctk.CTkFrame(
|
||||
self.main_panel,
|
||||
self.root_frame,
|
||||
fg_color=CARD,
|
||||
corner_radius=12,
|
||||
border_width=1,
|
||||
border_color=BORDER,
|
||||
)
|
||||
self.overlay_frame.grid(row=0, column=0, rowspan=3, padx=outer_pad, pady=outer_pad, sticky="nsew")
|
||||
self.overlay_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.overlay_frame.grid_columnconfigure(0, weight=1)
|
||||
self.overlay_frame.grid_remove()
|
||||
|
||||
self.contact_details_panel = ctk.CTkFrame(self.root_frame, fg_color=BACKGROUND, corner_radius=0)
|
||||
self.contact_details_panel.grid_rowconfigure(1, weight=1)
|
||||
self.contact_details_panel.grid_columnconfigure(0, weight=1)
|
||||
self.contact_details_panel.grid_remove()
|
||||
|
||||
self.refresh_all()
|
||||
self._show_home_screen()
|
||||
|
||||
|
|
@ -1051,6 +1219,8 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.current_contact_phone = None
|
||||
self._hide_virtual_keyboard()
|
||||
self.main_panel.grid_remove()
|
||||
if hasattr(self, 'contact_details_panel'):
|
||||
self.contact_details_panel.grid_remove()
|
||||
self.sidebar.grid(row=0, column=0, sticky="nsew")
|
||||
self._refresh_contacts()
|
||||
|
||||
|
|
@ -1067,7 +1237,9 @@ class SecureSmsApp(ctk.CTk):
|
|||
self.after(0, self._hide_mouse_cursor)
|
||||
|
||||
def handle_background_refresh(self, phone=None):
|
||||
self.refresh_all()
|
||||
if hasattr(self, '_refresh_timer') and self._refresh_timer:
|
||||
self.after_cancel(self._refresh_timer)
|
||||
self._refresh_timer = self.after(300, self.refresh_all)
|
||||
|
||||
def _refresh_connection_badge(self):
|
||||
if hasattr(self, 'drawer_modem_label') and self.drawer_modem_label.winfo_exists():
|
||||
|
|
@ -1092,33 +1264,18 @@ class SecureSmsApp(ctk.CTk):
|
|||
colors = ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#00BCD4", "#009688", "#4CAF50", "#FF9800", "#FF5722"]
|
||||
|
||||
for index, contact in enumerate(contacts):
|
||||
card = ctk.CTkFrame(self.contacts_frame, fg_color="transparent", corner_radius=12)
|
||||
card = ctk.CTkFrame(self.contacts_frame, fg_color=SIDEBAR, corner_radius=12)
|
||||
card.grid(row=index, column=0, padx=4, pady=4, sticky="ew")
|
||||
self.contacts_frame.grid_columnconfigure(0, weight=1)
|
||||
card.grid_columnconfigure(0, weight=1)
|
||||
card.grid_columnconfigure(1, weight=0)
|
||||
|
||||
def make_on_press(p=contact.phone, n=contact.name):
|
||||
def press_handler(e):
|
||||
self._long_press_triggered = False
|
||||
self._long_press_timer = self.after(800, lambda: self._trigger_long_press(p, n))
|
||||
return press_handler
|
||||
|
||||
def make_on_release():
|
||||
def release_handler(e):
|
||||
if hasattr(self, "_long_press_timer") and self._long_press_timer:
|
||||
self.after_cancel(self._long_press_timer)
|
||||
self._long_press_timer = None
|
||||
return release_handler
|
||||
|
||||
def make_onclick(p=contact.phone):
|
||||
def click_handler(e):
|
||||
if getattr(self, "_long_press_triggered", False):
|
||||
return
|
||||
self.after(10, lambda: self._select_contact(p))
|
||||
return click_handler
|
||||
|
||||
info_frame = ctk.CTkFrame(card, fg_color="transparent")
|
||||
info_frame = ctk.CTkFrame(card, fg_color=SIDEBAR, corner_radius=0)
|
||||
info_frame.grid(row=0, column=0, sticky="ew", padx=(10, 14), pady=10)
|
||||
|
||||
RTLLabel(info_frame, text=contact.name, text_color=TEXT, font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold")).pack(anchor="e")
|
||||
|
|
@ -1135,25 +1292,13 @@ class SecureSmsApp(ctk.CTk):
|
|||
first_letter = (contact.name or contact.phone)[0].upper()
|
||||
RTLLabel(avatar, text=first_letter, text_color="white", font=ctk.CTkFont(family=FONT_TITLE, size=22, weight="bold")).place(relx=0.5, rely=0.5, anchor="center")
|
||||
|
||||
card.bind("<ButtonPress-1>", make_on_press(), add="+")
|
||||
card.bind("<ButtonRelease-1>", make_on_release(), add="+")
|
||||
card.bind("<Button-1>", make_onclick())
|
||||
|
||||
info_frame.bind("<ButtonPress-1>", make_on_press(), add="+")
|
||||
info_frame.bind("<ButtonRelease-1>", make_on_release(), add="+")
|
||||
info_frame.bind("<Button-1>", make_onclick())
|
||||
|
||||
avatar.bind("<ButtonPress-1>", make_on_press(), add="+")
|
||||
avatar.bind("<ButtonRelease-1>", make_on_release(), add="+")
|
||||
avatar.bind("<Button-1>", make_onclick())
|
||||
|
||||
for child in info_frame.winfo_children():
|
||||
child.bind("<ButtonPress-1>", make_on_press(), add="+")
|
||||
child.bind("<ButtonRelease-1>", make_on_release(), add="+")
|
||||
child.bind("<Button-1>", make_onclick())
|
||||
for child in avatar.winfo_children():
|
||||
child.bind("<ButtonPress-1>", make_on_press(), add="+")
|
||||
child.bind("<ButtonRelease-1>", make_on_release(), add="+")
|
||||
child.bind("<Button-1>", make_onclick())
|
||||
|
||||
# Bind hover effects manually if needed, or rely on transparent bg
|
||||
|
|
@ -1232,23 +1377,55 @@ class SecureSmsApp(ctk.CTk):
|
|||
)
|
||||
bubble.pack(anchor=anchor, padx=8, pady=3, fill="none")
|
||||
|
||||
RTLLabel(
|
||||
state_val = getattr(message, 'transport_state', 'unknown').lower()
|
||||
if state_val in ["sent"]: state_text = "وضعیت: ارسال شده ✓"
|
||||
elif state_val in ["delivered", "read"]: state_text = "وضعیت: تحویل داده شده ✓✓"
|
||||
elif state_val in ["failed", "error"]: state_text = "وضعیت: ارسال ناموفق ✗"
|
||||
elif state_val in ["queued", "pending"]: state_text = "وضعیت: در صف ارسال ⏳"
|
||||
elif state_val in ["offline", "local"]: state_text = "وضعیت: ذخیره محلی 📴"
|
||||
else: state_text = f"وضعیت: {state_val}"
|
||||
|
||||
status_label = RTLLabel(
|
||||
bubble,
|
||||
text=state_text,
|
||||
text_color="#6B8E85" if is_out else MUTED,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=11, weight="bold"),
|
||||
justify="right"
|
||||
)
|
||||
|
||||
text_label = RTLLabel(
|
||||
bubble,
|
||||
text=message.body,
|
||||
text_color=TEXT,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15),
|
||||
wraplength=max(180, int(self.window_width * 0.5)),
|
||||
justify="right"
|
||||
).pack(padx=12, pady=(8, 2), anchor="e")
|
||||
)
|
||||
text_label.pack(padx=12, pady=(8, 2), anchor="e")
|
||||
|
||||
badge_text = f"🛡️ {message.created_at}" if message.mode == "secure" else message.created_at
|
||||
RTLLabel(
|
||||
badge_label = RTLLabel(
|
||||
bubble,
|
||||
text=badge_text,
|
||||
text_color=MUTED,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=10),
|
||||
justify="right"
|
||||
).pack(padx=12, pady=(0, 6), anchor="w" if is_out else "e")
|
||||
)
|
||||
badge_label.pack(padx=12, pady=(0, 6), anchor="w" if is_out else "e")
|
||||
|
||||
def make_toggle(lbl=status_label, is_out_dir=is_out):
|
||||
def toggle(e):
|
||||
if lbl.winfo_ismapped():
|
||||
lbl.pack_forget()
|
||||
else:
|
||||
lbl.pack(padx=12, pady=(0, 8), anchor="w" if is_out_dir else "e")
|
||||
return toggle
|
||||
|
||||
toggle_fn = make_toggle()
|
||||
bubble.bind("<Button-1>", toggle_fn, add="+")
|
||||
status_label.bind("<Button-1>", toggle_fn, add="+")
|
||||
text_label.bind("<Button-1>", toggle_fn, add="+")
|
||||
badge_label.bind("<Button-1>", toggle_fn, add="+")
|
||||
|
||||
try:
|
||||
self.after(50, lambda: self.chat_container._parent_canvas.yview_moveto(1.0))
|
||||
|
|
@ -1318,6 +1495,14 @@ class SecureSmsApp(ctk.CTk):
|
|||
if not name or not phone:
|
||||
self.contact_form_message.configure(text='نام و شماره هر دو لازم هستند.')
|
||||
return
|
||||
|
||||
persian_to_english = str.maketrans('۰۱۲۳۴۵۶۷۸۹', '0123456789')
|
||||
phone = phone.translate(persian_to_english)
|
||||
|
||||
if len(phone) != 11 or not phone.isdigit() or not phone.startswith("09"):
|
||||
self.contact_form_message.configure(text='شماره باید ۱۱ عدد باشد و با 09 شروع شود.')
|
||||
return
|
||||
|
||||
self.controller.save_contact(name, phone)
|
||||
self.current_contact_phone = phone
|
||||
self.contact_form_message.configure(text='مخاطب ذخیره شد.')
|
||||
|
|
@ -1348,9 +1533,12 @@ class SecureSmsApp(ctk.CTk):
|
|||
RTLButton(
|
||||
body,
|
||||
text='بله، حذف کن',
|
||||
fg_color=DANGER,
|
||||
text_color="white",
|
||||
hover_color="#9C3A4D",
|
||||
corner_radius=24,
|
||||
fg_color=SURFACE,
|
||||
hover_color="#FDE8E8",
|
||||
text_color=DANGER,
|
||||
border_color=DANGER,
|
||||
border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=48,
|
||||
command=confirm_delete,
|
||||
|
|
@ -1368,43 +1556,72 @@ class SecureSmsApp(ctk.CTk):
|
|||
).pack(fill="x", padx=18, pady=(8, 24))
|
||||
|
||||
def _show_drawer_menu(self):
|
||||
self._show_overlay()
|
||||
header = self._build_overlay_header('منو', 'گزینههای برنامه')
|
||||
header.pack(fill="x", padx=16, pady=(16, 10))
|
||||
|
||||
body = ctk.CTkFrame(self.overlay_frame, fg_color=SURFACE, corner_radius=22)
|
||||
body.pack(fill="both", expand=True, padx=16, pady=(0, 16))
|
||||
|
||||
self.drawer_modem_label = RTLLabel(
|
||||
body,
|
||||
text="",
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=15, weight="bold"),
|
||||
justify="right"
|
||||
)
|
||||
self.drawer_modem_label.pack(anchor="e", padx=18, pady=(24, 14))
|
||||
self._hide_virtual_keyboard()
|
||||
self.sidebar.grid_remove()
|
||||
self.drawer_panel.grid(row=0, column=0, sticky="nsew")
|
||||
self.drawer_panel.lift()
|
||||
self._refresh_connection_badge()
|
||||
|
||||
RTLButton(
|
||||
body,
|
||||
text='تنظیمات',
|
||||
fg_color=PRIMARY,
|
||||
text_color="white",
|
||||
hover_color=PRIMARY_DARK,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=48,
|
||||
command=self._open_settings_panel,
|
||||
).pack(fill="x", padx=18, pady=8)
|
||||
def _hide_drawer_menu(self):
|
||||
self.drawer_panel.grid_remove()
|
||||
self.sidebar.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
def _show_contact_details_page(self):
|
||||
self._hide_virtual_keyboard()
|
||||
self.main_panel.grid_remove()
|
||||
self.contact_details_panel.grid(row=0, column=0, sticky="nsew")
|
||||
self.contact_details_panel.lift()
|
||||
|
||||
for child in self.contact_details_panel.winfo_children():
|
||||
child.destroy()
|
||||
|
||||
header = ctk.CTkFrame(self.contact_details_panel, fg_color=CARD, corner_radius=0, border_width=0)
|
||||
header.grid(row=0, column=0, sticky="ew")
|
||||
header.grid_columnconfigure(0, weight=1)
|
||||
|
||||
RTLButton(
|
||||
body,
|
||||
text='بخش ادمین',
|
||||
fg_color=INPUT_BG,
|
||||
text_color=TEXT,
|
||||
hover_color=BORDER,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=48,
|
||||
command=self._open_admin_login,
|
||||
).pack(fill="x", padx=18, pady=(8, 24))
|
||||
header, text='بازگشت', width=64, height=36, corner_radius=18,
|
||||
fg_color=INPUT_BG, hover_color=BORDER, text_color=TEXT,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=14, weight="bold"),
|
||||
command=self._hide_contact_details_page
|
||||
).grid(row=0, column=1, padx=(4, 14), pady=(10, 10), sticky="e")
|
||||
|
||||
RTLLabel(
|
||||
header, text='پروفایل مخاطب', text_color=TEXT,
|
||||
font=ctk.CTkFont(family=FONT_TITLE, size=18, weight="bold"),
|
||||
).grid(row=0, column=0, padx=8, sticky="e")
|
||||
|
||||
body = ctk.CTkFrame(self.contact_details_panel, fg_color=BACKGROUND, corner_radius=0)
|
||||
body.grid(row=1, column=0, sticky="nsew", padx=20, pady=20)
|
||||
body.grid_columnconfigure(0, weight=1)
|
||||
|
||||
contact = self.controller.get_contact(self.current_contact_phone)
|
||||
if not contact:
|
||||
return
|
||||
|
||||
colors = ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#00BCD4", "#009688", "#4CAF50", "#FF9800", "#FF5722"]
|
||||
avatar_color = colors[hash(contact.name or contact.phone) % len(colors)]
|
||||
|
||||
avatar = ctk.CTkFrame(body, width=90, height=90, corner_radius=45, fg_color=avatar_color)
|
||||
avatar.pack(pady=(40, 10))
|
||||
avatar.pack_propagate(False)
|
||||
initial = contact.name[0] if contact.name else "?"
|
||||
RTLLabel(avatar, text=initial, text_color="white", font=ctk.CTkFont(family=FONT_TITLE, size=36, weight="bold")).place(relx=0.5, rely=0.5, anchor="center")
|
||||
|
||||
RTLLabel(body, text=contact.name, text_color=TEXT, font=ctk.CTkFont(family=FONT_TITLE, size=24, weight="bold")).pack(pady=(12, 2))
|
||||
RTLLabel(body, text=contact.phone, text_color=MUTED, font=ctk.CTkFont(family=FONT_BODY, size=16)).pack(pady=(0, 30))
|
||||
|
||||
RTLButton(
|
||||
body, text='پاک کردن پروفایل',
|
||||
corner_radius=24,
|
||||
fg_color=BACKGROUND, hover_color="#FDE8E8", text_color=DANGER, border_color=DANGER, border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"), height=48,
|
||||
command=lambda: self._show_delete_contact_dialog(contact.phone, contact.name)
|
||||
).pack(fill="x", padx=18, pady=(24, 8))
|
||||
|
||||
def _hide_contact_details_page(self):
|
||||
self.contact_details_panel.grid_remove()
|
||||
self._show_chat_screen()
|
||||
|
||||
def _open_settings_panel(self):
|
||||
self._show_overlay()
|
||||
|
|
@ -1435,8 +1652,8 @@ class SecureSmsApp(ctk.CTk):
|
|||
RTLButton(
|
||||
body,
|
||||
text='ورود به پنل ادمین',
|
||||
fg_color=PRIMARY,
|
||||
hover_color=PRIMARY_DARK,
|
||||
corner_radius=21,
|
||||
fg_color=SURFACE, hover_color=PRIMARY_SOFT, text_color=PRIMARY_DARK, border_color=PRIMARY, border_width=2,
|
||||
font=ctk.CTkFont(family=FONT_BODY, size=16, weight="bold"),
|
||||
height=42,
|
||||
command=self._open_admin_login,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user