Mobilna adaptacja czatu: nowe komponenty i optymalizacje
Zespół wdrożył pełną adaptację na urządzenia mobilne opartą na WebView z ograniczoną funkcjonalnością, aby zmniejszyć obciążenie. Dodano bibliotekę RNNoiser do przetwarzania dźwięku, funkcję odroczonych wiadomości, przeprojektowano input z rozszerzonym formatowaniem, zmniejszono opóźnienia zapytań i zoptymalizowano przechowywanie danych. Wszystkie zmiany są skierowane do programistów middle/senior pracujących z React Native i WebView.
Responsywny UI dla interfejsów dotykowych
Opracowano MobileBottomNav — minimalistyczny dolny pasek nawigacji z pięcioma zakładkami: serwery, czaty, kanban, wyszukiwanie i profil. Komponent jest bezstanowy, przekazuje tylko ID aktywnego widoku przez onViewChange bez wewnętrznej logiki lub wywołań API.
import { Icon } from '@shared/ui/Icon';
import { UserAvatar } from '@shared/ui/UserAvatar';
export function MobileBottomNav({
currentView,
onViewChange,
user
}) {
const tabs = [
{ id: 'general-chats', icon: 'groupChat', label: 'Serwery' },
{ id: 'direct-chats', icon: 'chat', label: 'Czaty' },
{ id: 'chat-board', icon: 'board', label: 'Kanban' },
{ id: 'mobile-search', icon: 'search', label: 'Wyszukiwanie' },
{ id: 'profile', label: 'Profil', isProfile: true },
];
return (
<nav className="mobile-bottom-nav">
{tabs.map(tab => (
<button
key={tab.id}
className={`mobile-bottom-nav-tab ${currentView === tab.id ? 'active' : ''}`}
onClick={() => onViewChange(tab.id)}
>
<span className="mobile-bottom-nav-icon">
{tab.isProfile ? (
<UserAvatar
avatar={user?.avatar}
userId={user?.user_id}
username={user?.username}
status={user?.status}
size={28}
showStatus={false}
className="mobile-bottom-nav-avatar"
/>
) : (
<Icon name={tab.icon} size={22} />
)}
</span>
<span className="mobile-bottom-nav-label">{tab.label}</span>
</button>
))}
</nav>
);
}
Zakładka profilu używa UserAvatar zamiast ikony dla łatwiejszego rozpoznania wizualnego. Komponent nadrzędny obsługuje przełączanie widoków na podstawie otrzymanego ID.
Zoptymalizowana lista serwerów
Zamiast desktopowego LeftMenu stworzono MobileServersList pod interakcje dotykowe. Lewy panel to ikony serwerów w stylu Discord, prawy — tematy wybranego serwera. Komponent ponownie wykorzystuje hook useLeftMenu dla logiki biznesowej: ładowanie list, zdarzenia WebSocket, tworzenie serwerów. To minimalizuje duplikację kodu i eliminuje opóźnienia renderowania układu desktopowego.
Przejście przez dotknięcie serwera aktualizuje tematy po prawej bez pełnego przeładowania. Dotknięcie tematu otwiera czat.
Przeprojektowany input z rozszerzonym formatowaniem
Input dostosowano do formatu Mattermost z zapobieganiem przenoszenia elementów edytora. Funkcjonalność obejmuje:
- Pogrubienie i kursywa: standardowe formatowanie Markdown.
- Blok kodu: podświetlanie składni do wstawiania fragmentów.
- Diagramy PlantUML: generowanie UML z tekstowej składni bezpośrednio w wiadomości.
- Cytat: blokowe wyróżnienie z >.
- Link: osadzanie hiperłączy.
- Lista: punktowana lub numerowana.
- Ankieta: interaktywne głosowania.
To pozwala senior-developerom wymieniać się treściami technicznymi bez zewnętrznych narzędzi.
Kontekstowe menu i gesty
Dolne menu aktywuje się długim dotknięciem — ponownie użyto istniejącego komponentu z mobilnymi stylami pod isMobile. Reakcje na wiadomości otwierają się w responsywnym overlayu. Prawe menu zoptymalizowano CSS z logiką warunkową dla urządzeń mobilnych.
Osobna sekcja "Odroczone" agreguje wszystkie zaplanowane wiadomości dla szybkiego przeglądu planera.
Mobilny profil użytkownika
Stworzono dedykowany komponent MobileProfilePage z kartą użytkownika, statusem i sekcjami ustawień.
return (
<div className="mobile-profile-page">
<div className="mobile-profile-page-header">
<div className="mobile-profile-user-card">
<div className="mobile-profile-avatar">
{avatarUrl ? (
<img src={avatarUrl} alt={user.username} className="mobile-profile-avatar-img" />
) : (
<div className="mobile-profile-avatar-placeholder">{initials}</div>
)}
<span className={`status-icon ${user.status || 'offline'} mobile-profile-status"></span>
</div>
<div className="mobile-profile-info">
<div className="mobile-profile-username">{user.username}</div>
<div className="mobile-profile-handle">{user.handle || `@${user.username}`}</div>
</div>
</div>
</div>
<div className="mobile-profile-page-content">
<div className="mobile-profile-section">
<div className="mobile-profile-item" onClick={onOpenProfile}>
<span className="mobile-profile-item-icon"><Icon name="user" size={20} /></span>
<span className="mobile-profile-item-text">Edytuj profil</span>
<span className="mobile-profile-item-arrow"><Icon name="chevronRight" size={16} /></span>
</div>
{/* Pozostałe punkty analogicznie */}
</div>
{/* Sekcje statusów i wylogowania */}
</div>
</div>
);
Każdy punkt wywołuje callback, komponent nie zarządza modalami ani nawigacją.
Dodatkowe ulepszenia
- RNNoiser: zintegrowano do redukcji szumu w wiadomościach głosowych i połączeniach.
- Opóźnienia: zoptymalizowano zapytania przez buforowanie i grupowanie.
- Przechowywanie danych: pełna przebudowa z uwzględnieniem ograniczeń pamięci mobilnej.
Te zmiany zapewniają wydajność na słabszych urządzeniach bez uszczerbku dla funkcjonalności.
Co jest ważne
- Pełna adaptacja na WebView z oddzielnymi komponentami dla interfejsów dotykowych.
- Ponowne wykorzystanie logiki biznesowej przez hooki bez duplikacji.
- Rozszerzony input z PlantUML i ankietami dla czatów technicznych.
- Odroczone wiadomości w dedykowanej sekcji dla wygody planowania.
- Podejście bezstanowe w komponentach UI dla przewidywalnego renderowania.
— Editorial Team
Brak komentarzy.