feat(users): audio_resume_rewind_seconds column
Per-user resume rewind for the audiobook/podcast player: how many
seconds (0/5/10) before the saved position playback starts, both on
open-resume and again on every genuine unpause. Motivated by the car
startup case — exact resume loses the listener's place, and the
PlaybackRequestResolver path (phone UI, Android Auto, media buttons)
all funnel through one start position.
0 = exact resume (default), so the column is inert until the profile
page sets it. Follows the default_audio_speed / keep_screen_awake
precedent: idempotent ALTER in schema.sql (re-runs on every boot),
explicit GetUser SELECT, dedicated :exec update. Handler-side strict
allow-list {0,5,10} lands with the API slice.
This commit is contained in:
1 parent
0d613ba0e3
commit
daffe499eb
5 files changed
+61
-29
No files matched your search
@@ -48,6 +48,14 @@ ALTER TABLE users ADD COLUMN IF NOT EXISTS default_audio_speed REAL NOT NULL DEF
|
||||
-- mid-session. On by default; the profile page's On/Off dropdown owns it.
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS keep_screen_awake BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
|
||||
-- Audio resume rewind: seconds before the saved position the
|
||||
-- audiobook/podcast player starts (on open-resume AND again on every
|
||||
-- genuine unpause — the re-orientation gap, never stacked). Strict
|
||||
-- allow-list 0/5/10 enforced handler-side; 0 = exact resume. The
|
||||
-- profile page's dropdown owns it; the chapters payload carries it to
|
||||
-- every client (web + Android, incl. the car path).
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS audio_resume_rewind_seconds INT NOT NULL DEFAULT 0;
|
||||
|
||||
-- Create system_settings table
|
||||
CREATE TABLE IF NOT EXISTS system_settings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
+15
-14
@@ -640,18 +640,19 @@ type UserDashboardPreferencesGlobal struct {
|
||||
}
|
||||
|
||||
type Users struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Email string `db:"email" json:"email"`
|
||||
Username string `db:"username" json:"username"`
|
||||
PasswordHash string `db:"password_hash" json:"password_hash"`
|
||||
FirstName pgtype.Text `db:"first_name" json:"first_name"`
|
||||
LastName pgtype.Text `db:"last_name" json:"last_name"`
|
||||
Role string `db:"role" json:"role"`
|
||||
Theme pgtype.Text `db:"theme" json:"theme"`
|
||||
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
Timezone pgtype.Text `db:"timezone" json:"timezone"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
|
||||
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Email string `db:"email" json:"email"`
|
||||
Username string `db:"username" json:"username"`
|
||||
PasswordHash string `db:"password_hash" json:"password_hash"`
|
||||
FirstName pgtype.Text `db:"first_name" json:"first_name"`
|
||||
LastName pgtype.Text `db:"last_name" json:"last_name"`
|
||||
Role string `db:"role" json:"role"`
|
||||
Theme pgtype.Text `db:"theme" json:"theme"`
|
||||
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
Timezone pgtype.Text `db:"timezone" json:"timezone"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
|
||||
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
|
||||
AudioResumeRewindSeconds int32 `db:"audio_resume_rewind_seconds" json:"audio_resume_rewind_seconds"`
|
||||
}
|
||||
@@ -894,6 +894,7 @@ type Querier interface {
|
||||
UpdateSystemSetting(ctx context.Context, arg UpdateSystemSettingParams) error
|
||||
// Update universal progress
|
||||
UpdateUniversalProgress(ctx context.Context, arg UpdateUniversalProgressParams) (ReadingProgress, error)
|
||||
UpdateUserAudioResumeRewind(ctx context.Context, arg UpdateUserAudioResumeRewindParams) error
|
||||
UpdateUserDefaultAudioSpeed(ctx context.Context, arg UpdateUserDefaultAudioSpeedParams) error
|
||||
UpdateUserKeepScreenAwake(ctx context.Context, arg UpdateUserKeepScreenAwakeParams) error
|
||||
UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDevicesParams) (Users, error)
|
||||
|
||||
@@ -11407,26 +11407,28 @@ SELECT
|
||||
u.timezone,
|
||||
u.default_audio_speed,
|
||||
u.keep_screen_awake,
|
||||
u.audio_resume_rewind_seconds,
|
||||
(SELECT COUNT(*) FROM devices WHERE user_id = u.id) as device_count
|
||||
FROM users u
|
||||
WHERE u.id = $1
|
||||
`
|
||||
|
||||
type GetUserRow struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Email string `db:"email" json:"email"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Theme pgtype.Text `db:"theme" json:"theme"`
|
||||
FirstName pgtype.Text `db:"first_name" json:"first_name"`
|
||||
LastName pgtype.Text `db:"last_name" json:"last_name"`
|
||||
Role string `db:"role" json:"role"`
|
||||
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
Timezone pgtype.Text `db:"timezone" json:"timezone"`
|
||||
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
|
||||
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
|
||||
DeviceCount int64 `db:"device_count" json:"device_count"`
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Email string `db:"email" json:"email"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Theme pgtype.Text `db:"theme" json:"theme"`
|
||||
FirstName pgtype.Text `db:"first_name" json:"first_name"`
|
||||
LastName pgtype.Text `db:"last_name" json:"last_name"`
|
||||
Role string `db:"role" json:"role"`
|
||||
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
Timezone pgtype.Text `db:"timezone" json:"timezone"`
|
||||
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
|
||||
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
|
||||
AudioResumeRewindSeconds int32 `db:"audio_resume_rewind_seconds" json:"audio_resume_rewind_seconds"`
|
||||
DeviceCount int64 `db:"device_count" json:"device_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error) {
|
||||
@@ -11446,6 +11448,7 @@ func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, erro
|
||||
&i.Timezone,
|
||||
&i.DefaultAudioSpeed,
|
||||
&i.KeepScreenAwake,
|
||||
&i.AudioResumeRewindSeconds,
|
||||
&i.DeviceCount,
|
||||
)
|
||||
return i, err
|
||||
@@ -20287,6 +20290,20 @@ func (q *Queries) UpdateUniversalProgress(ctx context.Context, arg UpdateUnivers
|
||||
return i, err
|
||||
}
|
||||
|
||||
const UpdateUserAudioResumeRewind = `-- name: UpdateUserAudioResumeRewind :exec
|
||||
UPDATE users SET audio_resume_rewind_seconds = $2, updated_at = NOW() WHERE id = $1
|
||||
`
|
||||
|
||||
type UpdateUserAudioResumeRewindParams struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
AudioResumeRewindSeconds int32 `db:"audio_resume_rewind_seconds" json:"audio_resume_rewind_seconds"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserAudioResumeRewind(ctx context.Context, arg UpdateUserAudioResumeRewindParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdateUserAudioResumeRewind, arg.ID, arg.AudioResumeRewindSeconds)
|
||||
return err
|
||||
}
|
||||
|
||||
const UpdateUserDefaultAudioSpeed = `-- name: UpdateUserDefaultAudioSpeed :exec
|
||||
UPDATE users SET default_audio_speed = $2, updated_at = NOW() WHERE id = $1
|
||||
`
|
||||
@@ -20317,7 +20334,7 @@ func (q *Queries) UpdateUserKeepScreenAwake(ctx context.Context, arg UpdateUserK
|
||||
|
||||
const UpdateUserMaxDevices = `-- name: UpdateUserMaxDevices :one
|
||||
UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1
|
||||
RETURNING id, email, username, password_hash, first_name, last_name, role, theme, max_devices, created_at, timezone, updated_at, default_audio_speed, keep_screen_awake
|
||||
RETURNING id, email, username, password_hash, first_name, last_name, role, theme, max_devices, created_at, timezone, updated_at, default_audio_speed, keep_screen_awake, audio_resume_rewind_seconds
|
||||
`
|
||||
|
||||
type UpdateUserMaxDevicesParams struct {
|
||||
@@ -20343,6 +20360,7 @@ func (q *Queries) UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDev
|
||||
&i.UpdatedAt,
|
||||
&i.DefaultAudioSpeed,
|
||||
&i.KeepScreenAwake,
|
||||
&i.AudioResumeRewindSeconds,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ SELECT
|
||||
u.timezone,
|
||||
u.default_audio_speed,
|
||||
u.keep_screen_awake,
|
||||
u.audio_resume_rewind_seconds,
|
||||
(SELECT COUNT(*) FROM devices WHERE user_id = u.id) as device_count
|
||||
FROM users u
|
||||
WHERE u.id = $1;
|
||||
@@ -526,6 +527,9 @@ UPDATE users SET default_audio_speed = $2, updated_at = NOW() WHERE id = $1;
|
||||
-- name: UpdateUserKeepScreenAwake :exec
|
||||
UPDATE users SET keep_screen_awake = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
-- name: UpdateUserAudioResumeRewind :exec
|
||||
UPDATE users SET audio_resume_rewind_seconds = $2, updated_at = NOW() WHERE id = $1;
|
||||
|
||||
-- Per-book playback speed preference (player plan §8).
|
||||
|
||||
-- name: GetAudioPlaybackSpeed :one
|
||||
|
||||
Reference in new issue
Block a user