1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 08:19:00 +03:00
This commit is contained in:
Anton Afanasyeu
2026-05-26 16:44:12 +02:00
parent b71d2bf3f0
commit 1e79c9a35f
3 changed files with 32 additions and 10 deletions

View File

@@ -8,19 +8,19 @@
isolation: isolate; isolation: isolate;
} }
/* 97% transparent watermark */ /* Debug watermark visibility */
.hub-page::before { .hub-page::before {
content: ""; content: "";
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: 0; z-index: 0;
pointer-events: none; pointer-events: none;
background: url("assets/hub-logo.svg") center center / min(92vmin, 960px) no-repeat; background: url("assets/hub-logo.svg") center center / cover no-repeat;
opacity: 0.03; opacity: 0.75;
} }
[data-theme="light"] .hub-page::before { [data-theme="light"] .hub-page::before {
opacity: 0.035; opacity: 0.75;
} }
.hub-shell { .hub-shell {

View File

@@ -33,10 +33,12 @@ body {
background: var(--bg); background: var(--bg);
color: var(--text); color: var(--text);
min-height: 100vh; min-height: 100vh;
display: flex;
flex-direction: column;
} }
a { color: var(--accent); text-decoration: none; } a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; } a:hover { text-decoration: underline; }
.shell { display: flex; min-height: calc(100vh - 40px); } .shell { display: flex; min-height: calc(100vh - 40px); flex: 1 0 auto; }
.shell.single .main-pane { max-width: 1100px; margin: 0 auto; padding: 24px; } .shell.single .main-pane { max-width: 1100px; margin: 0 auto; padding: 24px; }
.nav-pane { .nav-pane {
width: 56px; width: 56px;
@@ -404,9 +406,14 @@ a:hover { text-decoration: underline; }
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
padding: 0 16px; padding: 0 16px;
font-size: 12px; font-size: 12px;
color: var(--muted); color: var(--muted);
position: sticky;
bottom: 0;
background: var(--surface);
z-index: 40;
} }
.login-page { .login-page {
display: flex; display: flex;

View File

@@ -11,6 +11,7 @@ final class TicketRepository {
'device_model', 'device_model',
'owner_username', 'owner_username',
]; ];
private static ?array $ticketColumns = null;
public static function insert(array $payload): void { public static function insert(array $payload): void {
$err = TicketTagCatalog::validate(is_array($payload['tags'] ?? null) ? $payload['tags'] : []); $err = TicketTagCatalog::validate(is_array($payload['tags'] ?? null) ? $payload['tags'] : []);
@@ -88,9 +89,16 @@ final class TicketRepository {
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM tickets WHERE $whereSql"); $countStmt = $pdo->prepare("SELECT COUNT(*) FROM tickets WHERE $whereSql");
$countStmt->execute($params); $countStmt->execute($params);
$total = (int) $countStmt->fetchColumn(); $total = (int) $countStmt->fetchColumn();
$sql = "SELECT id, ticket_id, title, brief, opened_at_ms, updated_at_ms, created_at_ms, $select = 'id, ticket_id, title, brief, opened_at_ms, updated_at_ms, created_at_ms,
owner_username, verified_by_username, device_model, app_package, app_version, owner_username, verified_by_username, device_model, app_package, app_version,
os_name, os_version, rating, tags_json, workflow_state, assignees_json os_name, os_version, rating, tags_json';
if (self::hasTicketColumn('workflow_state')) {
$select .= ', workflow_state';
}
if (self::hasTicketColumn('assignees_json')) {
$select .= ', assignees_json';
}
$sql = "SELECT $select
FROM tickets WHERE $whereSql ORDER BY $sortCol $dir LIMIT $perPage OFFSET $offset"; FROM tickets WHERE $whereSql ORDER BY $sortCol $dir LIMIT $perPage OFFSET $offset";
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$stmt->execute($params); $stmt->execute($params);
@@ -181,7 +189,7 @@ final class TicketRepository {
$sets[] = 'verified_by_username = ?'; $sets[] = 'verified_by_username = ?';
$params[] = $v !== '' ? mb_substr($v, 0, 64) : null; $params[] = $v !== '' ? mb_substr($v, 0, 64) : null;
} }
if (isset($fields['workflow_state'])) { if (isset($fields['workflow_state']) && self::hasTicketColumn('workflow_state')) {
$state = TicketWorkflow::normalize((string) $fields['workflow_state']); $state = TicketWorkflow::normalize((string) $fields['workflow_state']);
$sets[] = 'workflow_state = ?'; $sets[] = 'workflow_state = ?';
$params[] = $state; $params[] = $state;
@@ -192,7 +200,7 @@ final class TicketRepository {
$sets[] = 'tags_json = ?'; $sets[] = 'tags_json = ?';
$params[] = json_encode($tags, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $params[] = json_encode($tags, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} }
if (array_key_exists('assignees', $fields)) { if (array_key_exists('assignees', $fields) && self::hasTicketColumn('assignees_json')) {
$assignees = self::normalizeAssignees($fields['assignees']); $assignees = self::normalizeAssignees($fields['assignees']);
$err = UserRepository::validateUsernames( $err = UserRepository::validateUsernames(
array_map(static fn (array $a): string => $a['username'], $assignees) array_map(static fn (array $a): string => $a['username'], $assignees)
@@ -239,6 +247,13 @@ final class TicketRepository {
} }
} }
private static function hasTicketColumn(string $column): bool {
if (self::$ticketColumns === null) {
self::$ticketColumns = Database::columnNames(Database::pdo(), 'tickets');
}
return in_array($column, self::$ticketColumns, true);
}
/** @param array<string, mixed> $row */ /** @param array<string, mixed> $row */
private static function mapListRow(array $row): array { private static function mapListRow(array $row): array {
$tags = parse_report_tags($row['tags_json'] ?? '[]'); $tags = parse_report_tags($row['tags_json'] ?? '[]');