mirror of
https://github.com/chenasraf/nextcloud-pantry.git
synced 2026-05-18 01:28:57 +00:00
refactor: unify pref endpoints
This commit is contained in:
@@ -20,9 +20,8 @@ use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
|
||||
/**
|
||||
* @psalm-import-type PantryLastHouse from ResponseDefinitions
|
||||
* @psalm-import-type PantryImageFolder from ResponseDefinitions
|
||||
* @psalm-import-type PantryNotificationPrefs from ResponseDefinitions
|
||||
* @psalm-import-type PantryUserPrefs from ResponseDefinitions
|
||||
* @psalm-import-type PantryHousePrefs from ResponseDefinitions
|
||||
*/
|
||||
final class PrefsController extends OCSController {
|
||||
use TranslatesDomainExceptions;
|
||||
@@ -38,252 +37,90 @@ final class PrefsController extends OCSController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user's last-used house id
|
||||
* Get all user-level preferences (not scoped to a house)
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryLastHouse, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, PantryUserPrefs, array{}>
|
||||
*
|
||||
* 200: Last house returned
|
||||
* 200: Prefs returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/prefs/last-house')]
|
||||
#[ApiRoute(verb: 'GET', url: '/api/prefs')]
|
||||
#[NoAdminRequired]
|
||||
public function getLastHouse(): DataResponse {
|
||||
public function getUserPrefs(): DataResponse {
|
||||
return $this->runAction(function (): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$houseId = $this->prefs->getLastHouseId($uid);
|
||||
$prefs = $this->prefs->getAllUserPrefs($uid);
|
||||
// If the saved house is no longer accessible, forget it.
|
||||
$houseId = $prefs['lastHouseId'] ?? null;
|
||||
if ($houseId !== null) {
|
||||
try {
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
} catch (ForbiddenException) {
|
||||
$this->prefs->setLastHouseId($uid, null);
|
||||
$houseId = null;
|
||||
$prefs['lastHouseId'] = null;
|
||||
}
|
||||
}
|
||||
return new DataResponse(['houseId' => $houseId]);
|
||||
return new DataResponse($prefs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current user's last-used house id
|
||||
* Update user-level preferences
|
||||
*
|
||||
* @param int|null $houseId House id, or null to clear.
|
||||
* @param int|null $lastHouseId Last-used house id, or null to clear.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryLastHouse, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, PantryUserPrefs, array{}>
|
||||
*
|
||||
* 200: Last house updated
|
||||
* 200: Prefs updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/prefs/last-house')]
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/prefs')]
|
||||
#[NoAdminRequired]
|
||||
public function setLastHouse(?int $houseId = null): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
public function setUserPrefs(?int $lastHouseId = null): DataResponse {
|
||||
return $this->runAction(function () use ($lastHouseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
if ($houseId !== null) {
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
$patch = [];
|
||||
if ($lastHouseId !== null) {
|
||||
$this->auth->requireMember($lastHouseId, $uid);
|
||||
$patch['lastHouseId'] = $lastHouseId;
|
||||
} else {
|
||||
// Explicit null means clear
|
||||
$patch['lastHouseId'] = null;
|
||||
}
|
||||
$this->prefs->setLastHouseId($uid, $houseId);
|
||||
return new DataResponse(['houseId' => $houseId]);
|
||||
$this->prefs->setUserPrefs($uid, $patch);
|
||||
return new DataResponse($this->prefs->getAllUserPrefs($uid));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's preferred image upload folder for a house
|
||||
* Get all per-house preferences for the current user
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryImageFolder, array{}>
|
||||
*
|
||||
* 200: Folder returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs/image-folder')]
|
||||
#[NoAdminRequired]
|
||||
public function getImageFolder(int $houseId): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
return new DataResponse(['folder' => $this->prefs->getImageFolder($uid, $houseId)]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's preferred image upload folder for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
* @param string $folder Absolute path within the user's files.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryImageFolder, array{}>
|
||||
*
|
||||
* 200: Folder updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs/image-folder')]
|
||||
#[NoAdminRequired]
|
||||
public function setImageFolder(int $houseId, string $folder): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $folder): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
$stored = $this->prefs->setImageFolder($uid, $houseId, $folder);
|
||||
return new DataResponse(['folder' => $stored]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get photo sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string, foldersFirst: bool}, array{}>
|
||||
*
|
||||
* 200: Sort preference returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs/photo-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function getPhotoSort(int $houseId): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
return new DataResponse([
|
||||
'sort' => $this->prefs->getPhotoSort($uid, $houseId),
|
||||
'foldersFirst' => $this->prefs->getPhotoFoldersFirst($uid, $houseId),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set photo sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
* @param string|null $sort Sort mode.
|
||||
* @param bool|null $foldersFirst Whether folders appear first.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string, foldersFirst: bool}, array{}>
|
||||
*
|
||||
* 200: Sort preference updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs/photo-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function setPhotoSort(int $houseId, ?string $sort = null, ?bool $foldersFirst = null): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $sort, $foldersFirst): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
if ($sort !== null) {
|
||||
$this->prefs->setPhotoSort($uid, $houseId, $sort);
|
||||
}
|
||||
if ($foldersFirst !== null) {
|
||||
$this->prefs->setPhotoFoldersFirst($uid, $houseId, $foldersFirst);
|
||||
}
|
||||
return new DataResponse([
|
||||
'sort' => $this->prefs->getPhotoSort($uid, $houseId),
|
||||
'foldersFirst' => $this->prefs->getPhotoFoldersFirst($uid, $houseId),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get note sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string}, array{}>
|
||||
*
|
||||
* 200: Sort preference returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs/note-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function getNoteSort(int $houseId): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
return new DataResponse([
|
||||
'sort' => $this->prefs->getNoteSort($uid, $houseId),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set note sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
* @param string $sort Sort mode.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string}, array{}>
|
||||
*
|
||||
* 200: Sort preference updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs/note-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function setNoteSort(int $houseId, string $sort): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $sort): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
$stored = $this->prefs->setNoteSort($uid, $houseId, $sort);
|
||||
return new DataResponse(['sort' => $stored]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checklist item sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string}, array{}>
|
||||
*
|
||||
* 200: Sort preference returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs/checklist-item-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function getChecklistItemSort(int $houseId): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
return new DataResponse([
|
||||
'sort' => $this->prefs->getChecklistItemSort($uid, $houseId),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set checklist item sort preference for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
* @param string $sort Sort mode.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{sort: string}, array{}>
|
||||
*
|
||||
* 200: Sort preference updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs/checklist-item-sort')]
|
||||
#[NoAdminRequired]
|
||||
public function setChecklistItemSort(int $houseId, string $sort): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $sort): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
$stored = $this->prefs->setChecklistItemSort($uid, $houseId, $sort);
|
||||
return new DataResponse(['sort' => $stored]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification preferences for a house
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryNotificationPrefs, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, PantryHousePrefs, array{}>
|
||||
*
|
||||
* 200: Prefs returned
|
||||
*/
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs/notifications')]
|
||||
#[ApiRoute(verb: 'GET', url: '/api/houses/{houseId}/prefs')]
|
||||
#[NoAdminRequired]
|
||||
public function getNotificationPrefs(int $houseId): DataResponse {
|
||||
public function getHousePrefs(int $houseId): DataResponse {
|
||||
return $this->runAction(function () use ($houseId): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
return new DataResponse($this->prefs->getNotificationPrefs($uid, $houseId));
|
||||
return new DataResponse($this->prefs->getAllHousePrefs($uid, $houseId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update notification preferences for a house
|
||||
* Update per-house preferences for the current user
|
||||
*
|
||||
* Only the fields present in the request body are updated; omitted fields
|
||||
* are left unchanged.
|
||||
*
|
||||
* @param int $houseId House id.
|
||||
* @param string|null $imageFolder Image upload folder path.
|
||||
* @param string|null $photoSort Photo sort mode.
|
||||
* @param bool|null $photoFoldersFirst Whether folders appear first in photo board.
|
||||
* @param string|null $noteSort Note sort mode.
|
||||
* @param string|null $checklistItemSort Checklist item sort mode.
|
||||
* @param bool|null $notifyPhoto Photo upload notifications.
|
||||
* @param bool|null $notifyNoteCreate Note creation notifications.
|
||||
* @param bool|null $notifyNoteEdit Note edit notifications.
|
||||
@@ -291,35 +128,44 @@ final class PrefsController extends OCSController {
|
||||
* @param bool|null $notifyItemRecur Recurring item reappeared notifications.
|
||||
* @param bool|null $notifyItemDone Item completed notifications.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, PantryNotificationPrefs, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, PantryHousePrefs, array{}>
|
||||
*
|
||||
* 200: Prefs updated
|
||||
*/
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs/notifications')]
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/houses/{houseId}/prefs')]
|
||||
#[NoAdminRequired]
|
||||
public function setNotificationPrefs(int $houseId, ?bool $notifyPhoto = null, ?bool $notifyNoteCreate = null, ?bool $notifyNoteEdit = null, ?bool $notifyItemAdd = null, ?bool $notifyItemRecur = null, ?bool $notifyItemDone = null): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $notifyPhoto, $notifyNoteCreate, $notifyNoteEdit, $notifyItemAdd, $notifyItemRecur, $notifyItemDone): DataResponse {
|
||||
public function setHousePrefs(
|
||||
int $houseId,
|
||||
?string $imageFolder = null,
|
||||
?string $photoSort = null,
|
||||
?bool $photoFoldersFirst = null,
|
||||
?string $noteSort = null,
|
||||
?string $checklistItemSort = null,
|
||||
?bool $notifyPhoto = null,
|
||||
?bool $notifyNoteCreate = null,
|
||||
?bool $notifyNoteEdit = null,
|
||||
?bool $notifyItemAdd = null,
|
||||
?bool $notifyItemRecur = null,
|
||||
?bool $notifyItemDone = null,
|
||||
): DataResponse {
|
||||
return $this->runAction(function () use ($houseId, $imageFolder, $photoSort, $photoFoldersFirst, $noteSort, $checklistItemSort, $notifyPhoto, $notifyNoteCreate, $notifyNoteEdit, $notifyItemAdd, $notifyItemRecur, $notifyItemDone): DataResponse {
|
||||
$uid = $this->requireUid();
|
||||
$this->auth->requireMember($houseId, $uid);
|
||||
if ($notifyPhoto !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_photo', $notifyPhoto);
|
||||
}
|
||||
if ($notifyNoteCreate !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_note_create', $notifyNoteCreate);
|
||||
}
|
||||
if ($notifyNoteEdit !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_note_edit', $notifyNoteEdit);
|
||||
}
|
||||
if ($notifyItemAdd !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_item_add', $notifyItemAdd);
|
||||
}
|
||||
if ($notifyItemRecur !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_item_recur', $notifyItemRecur);
|
||||
}
|
||||
if ($notifyItemDone !== null) {
|
||||
$this->prefs->setNotificationPref($uid, $houseId, 'notify_item_done', $notifyItemDone);
|
||||
}
|
||||
return new DataResponse($this->prefs->getNotificationPrefs($uid, $houseId));
|
||||
$patch = array_filter([
|
||||
'imageFolder' => $imageFolder,
|
||||
'photoSort' => $photoSort,
|
||||
'photoFoldersFirst' => $photoFoldersFirst,
|
||||
'noteSort' => $noteSort,
|
||||
'checklistItemSort' => $checklistItemSort,
|
||||
'notifyPhoto' => $notifyPhoto,
|
||||
'notifyNoteCreate' => $notifyNoteCreate,
|
||||
'notifyNoteEdit' => $notifyNoteEdit,
|
||||
'notifyItemAdd' => $notifyItemAdd,
|
||||
'notifyItemRecur' => $notifyItemRecur,
|
||||
'notifyItemDone' => $notifyItemDone,
|
||||
], fn ($v) => $v !== null);
|
||||
$this->prefs->setHousePrefs($uid, $houseId, $patch);
|
||||
return new DataResponse($this->prefs->getAllHousePrefs($uid, $houseId));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -70,11 +70,17 @@ namespace OCA\Pantry;
|
||||
*
|
||||
* @psalm-type PantrySuccess = array{success: true}
|
||||
*
|
||||
* @psalm-type PantryLastHouse = array{houseId: int|null}
|
||||
* @psalm-type PantryUserPrefs = array{
|
||||
* lastHouseId: int|null,
|
||||
* firstDayOfWeek: int,
|
||||
* }
|
||||
*
|
||||
* @psalm-type PantryImageFolder = array{folder: string}
|
||||
*
|
||||
* @psalm-type PantryNotificationPrefs = array{
|
||||
* @psalm-type PantryHousePrefs = array{
|
||||
* imageFolder: string,
|
||||
* photoSort: string,
|
||||
* photoFoldersFirst: bool,
|
||||
* noteSort: string,
|
||||
* checklistItemSort: string,
|
||||
* notifyPhoto: bool,
|
||||
* notifyNoteCreate: bool,
|
||||
* notifyNoteEdit: bool,
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace OCA\Pantry\Service;
|
||||
|
||||
use OCA\Pantry\AppInfo\Application;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
|
||||
class PrefsService {
|
||||
private const KEY_LAST_HOUSE = 'last_house_id';
|
||||
@@ -17,9 +18,18 @@ class PrefsService {
|
||||
|
||||
public function __construct(
|
||||
private IConfig $config,
|
||||
private IL10N $l,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getFirstDayOfWeek(string $uid): int {
|
||||
$value = $this->config->getUserValue($uid, 'core', 'first_day_of_week', '');
|
||||
if ($value === '') {
|
||||
return (int)$this->l->l('firstday', null);
|
||||
}
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
public function getLastHouseId(string $uid): ?int {
|
||||
$value = $this->config->getUserValue($uid, Application::APP_ID, self::KEY_LAST_HOUSE, '');
|
||||
if ($value === '') {
|
||||
@@ -52,6 +62,28 @@ class PrefsService {
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
// ----- Unified user prefs -----
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getAllUserPrefs(string $uid): array {
|
||||
return [
|
||||
'lastHouseId' => $this->getLastHouseId($uid),
|
||||
'firstDayOfWeek' => $this->getFirstDayOfWeek($uid),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $patch
|
||||
*/
|
||||
public function setUserPrefs(string $uid, array $patch): void {
|
||||
if (array_key_exists('lastHouseId', $patch)) {
|
||||
$v = $patch['lastHouseId'];
|
||||
$this->setLastHouseId($uid, is_int($v) ? $v : null);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Sort preferences -----
|
||||
|
||||
private const KEY_PHOTO_SORT = 'photo_sort';
|
||||
@@ -164,6 +196,57 @@ class PrefsService {
|
||||
];
|
||||
}
|
||||
|
||||
// ----- Unified house prefs -----
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getAllHousePrefs(string $uid, int $houseId): array {
|
||||
return [
|
||||
'imageFolder' => $this->getImageFolder($uid, $houseId),
|
||||
'photoSort' => $this->getPhotoSort($uid, $houseId),
|
||||
'photoFoldersFirst' => $this->getPhotoFoldersFirst($uid, $houseId),
|
||||
'noteSort' => $this->getNoteSort($uid, $houseId),
|
||||
'checklistItemSort' => $this->getChecklistItemSort($uid, $houseId),
|
||||
...$this->getNotificationPrefs($uid, $houseId),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $patch
|
||||
*/
|
||||
public function setHousePrefs(string $uid, int $houseId, array $patch): void {
|
||||
if (array_key_exists('imageFolder', $patch) && is_string($patch['imageFolder'])) {
|
||||
$this->setImageFolder($uid, $houseId, $patch['imageFolder']);
|
||||
}
|
||||
if (array_key_exists('photoSort', $patch) && is_string($patch['photoSort'])) {
|
||||
$this->setPhotoSort($uid, $houseId, $patch['photoSort']);
|
||||
}
|
||||
if (array_key_exists('photoFoldersFirst', $patch) && is_bool($patch['photoFoldersFirst'])) {
|
||||
$this->setPhotoFoldersFirst($uid, $houseId, $patch['photoFoldersFirst']);
|
||||
}
|
||||
if (array_key_exists('noteSort', $patch) && is_string($patch['noteSort'])) {
|
||||
$this->setNoteSort($uid, $houseId, $patch['noteSort']);
|
||||
}
|
||||
if (array_key_exists('checklistItemSort', $patch) && is_string($patch['checklistItemSort'])) {
|
||||
$this->setChecklistItemSort($uid, $houseId, $patch['checklistItemSort']);
|
||||
}
|
||||
// Notification prefs
|
||||
$notifKeys = [
|
||||
'notifyPhoto' => 'notify_photo',
|
||||
'notifyNoteCreate' => 'notify_note_create',
|
||||
'notifyNoteEdit' => 'notify_note_edit',
|
||||
'notifyItemAdd' => 'notify_item_add',
|
||||
'notifyItemRecur' => 'notify_item_recur',
|
||||
'notifyItemDone' => 'notify_item_done',
|
||||
];
|
||||
foreach ($notifKeys as $camel => $dbKey) {
|
||||
if (array_key_exists($camel, $patch) && is_bool($patch[$camel])) {
|
||||
$this->setNotificationPref($uid, $houseId, $dbKey, $patch[$camel]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeFolder(string $folder): string {
|
||||
$trimmed = trim($folder);
|
||||
if ($trimmed === '') {
|
||||
|
||||
1096
openapi.json
1096
openapi.json
File diff suppressed because it is too large
Load Diff
207
src/api/prefs.ts
207
src/api/prefs.ts
@@ -1,77 +1,44 @@
|
||||
import { ocs } from '@/axios'
|
||||
|
||||
// ----- User-level prefs (not per-house) -----
|
||||
|
||||
export interface UserPrefs {
|
||||
lastHouseId: number | null
|
||||
/** 0 = Sunday, 1 = Monday, …, 6 = Saturday. Read-only from server. */
|
||||
firstDayOfWeek: number
|
||||
}
|
||||
|
||||
export async function getUserPrefs(): Promise<UserPrefs> {
|
||||
const resp = await ocs.get<UserPrefs>('/prefs')
|
||||
return resp.data ?? { lastHouseId: null, firstDayOfWeek: 1 }
|
||||
}
|
||||
|
||||
export async function setUserPrefs(patch: Partial<UserPrefs>): Promise<UserPrefs> {
|
||||
const resp = await ocs.put<UserPrefs>('/prefs', patch)
|
||||
return resp.data ?? { lastHouseId: null, firstDayOfWeek: 1 }
|
||||
}
|
||||
|
||||
// Convenience wrappers (used widely, keep the simple API)
|
||||
export async function getLastHouse(): Promise<number | null> {
|
||||
const resp = await ocs.get<{ houseId: number | null }>('/prefs/last-house')
|
||||
return resp.data?.houseId ?? null
|
||||
const prefs = await getUserPrefs()
|
||||
return prefs.lastHouseId
|
||||
}
|
||||
|
||||
export async function setLastHouse(houseId: number | null): Promise<void> {
|
||||
await ocs.put('/prefs/last-house', { houseId })
|
||||
await setUserPrefs({ lastHouseId: houseId })
|
||||
}
|
||||
|
||||
export async function getImageFolder(houseId: number): Promise<string> {
|
||||
const resp = await ocs.get<{ folder: string }>(`/houses/${houseId}/prefs/image-folder`)
|
||||
return resp.data?.folder ?? '/Pantry'
|
||||
}
|
||||
|
||||
export async function setImageFolder(houseId: number, folder: string): Promise<string> {
|
||||
const resp = await ocs.put<{ folder: string }>(`/houses/${houseId}/prefs/image-folder`, {
|
||||
folder,
|
||||
})
|
||||
return resp.data?.folder ?? folder
|
||||
}
|
||||
// ----- Per-house prefs -----
|
||||
|
||||
export type PhotoSort = 'custom' | 'newest' | 'oldest' | 'description_asc' | 'description_desc'
|
||||
export type NoteSort = 'custom' | 'newest' | 'oldest' | 'title_asc' | 'title_desc'
|
||||
export type ChecklistItemSort = 'custom' | 'newest' | 'oldest' | 'name_asc' | 'name_desc'
|
||||
|
||||
export interface PhotoSortPrefs {
|
||||
sort: PhotoSort
|
||||
foldersFirst: boolean
|
||||
}
|
||||
|
||||
export async function getPhotoSort(houseId: number): Promise<PhotoSortPrefs> {
|
||||
const resp = await ocs.get<PhotoSortPrefs>(`/houses/${houseId}/prefs/photo-sort`)
|
||||
return resp.data ?? { sort: 'custom', foldersFirst: true }
|
||||
}
|
||||
|
||||
export async function setPhotoSort(
|
||||
houseId: number,
|
||||
prefs: Partial<PhotoSortPrefs>,
|
||||
): Promise<PhotoSortPrefs> {
|
||||
const resp = await ocs.put<PhotoSortPrefs>(`/houses/${houseId}/prefs/photo-sort`, prefs)
|
||||
return resp.data ?? { sort: 'custom', foldersFirst: true }
|
||||
}
|
||||
|
||||
export async function getNoteSort(houseId: number): Promise<{ sort: NoteSort }> {
|
||||
const resp = await ocs.get<{ sort: NoteSort }>(`/houses/${houseId}/prefs/note-sort`)
|
||||
return resp.data ?? { sort: 'custom' }
|
||||
}
|
||||
|
||||
export async function setNoteSort(houseId: number, sort: NoteSort): Promise<{ sort: NoteSort }> {
|
||||
const resp = await ocs.put<{ sort: NoteSort }>(`/houses/${houseId}/prefs/note-sort`, { sort })
|
||||
return resp.data ?? { sort }
|
||||
}
|
||||
|
||||
export type ChecklistItemSort = 'custom' | 'newest' | 'oldest' | 'name_asc' | 'name_desc'
|
||||
|
||||
export async function getChecklistItemSort(houseId: number): Promise<{ sort: ChecklistItemSort }> {
|
||||
const resp = await ocs.get<{ sort: ChecklistItemSort }>(
|
||||
`/houses/${houseId}/prefs/checklist-item-sort`,
|
||||
)
|
||||
return resp.data ?? { sort: 'custom' }
|
||||
}
|
||||
|
||||
export async function setChecklistItemSort(
|
||||
houseId: number,
|
||||
sort: ChecklistItemSort,
|
||||
): Promise<{ sort: ChecklistItemSort }> {
|
||||
const resp = await ocs.put<{ sort: ChecklistItemSort }>(
|
||||
`/houses/${houseId}/prefs/checklist-item-sort`,
|
||||
{ sort },
|
||||
)
|
||||
return resp.data ?? { sort }
|
||||
}
|
||||
|
||||
export interface NotificationPrefs {
|
||||
notifyPhoto: boolean
|
||||
notifyNoteCreate: boolean
|
||||
@@ -81,33 +48,115 @@ export interface NotificationPrefs {
|
||||
notifyItemDone: boolean
|
||||
}
|
||||
|
||||
export interface HousePrefs extends NotificationPrefs {
|
||||
imageFolder: string
|
||||
photoSort: PhotoSort
|
||||
photoFoldersFirst: boolean
|
||||
noteSort: NoteSort
|
||||
checklistItemSort: ChecklistItemSort
|
||||
}
|
||||
|
||||
const housePrefsDefaults: HousePrefs = {
|
||||
imageFolder: '/Pantry',
|
||||
photoSort: 'custom',
|
||||
photoFoldersFirst: true,
|
||||
noteSort: 'custom',
|
||||
checklistItemSort: 'custom',
|
||||
notifyPhoto: true,
|
||||
notifyNoteCreate: true,
|
||||
notifyNoteEdit: true,
|
||||
notifyItemAdd: true,
|
||||
notifyItemRecur: true,
|
||||
notifyItemDone: true,
|
||||
}
|
||||
|
||||
export async function getHousePrefs(houseId: number): Promise<HousePrefs> {
|
||||
const resp = await ocs.get<HousePrefs>(`/houses/${houseId}/prefs`)
|
||||
return { ...housePrefsDefaults, ...resp.data }
|
||||
}
|
||||
|
||||
export async function setHousePrefs(
|
||||
houseId: number,
|
||||
patch: Partial<HousePrefs>,
|
||||
): Promise<HousePrefs> {
|
||||
const resp = await ocs.put<HousePrefs>(`/houses/${houseId}/prefs`, patch)
|
||||
return { ...housePrefsDefaults, ...resp.data }
|
||||
}
|
||||
|
||||
// ----- Convenience wrappers for individual prefs -----
|
||||
|
||||
export async function getPhotoSort(houseId: number): Promise<PhotoSortPrefs> {
|
||||
const p = await getHousePrefs(houseId)
|
||||
return { sort: p.photoSort, foldersFirst: p.photoFoldersFirst }
|
||||
}
|
||||
|
||||
export async function setPhotoSort(
|
||||
houseId: number,
|
||||
prefs: Partial<PhotoSortPrefs>,
|
||||
): Promise<PhotoSortPrefs> {
|
||||
const patch: Partial<HousePrefs> = {}
|
||||
if (prefs.sort !== undefined) patch.photoSort = prefs.sort
|
||||
if (prefs.foldersFirst !== undefined) patch.photoFoldersFirst = prefs.foldersFirst
|
||||
const p = await setHousePrefs(houseId, patch)
|
||||
return { sort: p.photoSort, foldersFirst: p.photoFoldersFirst }
|
||||
}
|
||||
|
||||
export async function getNoteSort(houseId: number): Promise<{ sort: NoteSort }> {
|
||||
const p = await getHousePrefs(houseId)
|
||||
return { sort: p.noteSort }
|
||||
}
|
||||
|
||||
export async function setNoteSort(houseId: number, sort: NoteSort): Promise<{ sort: NoteSort }> {
|
||||
const p = await setHousePrefs(houseId, { noteSort: sort })
|
||||
return { sort: p.noteSort }
|
||||
}
|
||||
|
||||
export async function getChecklistItemSort(houseId: number): Promise<{ sort: ChecklistItemSort }> {
|
||||
const p = await getHousePrefs(houseId)
|
||||
return { sort: p.checklistItemSort }
|
||||
}
|
||||
|
||||
export async function setChecklistItemSort(
|
||||
houseId: number,
|
||||
sort: ChecklistItemSort,
|
||||
): Promise<{ sort: ChecklistItemSort }> {
|
||||
const p = await setHousePrefs(houseId, { checklistItemSort: sort })
|
||||
return { sort: p.checklistItemSort }
|
||||
}
|
||||
|
||||
export async function getImageFolder(houseId: number): Promise<string> {
|
||||
const p = await getHousePrefs(houseId)
|
||||
return p.imageFolder
|
||||
}
|
||||
|
||||
export async function setImageFolder(houseId: number, folder: string): Promise<string> {
|
||||
const p = await setHousePrefs(houseId, { imageFolder: folder })
|
||||
return p.imageFolder
|
||||
}
|
||||
|
||||
export async function getNotificationPrefs(houseId: number): Promise<NotificationPrefs> {
|
||||
const resp = await ocs.get<NotificationPrefs>(`/houses/${houseId}/prefs/notifications`)
|
||||
return (
|
||||
resp.data ?? {
|
||||
notifyPhoto: true,
|
||||
notifyNoteCreate: true,
|
||||
notifyNoteEdit: true,
|
||||
notifyItemAdd: true,
|
||||
notifyItemRecur: true,
|
||||
notifyItemDone: true,
|
||||
}
|
||||
)
|
||||
const p = await getHousePrefs(houseId)
|
||||
return {
|
||||
notifyPhoto: p.notifyPhoto,
|
||||
notifyNoteCreate: p.notifyNoteCreate,
|
||||
notifyNoteEdit: p.notifyNoteEdit,
|
||||
notifyItemAdd: p.notifyItemAdd,
|
||||
notifyItemRecur: p.notifyItemRecur,
|
||||
notifyItemDone: p.notifyItemDone,
|
||||
}
|
||||
}
|
||||
|
||||
export async function setNotificationPrefs(
|
||||
houseId: number,
|
||||
prefs: Partial<NotificationPrefs>,
|
||||
): Promise<NotificationPrefs> {
|
||||
const resp = await ocs.put<NotificationPrefs>(`/houses/${houseId}/prefs/notifications`, prefs)
|
||||
return (
|
||||
resp.data ?? {
|
||||
notifyPhoto: true,
|
||||
notifyNoteCreate: true,
|
||||
notifyNoteEdit: true,
|
||||
notifyItemAdd: true,
|
||||
notifyItemRecur: true,
|
||||
notifyItemDone: true,
|
||||
}
|
||||
)
|
||||
const p = await setHousePrefs(houseId, prefs)
|
||||
return {
|
||||
notifyPhoto: p.notifyPhoto,
|
||||
notifyNoteCreate: p.notifyNoteCreate,
|
||||
notifyNoteEdit: p.notifyNoteEdit,
|
||||
notifyItemAdd: p.notifyItemAdd,
|
||||
notifyItemRecur: p.notifyItemRecur,
|
||||
notifyItemDone: p.notifyItemDone,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,22 @@ namespace OCA\Pantry\Tests\Unit\Service;
|
||||
use OCA\Pantry\AppInfo\Application;
|
||||
use OCA\Pantry\Service\PrefsService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PrefsServiceTest extends TestCase {
|
||||
/** @var IConfig&MockObject */
|
||||
private IConfig $config;
|
||||
/** @var IL10N&MockObject */
|
||||
private IL10N $l;
|
||||
private PrefsService $svc;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->svc = new PrefsService($this->config);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->l->method('l')->willReturn('1'); // Monday fallback
|
||||
$this->svc = new PrefsService($this->config, $this->l);
|
||||
}
|
||||
|
||||
// ----- Notification preferences -----
|
||||
|
||||
Reference in New Issue
Block a user