improve Gitea PR plugin with correct filter options
- Replace custom filters with Gitea UI equivalents (your repos, assigned, created, review requested, reviewed, mentioned) - Fix pagination to fetch all pages from issues/search - Filter out false-positive issues (pull_request: null) - Use issues/search API for all filters (correct token scopes required) - Add StreamAction registration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -16,9 +16,12 @@
|
|||||||
"Repository": "Repository",
|
"Repository": "Repository",
|
||||||
"API Token": "API Token",
|
"API Token": "API Token",
|
||||||
"PR Filter": "PR-Filter",
|
"PR Filter": "PR-Filter",
|
||||||
|
"Your Repositories": "Eigene Repositories",
|
||||||
"Assigned to me": "Mir zugewiesen",
|
"Assigned to me": "Mir zugewiesen",
|
||||||
|
"Created by me": "Von mir erstellt",
|
||||||
"Review requested": "Review angefordert",
|
"Review requested": "Review angefordert",
|
||||||
"Both": "Beides",
|
"Reviewed by me": "Von mir bewertet",
|
||||||
|
"Mentioned me": "Hat mich erwähnt",
|
||||||
"Server": "Server",
|
"Server": "Server",
|
||||||
"Last Rised": "Letzter Anstieg",
|
"Last Rised": "Letzter Anstieg",
|
||||||
"Pull Requests": "Pull Requests",
|
"Pull Requests": "Pull Requests",
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
"Repository": "Repository",
|
"Repository": "Repository",
|
||||||
"API Token": "API Token",
|
"API Token": "API Token",
|
||||||
"PR Filter": "PR Filter",
|
"PR Filter": "PR Filter",
|
||||||
|
"Your Repositories": "Your Repositories",
|
||||||
"Assigned to me": "Assigned to me",
|
"Assigned to me": "Assigned to me",
|
||||||
|
"Created by me": "Created by me",
|
||||||
"Review requested": "Review requested",
|
"Review requested": "Review requested",
|
||||||
"Both": "Both",
|
"Reviewed by me": "Reviewed by me",
|
||||||
|
"Mentioned me": "Mentioned me",
|
||||||
"Server": "Server",
|
"Server": "Server",
|
||||||
"Last Rised": "Last Rised",
|
"Last Rised": "Last Rised",
|
||||||
"Pull Requests": "Pull Requests",
|
"Pull Requests": "Pull Requests",
|
||||||
|
|||||||
@@ -75,6 +75,19 @@
|
|||||||
],
|
],
|
||||||
"Tooltip": "Shows light color, time and mode from an ESP32 model railway controller via MQTT",
|
"Tooltip": "Shows light color, time and mode from an ESP32 model railway controller via MQTT",
|
||||||
"UUID": "dev.mars3142.ulanzideck.collection.railway"
|
"UUID": "dev.mars3142.ulanzideck.collection.railway"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Model Railway Stream",
|
||||||
|
"Icon": "assets/icons/railway.png",
|
||||||
|
"PropertyInspectorPath": "property-inspector/stream/inspector.html",
|
||||||
|
"States": [
|
||||||
|
{
|
||||||
|
"Name": "Default",
|
||||||
|
"Image": "assets/icons/railway.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Tooltip": "Displays the live 128x64 monochrome display stream from an ESP32 model railway controller via MQTT",
|
||||||
|
"UUID": "dev.mars3142.ulanzideck.collection.stream"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"OS": [
|
"OS": [
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class GiteaPRAction extends ActionBase {
|
|||||||
};
|
};
|
||||||
this.firstSeen = {};
|
this.firstSeen = {};
|
||||||
this.settingsLoaded = false;
|
this.settingsLoaded = false;
|
||||||
|
this.username = null;
|
||||||
|
|
||||||
this.$UD.onDidReceiveSettings(jsn => {
|
this.$UD.onDidReceiveSettings(jsn => {
|
||||||
if (jsn.context !== this.context) return;
|
if (jsn.context !== this.context) return;
|
||||||
@@ -30,9 +31,18 @@ class GiteaPRAction extends ActionBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRun() {
|
onRun() {
|
||||||
if (this.config.url) {
|
if (!this.config.url) return;
|
||||||
this.$UD.openUrl(`${this.config.url}/pulls?type=your_repositories&sort=&state=open&q=`);
|
const filter = this.config.filter || 'review_requested';
|
||||||
}
|
const filterMap = {
|
||||||
|
your_repos: 'type=your_repositories',
|
||||||
|
assigned: 'type=your_repositories&assignee=0',
|
||||||
|
created: 'type=your_repositories&poster=0',
|
||||||
|
review_requested: 'type=your_repositories&review_requested=true',
|
||||||
|
reviewed: 'type=your_repositories&reviewed=true',
|
||||||
|
mentioned: 'type=your_repositories&mentioned=0',
|
||||||
|
};
|
||||||
|
const q = filterMap[filter] || filterMap.your_repos;
|
||||||
|
this.$UD.openUrl(`${this.config.url}/pulls?${q}&state=open`);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPRs() {
|
fetchPRs() {
|
||||||
@@ -46,32 +56,36 @@ class GiteaPRAction extends ActionBase {
|
|||||||
const filter = this.config.filter || 'review_requested';
|
const filter = this.config.filter || 'review_requested';
|
||||||
const base = `${this.config.url}/api/v1/repos/issues/search?type=pullrequest&state=open&limit=50`;
|
const base = `${this.config.url}/api/v1/repos/issues/search?type=pullrequest&state=open&limit=50`;
|
||||||
|
|
||||||
const fetchPRs = async (url) => {
|
const fetchPages = async (url, paramChar = '&') => {
|
||||||
const r = await fetch(url, { headers, signal: AbortSignal.timeout(8000) });
|
const all = [];
|
||||||
console.log('GiteaPR status:', r.status, url);
|
let page = 1;
|
||||||
if (!r.ok) {
|
while (true) {
|
||||||
const body = await r.text();
|
const r = await fetch(`${url}${paramChar}page=${page}`, { headers, signal: AbortSignal.timeout(8000) });
|
||||||
console.log('GiteaPR error body:', body);
|
if (r.status === 404 || r.status === 403) return [];
|
||||||
return null;
|
if (!r.ok) {
|
||||||
|
console.log('GiteaPR error:', r.status, url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const data = await r.json();
|
||||||
|
if (!Array.isArray(data) || data.length === 0) break;
|
||||||
|
all.push(...data);
|
||||||
|
if (data.length < 50) break;
|
||||||
|
page++;
|
||||||
}
|
}
|
||||||
const data = await r.json();
|
return all;
|
||||||
return Array.isArray(data) ? data : [];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let prs;
|
const paramMap = {
|
||||||
if (filter === 'both') {
|
your_repos: '', all: '', both: '',
|
||||||
const [a, b] = await Promise.all([
|
assigned: '&assigned=true',
|
||||||
fetchPRs(`${base}&assigned=true`),
|
created: '&created=true',
|
||||||
fetchPRs(`${base}&review_requested=true`),
|
review_requested: '&review_requested=true',
|
||||||
]);
|
reviewed: '&reviewed=true',
|
||||||
if (a === null || b === null) { this.renderButton(null, true); return; }
|
mentioned: '&mentioned=true',
|
||||||
const seen = new Set();
|
};
|
||||||
prs = [...a, ...b].filter(pr => seen.has(pr.id) ? false : seen.add(pr.id));
|
const raw = await fetchPages(`${base}${paramMap[filter] ?? ''}`);
|
||||||
} else {
|
if (raw === null) { this.renderButton(null, true); return; }
|
||||||
const param = filter === 'assigned' ? '&assigned=true' : '&review_requested=true';
|
const prs = raw.filter(pr => pr.pull_request != null);
|
||||||
prs = await fetchPRs(`${base}${param}`);
|
|
||||||
if (prs === null) { this.renderButton(null, true); return; }
|
|
||||||
}
|
|
||||||
|
|
||||||
const wip = prs.filter(pr => pr.title?.startsWith('WIP: ')).length;
|
const wip = prs.filter(pr => pr.title?.startsWith('WIP: ')).length;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -120,7 +134,15 @@ class GiteaPRAction extends ActionBase {
|
|||||||
ctx.fillText(isError ? this.$UD.t('API Error') : this.$UD.t('Offline'), 98, 166);
|
ctx.fillText(isError ? this.$UD.t('API Error') : this.$UD.t('Offline'), 98, 166);
|
||||||
} else {
|
} else {
|
||||||
const filter = this.config.filter || 'review_requested';
|
const filter = this.config.filter || 'review_requested';
|
||||||
const label = filter === 'assigned' ? 'assigned' : filter === 'review_requested' ? 'review req.' : 'open';
|
const labelMap = {
|
||||||
|
your_repos: 'your repos',
|
||||||
|
assigned: 'assigned',
|
||||||
|
created: 'created',
|
||||||
|
review_requested: 'review req.',
|
||||||
|
reviewed: 'reviewed',
|
||||||
|
mentioned: 'mentioned',
|
||||||
|
};
|
||||||
|
const label = labelMap[filter] ?? 'your repos';
|
||||||
const color = count === 0 ? '#6bff6b' : count < 5 ? '#f0c040' : '#ff6b6b';
|
const color = count === 0 ? '#6bff6b' : count < 5 ? '#f0c040' : '#ff6b6b';
|
||||||
const text = count > 999 ? '999+' : String(count);
|
const text = count > 999 ? '999+' : String(count);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
<script src="./actions/GiteaAction.js"></script>
|
<script src="./actions/GiteaAction.js"></script>
|
||||||
<script src="./actions/GiteaPRAction.js"></script>
|
<script src="./actions/GiteaPRAction.js"></script>
|
||||||
<script src="./actions/RailwayAction.js"></script>
|
<script src="./actions/RailwayAction.js"></script>
|
||||||
|
<script src="./actions/StreamAction.js"></script>
|
||||||
<script src="./app.js"></script>
|
<script src="./app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ $UD.onAdd(jsn => {
|
|||||||
ACTION_CACHES[context] = new GiteaPRAction($UD, context);
|
ACTION_CACHES[context] = new GiteaPRAction($UD, context);
|
||||||
} else if (name === 'railway') {
|
} else if (name === 'railway') {
|
||||||
ACTION_CACHES[context] = new RailwayAction($UD, context);
|
ACTION_CACHES[context] = new RailwayAction($UD, context);
|
||||||
|
} else if (name === 'stream') {
|
||||||
|
ACTION_CACHES[context] = new StreamAction($UD, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@
|
|||||||
<div class="uspi-item">
|
<div class="uspi-item">
|
||||||
<div class="uspi-item-label" data-localize>PR Filter</div>
|
<div class="uspi-item-label" data-localize>PR Filter</div>
|
||||||
<select class="uspi-item-value" name="filter">
|
<select class="uspi-item-value" name="filter">
|
||||||
|
<option value="your_repos" data-localize>Your Repositories</option>
|
||||||
<option value="assigned" data-localize>Assigned to me</option>
|
<option value="assigned" data-localize>Assigned to me</option>
|
||||||
|
<option value="created" data-localize>Created by me</option>
|
||||||
<option value="review_requested" selected data-localize>Review requested</option>
|
<option value="review_requested" selected data-localize>Review requested</option>
|
||||||
<option value="both" data-localize>Both</option>
|
<option value="reviewed" data-localize>Reviewed by me</option>
|
||||||
|
<option value="mentioned" data-localize>Mentioned me</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="uspi-item">
|
<div class="uspi-item">
|
||||||
|
|||||||
Reference in New Issue
Block a user