mirror of
git://f0xx.org/android_cast
synced 2026-07-29 04:38:53 +03:00
sync BE
This commit is contained in:
@@ -1282,6 +1282,72 @@ button.report-tag--filter:hover {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.build-col-controls { width: 4.5rem; }
|
||||
.build-row-controls { vertical-align: middle; }
|
||||
.build-transport-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.build-transport-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .12),
|
||||
0 2px 8px rgba(0, 0, 0, .25),
|
||||
0 0 14px rgba(61, 139, 253, .1);
|
||||
transition: box-shadow .18s ease, border-color .18s ease, transform .12s ease;
|
||||
}
|
||||
.build-transport-btn:hover:not(:disabled),
|
||||
.build-transport-btn:focus-visible:not(:disabled) {
|
||||
border-color: var(--accent);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .45),
|
||||
0 0 18px rgba(61, 139, 253, .35);
|
||||
outline: none;
|
||||
}
|
||||
.build-transport-btn:active:not(:disabled) { transform: translateY(1px); }
|
||||
.build-transport-btn:disabled {
|
||||
opacity: .35;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
.build-transport-play::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 3px;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
border-left: 10px solid var(--accent);
|
||||
filter: drop-shadow(0 0 4px rgba(61, 139, 253, .45));
|
||||
}
|
||||
.build-transport-stop {
|
||||
border-color: rgba(248, 113, 113, .55);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(248, 113, 113, .25),
|
||||
0 2px 8px rgba(0, 0, 0, .25),
|
||||
0 0 12px rgba(248, 113, 113, .18);
|
||||
}
|
||||
.build-transport-stop::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 2px;
|
||||
background: var(--danger);
|
||||
box-shadow: 0 0 8px rgba(248, 113, 113, .55);
|
||||
}
|
||||
|
||||
.tag-edit-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: 2px;
|
||||
|
||||
@@ -40,6 +40,48 @@
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function graphPermalink(scopeKey, suffix, days) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('graph', scopeKey + '-' + suffix);
|
||||
if (days) {
|
||||
url.searchParams.set('days', String(days));
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function syncGraphPermalink(scopeKey, suffix) {
|
||||
const daysSel = el('graphs-days');
|
||||
const days = daysSel && daysSel.value ? daysSel.value : '14';
|
||||
const link = graphPermalink(scopeKey, suffix, days);
|
||||
if (window.history && window.history.replaceState) {
|
||||
window.history.replaceState(null, '', link);
|
||||
}
|
||||
const share = el('graph-detail-share');
|
||||
if (share) {
|
||||
share.href = link;
|
||||
}
|
||||
}
|
||||
|
||||
function clearGraphPermalink() {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('graph');
|
||||
if (window.history && window.history.replaceState) {
|
||||
window.history.replaceState(null, '', url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
function openGraphFromPermalink() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get('graph');
|
||||
if (!token) return;
|
||||
const node = document.getElementById('graph-' + token);
|
||||
if (!node) return;
|
||||
const card = node.closest('.card.card--lift');
|
||||
if (card) {
|
||||
openGraphDetail(card);
|
||||
}
|
||||
}
|
||||
|
||||
function parseCanvasId(id) {
|
||||
const m = String(id || '').match(/^graph-(user|slug|platform)-(.+)$/);
|
||||
if (!m) return null;
|
||||
@@ -430,6 +472,7 @@
|
||||
const overlay = el('graph-detail-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
detailState = null;
|
||||
clearGraphPermalink();
|
||||
const tip = el('graph-detail-tooltip');
|
||||
if (tip) tip.hidden = true;
|
||||
const statsEl = el('graph-detail-stats');
|
||||
@@ -449,6 +492,7 @@
|
||||
const scope = scopeData(parsed.scopeKey);
|
||||
if (!scope) return;
|
||||
const def = parsed.def;
|
||||
syncGraphPermalink(parsed.scopeKey, parsed.suffix);
|
||||
const fieldVal = scope[def.field];
|
||||
const titleEl = el('graph-detail-title');
|
||||
const canvasEl = el('graph-detail-canvas');
|
||||
@@ -655,6 +699,21 @@
|
||||
});
|
||||
const closeBtn = el('graph-detail-close');
|
||||
if (closeBtn) closeBtn.addEventListener('click', closeGraphDetail);
|
||||
const copyBtn = el('graph-detail-copy-link');
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener('click', function () {
|
||||
const share = el('graph-detail-share');
|
||||
const link = (share && share.href && share.href !== '#') ? share.href : window.location.href;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(link).then(function () {
|
||||
copyBtn.textContent = 'Copied';
|
||||
setTimeout(function () { copyBtn.textContent = 'Copy link'; }, 1500);
|
||||
}).catch(function () { window.prompt('Copy graph link:', link); });
|
||||
} else {
|
||||
window.prompt('Copy graph link:', link);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', (ev) => {
|
||||
if (ev.key === 'Escape') closeGraphDetail();
|
||||
@@ -680,6 +739,11 @@
|
||||
});
|
||||
|
||||
function load() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const daysParam = params.get('days');
|
||||
if (daysParam && daysSel) {
|
||||
daysSel.value = daysParam;
|
||||
}
|
||||
const days = Number(daysSel && daysSel.value ? daysSel.value : 14);
|
||||
status.textContent = 'Loading…';
|
||||
const xhr = new XMLHttpRequest();
|
||||
@@ -708,6 +772,7 @@
|
||||
markGraphBricks();
|
||||
const cols = colsSel && colsSel.value ? colsSel.value : '2';
|
||||
status.textContent = 'Loaded · viewer=' + viewer + ' · window=' + days + 'd · columns=' + cols;
|
||||
openGraphFromPermalink();
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
status.textContent = 'Network error';
|
||||
|
||||
@@ -246,7 +246,7 @@ final class GraphRepository {
|
||||
'viewer' => $viewer,
|
||||
'links' => self::serviceLinks(),
|
||||
];
|
||||
$payload['user'] = self::buildScope($days, $activeCompanyId, $scopeUserId);
|
||||
$payload['user'] = self::buildScope($days, $activeCompanyId, null);
|
||||
if ($viewer === 'user') {
|
||||
return $payload;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,23 @@ function json_out(array $data, int $code = 200): void {
|
||||
exit;
|
||||
}
|
||||
|
||||
/** Public URL prefix for the active console view (graphs vs crashes). */
|
||||
function console_base_path(?string $view = null): string {
|
||||
$view = $view ?? (string) ($_GET['view'] ?? '');
|
||||
if ($view === 'graphs') {
|
||||
$graphs = trim((string) cfg('graphs_base_path', ''));
|
||||
if ($graphs !== '') {
|
||||
return rtrim($graphs, '/');
|
||||
}
|
||||
$crashes = rtrim((string) cfg('base_path', ''), '/');
|
||||
if (str_ends_with($crashes, '/crashes')) {
|
||||
return substr($crashes, 0, -strlen('/crashes')) . '/graphs';
|
||||
}
|
||||
return '/app/androidcast_project/graphs';
|
||||
}
|
||||
return rtrim((string) cfg('base_path', ''), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw POST body for crash ingest. Supports plain JSON or Content-Encoding: gzip
|
||||
* (when nginx gunzip is off or the client talks to PHP directly).
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<?php endif; ?>
|
||||
<?php AnalyticsHead::render('crashes'); ?>
|
||||
</head>
|
||||
<body data-base-path="<?= h(Auth::basePath()) ?>"
|
||||
<body data-base-path="<?= h(console_base_path($view ?? null)) ?>"
|
||||
data-view="<?= h($view ?? 'home') ?>"
|
||||
data-can-tag-edit="<?= Auth::canEditTags() ? '1' : '0' ?>"
|
||||
<?= (($view ?? '') === 'report' && !empty($report['id'])) ? ' data-report-id="' . (int) $report['id'] . '"' : '' ?>
|
||||
@@ -357,6 +357,8 @@
|
||||
<p id="graph-detail-stats" class="graph-detail-stats muted" aria-live="polite"></p>
|
||||
</div>
|
||||
<button type="button" class="btn graph-detail-close" id="graph-detail-close" aria-label="Close full view">Close</button>
|
||||
<button type="button" class="btn" id="graph-detail-copy-link">Copy link</button>
|
||||
<a class="btn" id="graph-detail-share" href="#" hidden aria-hidden="true"></a>
|
||||
</header>
|
||||
<div class="graph-detail-canvas-wrap">
|
||||
<canvas id="graph-detail-canvas" hidden></canvas>
|
||||
|
||||
Reference in New Issue
Block a user