mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-09-14 05:42:00 +02:00
Merge pull request #603
feat(transactions): update cuelume; bring back improved volume user setting
This commit is contained in:
@@ -3,3 +3,20 @@ from crispy_forms.layout import Field
|
|||||||
|
|
||||||
class Switch(Field):
|
class Switch(Field):
|
||||||
template = "crispy-daisyui/layout/switch.html"
|
template = "crispy-daisyui/layout/switch.html"
|
||||||
|
|
||||||
|
|
||||||
|
class Range(Field):
|
||||||
|
template = "crispy-daisyui/layout/range.html"
|
||||||
|
|
||||||
|
def render(self, form, context, extra_context=None, **kwargs):
|
||||||
|
extra_context = extra_context or {}
|
||||||
|
|
||||||
|
# Build the measure (ticks + labels) from the field's min/max/step
|
||||||
|
attrs = form.fields[self.fields[0]].widget.attrs
|
||||||
|
if "min" in attrs and "max" in attrs:
|
||||||
|
step = attrs.get("step") or 1
|
||||||
|
extra_context["range_steps"] = list(
|
||||||
|
range(int(attrs["min"]), int(attrs["max"]) + 1, int(step))
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().render(form, context, extra_context=extra_context, **kwargs)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class RangeInput(forms.NumberInput):
|
||||||
|
input_type = "range"
|
||||||
+18
-1
@@ -2,7 +2,9 @@ from datetime import timedelta
|
|||||||
|
|
||||||
from apps.common.middleware.thread_local import get_current_user
|
from apps.common.middleware.thread_local import get_current_user
|
||||||
from apps.users.models import APIToken
|
from apps.users.models import APIToken
|
||||||
|
from apps.common.widgets.crispy.daisyui import Range
|
||||||
from apps.common.widgets.crispy.submit import NoClassSubmit
|
from apps.common.widgets.crispy.submit import NoClassSubmit
|
||||||
|
from apps.common.widgets.range import RangeInput
|
||||||
from apps.common.widgets.tom_select import TomSelect
|
from apps.common.widgets.tom_select import TomSelect
|
||||||
from apps.users.models import UserSettings
|
from apps.users.models import UserSettings
|
||||||
from apps.accounts.models import Account
|
from apps.accounts.models import Account
|
||||||
@@ -131,6 +133,20 @@ class UserSettingsForm(forms.ModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
volume = forms.IntegerField(
|
||||||
|
min_value=0,
|
||||||
|
max_value=10,
|
||||||
|
label=_("Volume"),
|
||||||
|
help_text=_("Set to 0 to mute all sounds"),
|
||||||
|
widget=RangeInput(
|
||||||
|
attrs={
|
||||||
|
"step": 1,
|
||||||
|
# Preview the selected volume
|
||||||
|
"_": "on change call playSound('sparkle', my valueAsNumber)",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserSettings
|
model = UserSettings
|
||||||
fields = [
|
fields = [
|
||||||
@@ -141,6 +157,7 @@ class UserSettingsForm(forms.ModelForm):
|
|||||||
"datetime_format",
|
"datetime_format",
|
||||||
"number_format",
|
"number_format",
|
||||||
"default_account",
|
"default_account",
|
||||||
|
"volume",
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
"default_account": TomSelect(clear_button=False, group_by="group"),
|
"default_account": TomSelect(clear_button=False, group_by="group"),
|
||||||
@@ -167,7 +184,7 @@ class UserSettingsForm(forms.ModelForm):
|
|||||||
"start_page",
|
"start_page",
|
||||||
"default_account",
|
"default_account",
|
||||||
HTML('<hr class="hr my-3" />'),
|
HTML('<hr class="hr my-3" />'),
|
||||||
"volume",
|
Range("volume"),
|
||||||
FormActions(
|
FormActions(
|
||||||
NoClassSubmit("submit", _("Save"), css_class="btn btn-primary"),
|
NoClassSubmit("submit", _("Save"), css_class="btn btn-primary"),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.2.16 on 2026-09-12 21:53
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0027_alter_usersettings_timezone'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='usersettings',
|
||||||
|
name='volume',
|
||||||
|
field=models.PositiveIntegerField(default=10, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)], verbose_name='Volume'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -481,7 +481,7 @@ class UserSettings(models.Model):
|
|||||||
mute_sounds = models.BooleanField(default=False)
|
mute_sounds = models.BooleanField(default=False)
|
||||||
volume = models.PositiveIntegerField(
|
volume = models.PositiveIntegerField(
|
||||||
default=10,
|
default=10,
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(10)],
|
validators=[MinValueValidator(0), MaxValueValidator(10)],
|
||||||
verbose_name=_("Volume"),
|
verbose_name=_("Volume"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{% load crispy_forms_field %}
|
||||||
|
|
||||||
|
{% if field.is_hidden %}
|
||||||
|
{{ field }}
|
||||||
|
{% else %}
|
||||||
|
<{% if tag %}{{ tag }}{% else %}fieldset{% endif %} id="div_{{ field.auto_id }}" class="fieldset{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
|
||||||
|
{% if field.label and form_show_labels %}
|
||||||
|
<label {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="fieldset-legend{% if label_class %} {{ label_class }}{% endif %}{% if field.field.required %} requiredField{% endif %}">
|
||||||
|
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
<div class="w-full">
|
||||||
|
{% if field.errors %}
|
||||||
|
{% crispy_field field 'class' 'range range-error w-full' %}
|
||||||
|
{% else %}
|
||||||
|
{% crispy_field field 'class' 'range range-primary w-full' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if range_steps %}
|
||||||
|
<div class="flex justify-between px-2.5 mt-2 text-xs">
|
||||||
|
{% for step in range_steps %}<span>|</span>{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between px-2.5 mt-2 text-xs">
|
||||||
|
{% for step in range_steps %}<span>{{ step|stringformat:"02d" }}</span>{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
|
||||||
|
</{% if tag %}{{ tag }}{% else %}fieldset{% endif %}>
|
||||||
|
{% endif %}
|
||||||
Generated
+4
-4
@@ -25,7 +25,7 @@
|
|||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-chart-sankey": "^0.14.3",
|
"chartjs-chart-sankey": "^0.14.3",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"cuelume": "^0.1.2",
|
"cuelume": "^0.2.2",
|
||||||
"daisyui": "5.5.20",
|
"daisyui": "5.5.20",
|
||||||
"htmx.org": "^2.0.10",
|
"htmx.org": "^2.0.10",
|
||||||
"hyperscript.org": "^0.9.91",
|
"hyperscript.org": "^0.9.91",
|
||||||
@@ -1319,9 +1319,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cuelume": {
|
"node_modules/cuelume": {
|
||||||
"version": "0.1.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/cuelume/-/cuelume-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/cuelume/-/cuelume-0.2.2.tgz",
|
||||||
"integrity": "sha512-VxL0k9l1fyKGot3VKM+wm5Xd2K75fVeBHdNTrR1EHiqKRtD6S5Dxy/vY7hwl9gXp3QT/TIywFXUos77n39YZVQ==",
|
"integrity": "sha512-TiNzJRjhddjC4jy4M7sv63V/86VaFhdIs6iHUCrc4CKhqypaxRBv1aedCBGebrz/cO7nroPkEfqHe/P7fOE2GQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-chart-sankey": "^0.14.3",
|
"chartjs-chart-sankey": "^0.14.3",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"cuelume": "^0.1.2",
|
"cuelume": "^0.2.2",
|
||||||
"daisyui": "5.5.20",
|
"daisyui": "5.5.20",
|
||||||
"htmx.org": "^2.0.10",
|
"htmx.org": "^2.0.10",
|
||||||
"hyperscript.org": "^0.9.91",
|
"hyperscript.org": "^0.9.91",
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { play } from "cuelume";
|
import { play } from "cuelume";
|
||||||
|
|
||||||
|
|
||||||
window.playSound = play;
|
// User volume is stored as 0-10 (0 = muted); cuelume expects a 0-1 gain.
|
||||||
|
// Loudness is perceived logarithmically, so a linear mapping makes adjacent
|
||||||
|
// steps sound nearly identical. Squaring spreads them out (10 → 0dB, 5 → -12dB, 1 → -40dB).
|
||||||
|
// Pass `volume` to override the user's saved volume (e.g. to preview a new value).
|
||||||
|
window.playSound = (name, volume) => {
|
||||||
|
volume ??= JSON.parse(document.getElementById("volume")?.textContent ?? "10");
|
||||||
|
play(name, { volume: (volume / 10) ** 2 });
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user