Skip to content

Commit 6297ac9

Browse files
committed
Add support for other roles to edit clocks
1 parent d265880 commit 6297ac9

File tree

7 files changed

+76
-18
lines changed

7 files changed

+76
-18
lines changed

lang/en.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,17 @@
4848
"bottomLeft": "Above Player List"
4949
}
5050
},
51+
"minimumEditorRole": {
52+
"name": "Minimum Editor Role",
53+
"hint": "Minimum role required to edit clock values"
54+
},
5155
"offset": {
5256
"name": "Vertical Offset",
5357
"hint": "Distance from the top or bottom in pixels. Used to handle module incompatibility."
5458
}
59+
},
60+
"Warnings": {
61+
"NoActiveGM": "An Active GM must be online to use that feature"
5562
}
5663
}
5764
}

module/clock-panel.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export class ClockPanel extends fapi.HandlebarsApplicationMixin(fapi.Application
7171
const defaultColor = game.settings.get(MODULE_ID, "defaultColor");
7272
const backgroundColor = game.settings.get(MODULE_ID, "defaultBackgroundColor");
7373
const maxSpokes = 28; // limit when to render spokes to not fill with black
74+
const editable = this.db.canUserEdit(game.user);
7475
return clocks.map((data) => ({
7576
...data,
7677
value: Math.clamp(data.value, 0, data.max),
@@ -79,6 +80,7 @@ export class ClockPanel extends fapi.HandlebarsApplicationMixin(fapi.Application
7980
spokes: data.max > maxSpokes ? [] : Array(data.max).keys(),
8081
editable: game.user.isGM,
8182
visible: !data.private || game.user.isGM,
83+
editable,
8284
}));
8385
}
8486

@@ -136,11 +138,11 @@ export class ClockPanel extends fapi.HandlebarsApplicationMixin(fapi.Application
136138
this.#sortable?.destroy();
137139
this.#sortable = null;
138140
const clockList = html.querySelector(".clock-list");
139-
if (clockList) {
141+
if (clockList && game.user.isGM) {
140142
this.#sortable = new SortableJS(clockList, {
141143
animation: 200,
142144
direction: "vertical",
143-
draggable: ".clock-entry.editable",
145+
draggable: ".clock-entry",
144146
dragClass: "drag-preview",
145147
ghostClass: "drag-gap",
146148
onEnd: (event) => {

module/database.js

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MODULE_ID } from "./settings.js";
2+
13
const DEFAULT_CLOCK = {
24
type: "clock",
35
value: 0,
@@ -26,19 +28,30 @@ export class ClockDatabase extends Collection {
2628
delete(id) {
2729
const clocks = this.#getClockData();
2830
delete clocks[id];
29-
game.settings.set("global-progress-clocks", "activeClocks", clocks);
31+
game.settings.set(MODULE_ID, "activeClocks", clocks);
3032
}
3133

32-
update(data) {
34+
async update(data) {
3335
if (!this.#verifyClockData(data)) return;
3436

3537
const clocks = this.#getClockData();
3638
const existing = clocks[data.id];
3739
if (!existing) return;
3840

39-
foundry.utils.mergeObject(existing, data);
40-
existing.value = Math.clamp(existing.value, 0, existing.max);
41-
game.settings.set("global-progress-clocks", "activeClocks", clocks);
41+
const newData = foundry.utils.mergeObject(foundry.utils.duplicate(existing), data);
42+
const newValue = Math.clamp(newData.value, 0, newData.max);
43+
if (game.user.hasPermission('SETTINGS_MODIFY')) {
44+
Object.assign(existing, newData);
45+
existing.value = newValue;
46+
await game.settings.set(MODULE_ID, "activeClocks", clocks);
47+
} else if (this.canUserEdit(game.user)) {
48+
const gm = game.users.activeGM;
49+
if (gm) {
50+
await gm.query("global-progress-clocks", { action: "update", clock: { id: newData.id, value: newValue } });
51+
} else {
52+
ui.notifications.warn("GlobalProgressClocks.Warnings.NoActiveGM", { localize: true });
53+
}
54+
}
4255
}
4356

4457
move(id, idx) {
@@ -50,11 +63,17 @@ export class ClockDatabase extends Collection {
5063
clocks.splice(idx, 0, item);
5164

5265
const newData = Object.fromEntries(clocks.map((c) => [c.id, c]));
53-
game.settings.set("global-progress-clocks", "activeClocks", newData);
66+
game.settings.set(MODULE_ID, "activeClocks", newData);
67+
}
68+
69+
canUserEdit(user) {
70+
// return user.hasPermission('SETTINGS_MODIFY');
71+
const requiredPermission = game.settings.get(MODULE_ID, "minimumEditorRole");
72+
return user.role >= requiredPermission;
5473
}
5574

5675
#getClockData() {
57-
const entries = game.settings.get("global-progress-clocks", "activeClocks");
76+
const entries = game.settings.get(MODULE_ID, "activeClocks");
5877
for (const key of Object.keys(entries)) {
5978
entries[key] = { ...DEFAULT_CLOCK, ...entries[key] };
6079
}
@@ -72,6 +91,16 @@ export class ClockDatabase extends Collection {
7291
}
7392
}
7493

94+
handleQuery = async (data) => {
95+
const action = data.action;
96+
if (action === "update") {
97+
if (!game.user.isGM) return;
98+
const clock = data.clock;
99+
await this.update({ id: clock.id, value: clock.value });
100+
return { ok: true };
101+
}
102+
}
103+
75104
// Limit the clock max size to 128
76105
#verifyClockData(data) {
77106
const maxSize = data.type === "points" ? 99 : 128;

module/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Hooks.once("init", () => {
1010
window.clockDatabase.refresh();
1111
});
1212

13+
Hooks.once("setup", () => {
14+
CONFIG.queries["global-progress-clocks"] = window.clockDatabase.handleQuery;
15+
})
16+
1317
Hooks.on("canvasReady", () => {
1418
window.clockPanel.render(true);
1519
});

module/settings.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ function registerSettings() {
4343
config: false,
4444
onChange: () => window.clockDatabase.refresh(),
4545
});
46+
47+
game.settings.register(MODULE_ID, "minimumEditorRole", {
48+
name: "GlobalProgressClocks.Settings.minimumEditorRole.name",
49+
hint: "GlobalProgressClocks.Settings.minimumEditorRole.hint",
50+
scope: "world",
51+
config: true,
52+
default: CONST.USER_ROLES.GAMEMASTER,
53+
type: Number,
54+
choices: {
55+
1: "USER.RolePlayer",
56+
2: "USER.RoleTrusted",
57+
3: "USER.RoleAssistant",
58+
4: "USER.RoleGamemaster",
59+
},
60+
onChange: () => window.clockDatabase.refresh(),
61+
})
4662
}
4763

4864
class DisplaySettings extends FormApplication {

styles/style.css

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,17 @@ body:has(#effects-panel .effect-item) #clock-panel {
277277
}
278278

279279
/* Show controls, hide real name, when hovering */
280-
&.editable {
281-
&:hover {
282-
.controls {
283-
display: flex;
284-
}
280+
&:has(.controls):hover {
281+
.controls {
282+
display: flex;
283+
}
285284

286-
.name > .value {
287-
visibility: hidden;
288-
}
285+
.name > .value {
286+
visibility: hidden;
289287
}
288+
}
290289

290+
&.editable {
291291
.points,
292292
.clock {
293293
cursor: pointer;

templates/clock-panel.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<div class="clock-entry ui-control {{#if clock.editable}}editable{{/if}} {{#if clock.private}}private{{/if}} {{#unless clock.visible}}hidden{{/unless}}" data-id="{{clock.id}}">
99
<div class="name">
1010
<span class="value">{{#if clock.private}}<i class="fas fa-eye-slash"></i> {{/if}}{{clock.name}}</span>
11-
{{#if clock.editable}}
11+
{{#if @root.options.editable}}
1212
<div class="controls">
1313
<a data-action="deleteEntry"><i class="fa-solid fa-fw fa-trash"></i></a>
1414
<a data-action="editEntry"><i class="fa-solid fa-fw fa-edit"></i></a>

0 commit comments

Comments
 (0)