Skip to content

Commit fb71615

Browse files
authored
Merge pull request #654 from accius/Staging
Staging
2 parents e4c4ed3 + a641774 commit fb71615

File tree

110 files changed

+11810
-3094
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+11810
-3094
lines changed

.env.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,17 @@ SETTINGS_SYNC=false
9393
# ===========================================
9494

9595
# Units: 'imperial' or 'metric'
96+
# Deprecated
9697
UNITS=imperial
9798

99+
# Seperated Units ('imperial' or 'metric')
100+
# DISTUNITS - Distance units
101+
DISTUNITS=imperial
102+
# TEMPUNITS - Temperature Units
103+
TEMPUNITS=imperial
104+
# PRESSUNITS - Pressure Units
105+
PRESSUNITS=imperial
106+
98107
# Time format: '12' or '24' hour
99108
TIME_FORMAT=12
100109

@@ -240,3 +249,10 @@ VITE_PSTROTATOR_TARGET=http://192.168.1.43:50004
240249
# How long the server keeps logged QSOs in memory (minutes)
241250
# 1440 = 24 hours
242251
N3FJP_QSO_RETENTION_MINUTES=1440
252+
253+
# ===========================================
254+
# DOCKER / HEALTH CHECK
255+
# ===========================================
256+
257+
# Override the health check endpoint (useful behind a reverse proxy)
258+
# HEALTH_ENDPOINT=http://localhost:3000/api/health
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Auto move to In Progress on assignment
2+
3+
on:
4+
issues:
5+
types: [assigned]
6+
pull_request:
7+
types: [assigned]
8+
9+
jobs:
10+
move-to-in-progress:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/github-script@v7
14+
with:
15+
github-token: ${{ secrets.PROJECT_TOKEN }}
16+
script: |
17+
const projectNumber = 1;
18+
const orgOrUser = 'accius';
19+
const isOrg = false;
20+
21+
// Get project ID
22+
const query = isOrg
23+
? `query { organization(login: "${orgOrUser}") { projectV2(number: ${projectNumber}) { id, field(name: "Status") { ... on ProjectV2SingleSelectField { id, options { id, name } } } } } }`
24+
: `query { user(login: "${orgOrUser}") { projectV2(number: ${projectNumber}) { id, field(name: "Status") { ... on ProjectV2SingleSelectField { id, options { id, name } } } } } }`;
25+
26+
const result = await github.graphql(query);
27+
const project = isOrg ? result.organization.projectV2 : result.user.projectV2;
28+
const statusField = project.field;
29+
const inProgressOption = statusField.options.find(o => o.name === 'In Progress');
30+
31+
// Get the item ID from the project
32+
const contentId = context.payload.issue?.node_id || context.payload.pull_request?.node_id;
33+
34+
// Find the item in the project
35+
const itemsQuery = `query {
36+
node(id: "${project.id}") {
37+
... on ProjectV2 {
38+
items(first: 100) {
39+
nodes { id, content { ... on Issue { id } ... on PullRequest { id } } }
40+
}
41+
}
42+
}
43+
}`;
44+
45+
const itemsResult = await github.graphql(itemsQuery);
46+
const item = itemsResult.node.items.nodes.find(
47+
i => i.content?.id === contentId
48+
);
49+
50+
if (!item) {
51+
console.log('Item not found in project — it may not be added to the board yet');
52+
return;
53+
}
54+
55+
// Update status to In Progress
56+
await github.graphql(`mutation {
57+
updateProjectV2ItemFieldValue(input: {
58+
projectId: "${project.id}"
59+
itemId: "${item.id}"
60+
fieldId: "${statusField.id}"
61+
value: { singleSelectOptionId: "${inProgressOption.id}" }
62+
}) { projectV2Item { id } }
63+
}`);
64+
65+
console.log('Moved to In Progress');

AddOns/APRS-AutoPos/aprs_autopos.user.js

Lines changed: 168 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ==UserScript==
22
// @name APRS Auto-Position for OpenHamClock
33
// @namespace http://tampermonkey.net/
4-
// @version 1.1
4+
// @version 1.2
55
// @description Automatically updates your station position in OpenHamClock based on APRS beacons
66
// @author DO3EET
77
// @match https://openhamclock.com/*
@@ -51,19 +51,21 @@
5151
const styles = `
5252
#ohc-addon-drawer {
5353
position: fixed;
54-
bottom: 20px;
54+
top: 100px;
5555
right: 20px;
5656
display: flex;
5757
flex-direction: row-reverse;
5858
align-items: center;
5959
gap: 10px;
6060
z-index: 10000;
6161
pointer-events: none;
62+
user-select: none;
6263
}
6364
#ohc-addon-drawer.ohc-vertical {
6465
flex-direction: column-reverse;
6566
}
6667
.ohc-addon-icon {
68+
position: relative;
6769
width: 45px;
6870
height: 45px;
6971
background: var(--bg-panel, rgba(17, 24, 32, 0.95));
@@ -80,7 +82,7 @@
8082
transition: all 0.3s ease;
8183
}
8284
.ohc-addon-icon:hover { border-color: var(--accent-amber, #ffb432); transform: scale(1.1); }
83-
#ohc-addon-launcher { background: var(--bg-tertiary, #1a2332); color: var(--accent-amber); }
85+
#ohc-addon-launcher { background: var(--bg-tertiary, #1a2332); color: var(--accent-amber); cursor: move; transition: transform 0.3s ease; }
8486
.ohc-addon-item { display: none; }
8587
8688
#ohc-autopos-container {
@@ -156,30 +158,189 @@
156158
if (!drawer) {
157159
drawer = document.createElement('div');
158160
drawer.id = 'ohc-addon-drawer';
161+
162+
const updateLayout = () => {
163+
if (!drawer) return;
164+
const rect = drawer.getBoundingClientRect();
165+
const winW = window.innerWidth;
166+
const winH = window.innerHeight;
167+
168+
const isRight = rect.left + rect.width / 2 > winW / 2;
169+
const isBottom = rect.top + rect.height / 2 > winH / 2;
170+
const isVert = drawer.classList.contains('ohc-vertical');
171+
172+
if (isVert) {
173+
drawer.style.flexDirection = isBottom ? 'column-reverse' : 'column';
174+
} else {
175+
drawer.style.flexDirection = isRight ? 'row-reverse' : 'row';
176+
}
177+
};
178+
159179
const savedLayout = localStorage.getItem('ohc_addon_layout') || 'horizontal';
160180
if (savedLayout === 'vertical') drawer.classList.add('ohc-vertical');
161181

182+
const savedPos = JSON.parse(localStorage.getItem('ohc_addon_pos') || '{}');
183+
if (savedPos.top) drawer.style.top = savedPos.top;
184+
if (savedPos.bottom) drawer.style.bottom = savedPos.bottom;
185+
if (savedPos.left) drawer.style.left = savedPos.left;
186+
if (savedPos.right) drawer.style.right = savedPos.right;
187+
188+
if (!savedPos.top && !savedPos.bottom) {
189+
drawer.style.top = '100px';
190+
drawer.style.right = '20px';
191+
}
192+
162193
const launcher = document.createElement('div');
163194
launcher.id = 'ohc-addon-launcher';
164195
launcher.className = 'ohc-addon-icon';
165196
launcher.innerHTML = '\uD83E\uDDE9';
166-
launcher.title = 'L: Toggle | R: Rotate';
197+
launcher.title = 'L: Toggle | M: Drag | R: Rotate';
198+
199+
let isDragging = false;
200+
let dragTimer = null;
201+
let wasDragged = false;
202+
let startX, startY, startTop, startLeft;
167203

168204
launcher.onclick = () => {
205+
if (wasDragged) {
206+
wasDragged = false;
207+
return;
208+
}
169209
const items = document.querySelectorAll('.ohc-addon-item');
170-
const isHidden = items[0]?.style.display !== 'flex';
210+
const isHidden = Array.from(items).some((el) => el.style.display !== 'flex');
171211
items.forEach((el) => (el.style.display = isHidden ? 'flex' : 'none'));
172212
launcher.style.transform = isHidden ? 'rotate(90deg)' : 'rotate(0deg)';
213+
updateLayout();
173214
};
174215

175216
launcher.oncontextmenu = (e) => {
176217
e.preventDefault();
177-
const isVert = drawer.classList.toggle('ohc-vertical');
178-
localStorage.setItem('ohc_addon_layout', isVert ? 'vertical' : 'horizontal');
218+
drawer.classList.toggle('ohc-vertical');
219+
localStorage.setItem('ohc_addon_layout', drawer.classList.contains('ohc-vertical') ? 'vertical' : 'horizontal');
220+
updateLayout();
221+
};
222+
223+
const startDrag = (x, y) => {
224+
isDragging = true;
225+
wasDragged = true;
226+
startX = x;
227+
startY = y;
228+
const rect = drawer.getBoundingClientRect();
229+
startTop = rect.top;
230+
startLeft = rect.left;
231+
launcher.style.cursor = 'grabbing';
179232
};
180233

234+
const handleMove = (x, y) => {
235+
if (!isDragging) return;
236+
const dx = x - startX;
237+
const dy = y - startY;
238+
drawer.style.top = startTop + dy + 'px';
239+
drawer.style.left = startLeft + dx + 'px';
240+
drawer.style.right = 'auto';
241+
drawer.style.bottom = 'auto';
242+
};
243+
244+
const stopDrag = () => {
245+
if (!isDragging) return;
246+
isDragging = false;
247+
launcher.style.cursor = 'move';
248+
249+
const rect = drawer.getBoundingClientRect();
250+
const winW = window.innerWidth;
251+
const winH = window.innerHeight;
252+
const isRight = rect.left + rect.width / 2 > winW / 2;
253+
const isBottom = rect.top + rect.height / 2 > winH / 2;
254+
255+
const pos = {};
256+
if (isRight) {
257+
drawer.style.left = 'auto';
258+
drawer.style.right = Math.max(0, winW - rect.right) + 'px';
259+
pos.right = drawer.style.right;
260+
} else {
261+
drawer.style.right = 'auto';
262+
drawer.style.left = Math.max(0, rect.left) + 'px';
263+
pos.left = drawer.style.left;
264+
}
265+
266+
if (isBottom) {
267+
drawer.style.top = 'auto';
268+
drawer.style.bottom = Math.max(0, winH - rect.bottom) + 'px';
269+
pos.bottom = drawer.style.bottom;
270+
} else {
271+
drawer.style.bottom = 'auto';
272+
drawer.style.top = Math.max(0, rect.top) + 'px';
273+
pos.top = drawer.style.top;
274+
}
275+
276+
localStorage.setItem('ohc_addon_pos', JSON.stringify(pos));
277+
updateLayout();
278+
};
279+
280+
launcher.onmousedown = (e) => {
281+
if (e.button === 1) {
282+
e.preventDefault();
283+
startDrag(e.clientX, e.clientY);
284+
} else if (e.button === 0) {
285+
startX = e.clientX;
286+
startY = e.clientY;
287+
dragTimer = setTimeout(() => startDrag(e.clientX, e.clientY), 500);
288+
}
289+
};
290+
291+
document.addEventListener('mousemove', (e) => {
292+
if (!isDragging && dragTimer) {
293+
if (Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5) {
294+
clearTimeout(dragTimer);
295+
dragTimer = null;
296+
}
297+
}
298+
handleMove(e.clientX, e.clientY);
299+
});
300+
301+
document.addEventListener('mouseup', () => {
302+
clearTimeout(dragTimer);
303+
dragTimer = null;
304+
stopDrag();
305+
});
306+
307+
launcher.ontouchstart = (e) => {
308+
const touch = e.touches[0];
309+
startX = touch.clientX;
310+
startY = touch.clientY;
311+
dragTimer = setTimeout(() => {
312+
startDrag(touch.clientX, touch.clientY);
313+
if (window.navigator.vibrate) window.navigator.vibrate(20);
314+
}, 500);
315+
};
316+
317+
document.addEventListener(
318+
'touchmove',
319+
(e) => {
320+
const touch = e.touches[0];
321+
if (!isDragging && dragTimer) {
322+
if (Math.abs(touch.clientX - startX) > 5 || Math.abs(touch.clientY - startY) > 5) {
323+
clearTimeout(dragTimer);
324+
dragTimer = null;
325+
}
326+
}
327+
if (isDragging) {
328+
e.preventDefault();
329+
handleMove(touch.clientX, touch.clientY);
330+
}
331+
},
332+
{ passive: false },
333+
);
334+
335+
document.addEventListener('touchend', () => {
336+
clearTimeout(dragTimer);
337+
dragTimer = null;
338+
stopDrag();
339+
});
340+
181341
drawer.appendChild(launcher);
182342
document.body.appendChild(drawer);
343+
setTimeout(updateLayout, 100);
183344
}
184345

185346
const toggleBtn = document.createElement('div');

0 commit comments

Comments
 (0)