Pro firefox enhancements
This commit is contained in:
@@ -1,7 +1,12 @@
|
|||||||
import { HttpInterceptorFn, HttpRequest, HttpHandlerFn } from '@angular/common/http';
|
import { HttpInterceptorFn, HttpRequest, HttpHandlerFn } from '@angular/common/http';
|
||||||
|
|
||||||
export const authInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
|
export const authInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
|
||||||
const token = localStorage.getItem('kmtn_access_token');
|
let token: string | null = null;
|
||||||
|
try {
|
||||||
|
token = localStorage.getItem('kmtn_access_token');
|
||||||
|
} catch {
|
||||||
|
// Storage blocked (e.g. Mullvad Browser strict mode)
|
||||||
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const cloned = req.clone({
|
const cloned = req.clone({
|
||||||
|
|||||||
@@ -165,14 +165,21 @@
|
|||||||
margin: 0 0 $spacing-md;
|
margin: 0 0 $spacing-md;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
|
// WebKit line-clamp (Chrome, Safari)
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 3;
|
-webkit-line-clamp: 3;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
// Standard line-clamp (Firefox 122+) — overrides display for supporting browsers
|
||||||
|
display: block;
|
||||||
|
line-clamp: 3;
|
||||||
|
|
||||||
transition: max-height $transition-slow ease, opacity $transition-normal;
|
transition: max-height $transition-slow ease, opacity $transition-normal;
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
-webkit-line-clamp: unset;
|
-webkit-line-clamp: unset;
|
||||||
|
line-clamp: unset;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +248,7 @@
|
|||||||
|
|
||||||
.show-description {
|
.show-description {
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,5 +271,6 @@
|
|||||||
|
|
||||||
.show-description {
|
.show-description {
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,32 @@ import { AppUser, AuthTokenResponse } from '../interfaces/auth';
|
|||||||
|
|
||||||
const TOKEN_KEY = 'kmtn_access_token';
|
const TOKEN_KEY = 'kmtn_access_token';
|
||||||
|
|
||||||
|
// ── Safe localStorage helpers (privacy browsers may throw SecurityError) ──
|
||||||
|
|
||||||
|
function safeLocalStorageGet(key: string): string | null {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(key);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeLocalStorageSet(key: string, value: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, value);
|
||||||
|
} catch {
|
||||||
|
// Storage blocked — silently ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeLocalStorageRemove(key: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} catch {
|
||||||
|
// Storage blocked — silently ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
@@ -30,7 +56,7 @@ export class AuthService {
|
|||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
}).pipe(
|
}).pipe(
|
||||||
tap((resp) => localStorage.setItem(TOKEN_KEY, resp.access_token)),
|
tap((resp) => safeLocalStorageSet(TOKEN_KEY, resp.access_token)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,13 +65,13 @@ export class AuthService {
|
|||||||
return this.http.post<AuthTokenResponse>(`${this.baseUrl}/google`, {
|
return this.http.post<AuthTokenResponse>(`${this.baseUrl}/google`, {
|
||||||
id_token: idToken,
|
id_token: idToken,
|
||||||
}).pipe(
|
}).pipe(
|
||||||
tap((resp) => localStorage.setItem(TOKEN_KEY, resp.access_token)),
|
tap((resp) => safeLocalStorageSet(TOKEN_KEY, resp.access_token)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear stored token and reset auth state. */
|
/** Clear stored token and reset auth state. */
|
||||||
logout(): void {
|
logout(): void {
|
||||||
localStorage.removeItem(TOKEN_KEY);
|
safeLocalStorageRemove(TOKEN_KEY);
|
||||||
this.authStateSubject.next(null);
|
this.authStateSubject.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +109,7 @@ export class AuthService {
|
|||||||
// ── Private ──────────────────────────────────────────────
|
// ── Private ──────────────────────────────────────────────
|
||||||
|
|
||||||
private _getToken(): string | null {
|
private _getToken(): string | null {
|
||||||
return localStorage.getItem(TOKEN_KEY);
|
return safeLocalStorageGet(TOKEN_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _initAuth(): Promise<void> {
|
private async _initAuth(): Promise<void> {
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
|
|
||||||
|
// Firefox fallback: solid color when gradient text is unsupported
|
||||||
|
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
|
||||||
|
background: none;
|
||||||
|
color: $from;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin card-style {
|
@mixin card-style {
|
||||||
|
|||||||
Reference in New Issue
Block a user