Skip to content

Commit 7eb2a66

Browse files
author
TeleGhost Dev
committed
Fix 8 critical issues for v1.0.4 patch release
1 parent 8f773b6 commit 7eb2a66

File tree

6 files changed

+121
-56
lines changed

6 files changed

+121
-56
lines changed

android/app/src/main/java/com/teleghost/app/TeleGhostService.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class TeleGhostService : Service() {
121121
val channel = NotificationChannel(
122122
CHANNEL_ID,
123123
getString(R.string.notification_channel_name),
124-
NotificationManager.IMPORTANCE_LOW // LOW = без звука, но видно в шторке
124+
NotificationManager.IMPORTANCE_DEFAULT // DEFAULT = visible and persistent
125125
).apply {
126126
description = getString(R.string.notification_channel_description)
127127
setShowBadge(false)
@@ -155,11 +155,12 @@ class TeleGhostService : Service() {
155155
.setContentText(getString(R.string.notification_text))
156156
.setSmallIcon(R.mipmap.ic_launcher_foreground) // TODO: заменить на кастомную иконку
157157
.setContentIntent(openPending)
158-
.setOngoing(true) // Не смахивается
159-
.setAutoCancel(false) // Не закрывается при нажатии
158+
.setOngoing(true) // Non-dismissible
159+
.setAutoCancel(false) // Doesn't close on click
160160
.setShowWhen(false)
161-
.setPriority(NotificationCompat.PRIORITY_LOW)
161+
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
162162
.setCategory(NotificationCompat.CATEGORY_SERVICE)
163+
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
163164
.addAction(
164165
android.R.drawable.ic_menu_close_clear_cancel,
165166
getString(R.string.notification_stop),

frontend/src/App.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@
112112
if (savedPinned) pinnedChats = JSON.parse(savedPinned);
113113
} catch (e) { console.error('Error loading pinned chats:', e); }
114114
115+
// Check if already logged in (Point 8)
116+
try {
117+
const myInfo = await AppActions.GetMyInfo();
118+
if (myInfo && myInfo.ID) {
119+
console.log("[App] Found existing session, skipping login to:", myInfo.Nickname);
120+
await loadMyInfo();
121+
screen = 'main';
122+
loadContacts();
123+
}
124+
} catch (e) { console.log("[App] No active session on startup"); }
125+
115126
// Back button support for mobile
116127
window.addEventListener('popstate', (e) => {
117128
if (isMobile) {
@@ -152,7 +163,7 @@
152163
// Remove optimistic messages that match (by tempId prefix)
153164
messages = [...(messages || []).filter(m => !m._optimistic), msg];
154165
}
155-
// scrollToBottom(); // User requested to remove auto-scroll on new messages
166+
// scrollToBottom();
156167
}
157168
loadContacts(); // Update last message
158169
});

frontend/src/components/Chat.svelte

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,29 @@
138138
return true;
139139
}
140140
141+
let pollInterval;
142+
141143
onMount(() => {
142144
if (containerRef) {
143-
// We only need to observe to SHOW/HIDE the button, NOT to force scroll
144145
resizeObserver = new ResizeObserver(() => {
145146
if (!containerRef) return;
146147
const distanceToBottom = containerRef.scrollHeight - containerRef.scrollTop - containerRef.clientHeight;
147-
showScrollButton = distanceToBottom > 50;
148+
showScrollButton = distanceToBottom > 80;
149+
150+
// Point 5: Auto-scroll to bottom on content changes if we were already at bottom
151+
if (distanceToBottom < 30 && chatReady) {
152+
scrollToBottom();
153+
}
148154
});
149155
resizeObserver.observe(containerRef);
150-
151-
return () => {
152-
resizeObserver.disconnect();
153-
};
154156
}
157+
158+
scrollToBottom(true);
159+
160+
return () => {
161+
if (pollInterval) clearInterval(pollInterval);
162+
if (resizeObserver) resizeObserver.disconnect();
163+
};
155164
});
156165
157166
// Handle Contact Change & Initialize Loading
@@ -163,45 +172,49 @@
163172
imagesLoading = true;
164173
pendingImages = 0;
165174
loadedImages = 0;
175+
176+
// Safety timeout to ensure chat shows even if image load hangs
177+
setTimeout(() => {
178+
if (!chatReady) {
179+
chatReady = true;
180+
imagesLoading = false;
181+
scrollToBottom(true);
182+
}
183+
}, 3000);
166184
}
167185
168186
// Handle Messages Update & Image Counting
169187
$: if (messages && currentContactId) {
170188
if (!chatReady) {
171-
chatReady = true;
172-
imagesLoading = false;
173-
scrollToBottom(true);
189+
// Check for images in new messages
190+
const images = messages.flatMap(m => m.Attachments || []).filter(a => a.MimeType && a.MimeType.startsWith('image/'));
191+
if (images.length > 0 && !initialScrollDone) {
192+
pendingImages = images.length;
193+
// chatReady will be set when images load OR timeout hits
194+
} else {
195+
chatReady = true;
196+
imagesLoading = false;
197+
scrollToBottom(true);
198+
}
174199
}
175200
}
176201
177202
function onImageLoad() {
178-
if (chatReady) return;
179-
loadedImages++;
180-
// finishLoading() could be here but we simplified it to messages reaction
181-
}
182-
183-
const dispatch = createEventDispatcher();
184-
let pollInterval;
185-
186-
onMount(() => {
187-
if (containerRef) {
188-
resizeObserver = new ResizeObserver(() => {
189-
if (!containerRef) return;
190-
const distanceToBottom = containerRef.scrollHeight - containerRef.scrollTop - containerRef.clientHeight;
191-
showScrollButton = distanceToBottom > 50;
192-
});
193-
resizeObserver.observe(containerRef);
194-
}
195-
196-
scrollToBottom(true);
197-
198-
return () => {
199-
if (pollInterval) clearInterval(pollInterval);
200-
if (resizeObserver) {
201-
resizeObserver.disconnect();
203+
if (chatReady) {
204+
// Smart scroll after image load if near bottom
205+
if (containerRef) {
206+
const dist = containerRef.scrollHeight - containerRef.scrollTop - containerRef.clientHeight;
207+
if (dist < 100) scrollToBottom(true);
202208
}
209+
return;
203210
}
204-
});
211+
loadedImages++;
212+
if (loadedImages >= pendingImages) {
213+
chatReady = true;
214+
imagesLoading = false;
215+
scrollToBottom(true);
216+
}
217+
}
205218
206219
// Auto-scroll on new messages
207220
$: if (messages && messages.length > 0 && chatReady) {
@@ -268,11 +281,11 @@
268281
</span>
269282
{#if isMobile}
270283
<button class="btn-icon-xs" style="margin-left: 8px; opacity: 0.7;" on:click|stopPropagation={() => {
271-
if(selectedContact?.PublicKey) {
272-
// Dispatch copy event or usage generic bridge
273-
// We need to import AppActions or similar if available, or just use native nav
274-
// Start simple:
275-
navigator.clipboard.writeText(selectedContact.PublicKey).then(() => alert('Адрес скопирован'));
284+
if(selectedContact?.I2PAddress) {
285+
import('../../wailsjs/go/main/App.js').then(Actions => {
286+
Actions.ClipboardSet(selectedContact.I2PAddress);
287+
dispatch('toast', { message: 'Адрес скопирован', type: 'success' });
288+
});
276289
}
277290
}}>
278291
<div class="icon-svg-xs">{@html Icons.Copy || '📋'}</div>

internal/appcore/appcore.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,17 +761,27 @@ func (a *AppCore) OnMessageReceived(msg *core.Message, senderPubKey, senderAddr
761761
return
762762
}
763763

764-
contact, _ := a.Repo.GetContactByPublicKey(a.Ctx, senderPubKey)
764+
var contact *core.Contact
765+
contact, _ = a.Repo.GetContactByPublicKey(a.Ctx, senderPubKey)
765766
if contact == nil {
766-
// Попробуем найти по адресу (для случая, когда контакт добавлен вручную по b32)
767+
// Try to find by address (for manual b32 contacts)
767768
contact, _ = a.Repo.GetContactByAddress(a.Ctx, senderAddr)
768769
if contact != nil {
769-
log.Printf("[AppCore] Found contact by address %s, updating PublicKey", senderAddr)
770+
oldChatID := contact.ChatID
771+
newChatID := identity.CalculateChatID(a.Identity.Keys.PublicKeyBase64, senderPubKey)
772+
773+
log.Printf("[AppCore] Discovered PublicKey for %s (%s). Migrating ChatID: %s -> %s", contact.Nickname, senderAddr, oldChatID, newChatID)
774+
770775
contact.PublicKey = senderPubKey
771-
a.Repo.SaveContact(a.Ctx, contact)
776+
contact.ChatID = newChatID
777+
contact.UpdatedAt = time.Now()
778+
779+
if err := a.Repo.UpdateContactAndMigrateChatID(a.Ctx, contact, oldChatID, newChatID); err != nil {
780+
log.Printf("[AppCore] Failed to migrate ChatID for %s: %v", contact.Nickname, err)
781+
}
772782
a.Emitter.Emit("contact_updated")
773783
} else {
774-
// Создаем контакт если неизвестен
784+
// Create new contact
775785
newChatID := identity.CalculateChatID(a.Identity.Keys.PublicKeyBase64, senderPubKey)
776786
contact = &core.Contact{
777787
ID: uuid.New().String(),

internal/network/router/router.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,16 +309,46 @@ func (r *SAMRouter) Dial(destination string) (net.Conn, error) {
309309
return nil, fmt.Errorf("router not started")
310310
}
311311

312-
// Парсим destination address
313-
addr, err := samConn.Lookup(destination)
314-
if err != nil {
315-
// Пробуем создать адрес напрямую из строки
312+
// Parse destination address
313+
// Point 6: Ensure SAM connection is still alive, reconnect if needed
314+
var addr i2pkeys.I2PAddr
315+
var err error
316+
317+
for i := 0; i < 2; i++ {
318+
addr, err = samConn.Lookup(destination)
319+
if err == nil {
320+
break
321+
}
322+
323+
// If lookup fails, maybe SAM connection is stale
324+
if i == 0 {
325+
log.Printf("[SAMRouter] Lookup failed: %v. Attempting to refresh SAM connection...", err)
326+
// We can't easily recreate the whole session here without locking everything,
327+
// but we can try to re-create the SAM bridge connection if possible.
328+
// Actually, if Lookup fails, it's likely the sam socket is dead.
329+
// Try to recreate sam3.SAM
330+
newSam, samErr := sam3.NewSAM(r.config.SAMAddress)
331+
if samErr == nil {
332+
r.mu.Lock()
333+
r.sam.Close()
334+
r.sam = newSam
335+
samConn = newSam
336+
r.mu.Unlock()
337+
continue
338+
}
339+
}
340+
341+
// Try direct parsing as fallback
316342
addr, err = i2pkeys.NewI2PAddrFromString(destination)
317-
if err != nil {
318-
return nil, fmt.Errorf("invalid destination: %w", err)
343+
if err == nil {
344+
break
319345
}
320346
}
321347

348+
if err != nil {
349+
return nil, fmt.Errorf("invalid destination: %w", err)
350+
}
351+
322352
conn, err := session.DialI2P(addr)
323353
if err != nil {
324354
return nil, fmt.Errorf("dial failed: %w", err)

mobile/mobile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ func dispatch(app *appcore.AppCore, method string, args []json.RawMessage) (inte
562562
return "У вас установлена последняя версия", nil
563563

564564
// === Utils ===
565-
case "CopyToClipboard":
565+
case "ClipboardSet":
566566
var text string
567567
parseArgs(args, &text)
568568
if bridge != nil {

0 commit comments

Comments
 (0)