diff --git a/src/action/index.ts b/src/action/index.ts index 9ca07ea..d4142e7 100644 --- a/src/action/index.ts +++ b/src/action/index.ts @@ -53,6 +53,7 @@ export { UpdateAppActivitiesAction } from './updateappactivities' export { UpdateCategoryBlockAllNotificationsAction } from './updatecategoryblockallnotifications' export { UpdateCategoryBlockedTimesAction } from './updatecategoryblockedtimes' export { UpdateCategoryTemporarilyBlockedAction } from './updatecategorytemporarilyblocked' +export { UpdateCategoryTimeWarningsAction } from './updatecategorytimewarnings' export { UpdateCategoryTitleAction } from './updatecategorytitle' export { UpdateDeviceNameAction } from './updatedevicename' export { UpdateDeviceStatusAction } from './updatedevicestatus' diff --git a/src/action/serialization/parentaction.ts b/src/action/serialization/parentaction.ts index a3ddd84..ac97862 100644 --- a/src/action/serialization/parentaction.ts +++ b/src/action/serialization/parentaction.ts @@ -44,6 +44,7 @@ import { SerializedSetUserTimezoneAction, SetUserTimezoneAction } from '../setus import { SerializedUpdateCategoryBlockAllNotificationsAction, UpdateCategoryBlockAllNotificationsAction } from '../updatecategoryblockallnotifications' import { SerializedUpdateCategoryBlockedTimesAction, UpdateCategoryBlockedTimesAction } from '../updatecategoryblockedtimes' import { SerializedUpdateCategoryTemporarilyBlockedAction, UpdateCategoryTemporarilyBlockedAction } from '../updatecategorytemporarilyblocked' +import { SerializedUpdateCategoryTimeWarningsAction, UpdateCategoryTimeWarningsAction } from '../updatecategorytimewarnings' import { SerializedUpdateCategoryTitleAction, UpdateCategoryTitleAction } from '../updatecategorytitle' import { SerializedUpdateDeviceNameAction, UpdateDeviceNameAction } from '../updatedevicename' import { SerializedUpdateEnableActivityLevelBlockingAction, UpdateEnableActivityLevelBlockingAction } from '../updateenableactivitylevelblocking' @@ -80,6 +81,7 @@ export type SerializedParentAction = SerializedUpdateCategoryBlockAllNotificationsAction | SerializedUpdateCategoryBlockedTimesAction | SerializedUpdateCategoryTemporarilyBlockedAction | + SerializedUpdateCategoryTimeWarningsAction | SerializedUpdateCategoryTitleAction | SerializedUpdateDeviceNameAction | SerializedUpdateEnableActivityLevelBlockingAction | @@ -142,6 +144,8 @@ export const parseParentAction = (action: SerializedParentAction): ParentAction return UpdateCategoryBlockAllNotificationsAction.parse(action) } else if (action.type === 'UPDATE_CATEGORY_BLOCKED_TIMES') { return UpdateCategoryBlockedTimesAction.parse(action) + } else if (action.type === 'UPDATE_CATEGORY_TIME_WARNINGS') { + return UpdateCategoryTimeWarningsAction.parse(action) } else if (action.type === 'UPDATE_CATEGORY_TITLE') { return UpdateCategoryTitleAction.parse(action) } else if (action.type === 'UPDATE_CATEGORY_TEMPORARILY_BLOCKED') { diff --git a/src/action/updatecategorytimewarnings.ts b/src/action/updatecategorytimewarnings.ts new file mode 100644 index 0000000..b2e887b --- /dev/null +++ b/src/action/updatecategorytimewarnings.ts @@ -0,0 +1,62 @@ +/* + * server component for the TimeLimit App + * Copyright (C) 2019 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { allowedTimeWarningFlags } from '../database/category' +import { assertIdWithinFamily } from '../util/token' +import { ParentAction } from './basetypes' + +export class UpdateCategoryTimeWarningsAction extends ParentAction { + readonly categoryId: string + readonly enable: boolean + readonly flags: number + + constructor ({ categoryId, enable, flags }: { + categoryId: string + enable: boolean + flags: number + }) { + super() + + assertIdWithinFamily(categoryId) + + if ((flags & allowedTimeWarningFlags) !== flags) { + throw new Error('illegal flags') + } + + this.categoryId = categoryId + this.enable = enable + this.flags = flags + } + + serialize = (): SerializedUpdateCategoryTimeWarningsAction => ({ + type: 'UPDATE_CATEGORY_TIME_WARNINGS', + categoryId: this.categoryId, + enable: this.enable, + flags: this.flags + }) + + static parse = ({ categoryId, enable, flags }: SerializedUpdateCategoryTimeWarningsAction) => ( + new UpdateCategoryTimeWarningsAction({ categoryId, enable, flags }) + ) +} + +export interface SerializedUpdateCategoryTimeWarningsAction { + type: 'UPDATE_CATEGORY_TIME_WARNINGS' + categoryId: string + enable: boolean + flags: number +} diff --git a/src/api/validator.ts b/src/api/validator.ts index 96e96c6..d158389 100644 --- a/src/api/validator.ts +++ b/src/api/validator.ts @@ -815,6 +815,33 @@ const definitions = { "type" ] }, + "SerializedUpdateCategoryTimeWarningsAction": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "UPDATE_CATEGORY_TIME_WARNINGS" + ] + }, + "categoryId": { + "type": "string" + }, + "enable": { + "type": "boolean" + }, + "flags": { + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "categoryId", + "enable", + "flags", + "type" + ] + }, "SerializedUpdateCategoryTitleAction": { "type": "object", "properties": { @@ -1567,6 +1594,9 @@ export const isSerializedParentAction: (value: object) => value is SerializedPar { "$ref": "#/definitions/SerializedUpdateCategoryTemporarilyBlockedAction" }, + { + "$ref": "#/definitions/SerializedUpdateCategoryTimeWarningsAction" + }, { "$ref": "#/definitions/SerializedUpdateCategoryTitleAction" }, diff --git a/src/database/category.ts b/src/database/category.ts index e7b3d8b..55f510b 100644 --- a/src/database/category.ts +++ b/src/database/category.ts @@ -20,6 +20,8 @@ import { serializedBitmaskRegex } from '../util/bitmask' import { booleanColumn, familyIdColumn, idWithinFamilyColumn, labelColumn, optionalIdWithinFamilyColumn, versionColumn } from './columns' import { SequelizeAttributes } from './types' +export const allowedTimeWarningFlags = 1 | 2 | 4 | 8 | 16 + export interface CategoryAttributesVersion1 { familyId: string categoryId: string @@ -105,7 +107,7 @@ export const attributesVersion4: SequelizeAttributes allowNull: false, validate: { min: 0, - max: 1 | 2 | 4 | 8 | 16 + max: allowedTimeWarningFlags // 1 => 1 minute // 2 => 3 minutes // 4 => 5 minutes diff --git a/src/function/sync/apply-actions/dispatch-parent-action/index.ts b/src/function/sync/apply-actions/dispatch-parent-action/index.ts index 47e5ef8..d976bb4 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/index.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/index.ts @@ -45,6 +45,7 @@ import { UpdateCategoryBlockAllNotificationsAction, UpdateCategoryBlockedTimesAction, UpdateCategoryTemporarilyBlockedAction, + UpdateCategoryTimeWarningsAction, UpdateCategoryTitleAction, UpdateDeviceNameAction, UpdateEnableActivityLevelBlockingAction, @@ -81,6 +82,7 @@ import { dispatchSetUserTimezone } from './setusertimezone' import { dispatchUpdateCategoryBlockAllNotifications } from './updatecategoryblockallnotifications' import { dispatchUpdateCategoryBlockedTimes } from './updatecategoryblockedtimes' import { dispatchUpdateCategoryTemporarilyBlocked } from './updatecategorytemporarilyblocked' +import { dispatchUpdateCategoryTimeWarnings } from './updatecategorytimewarnings' import { dispatchUpdateCategoryTitle } from './updatecategorytitle' import { dispatchUpdateDeviceName } from './updatedevicename' import { dispatchUpdateEnableActivityLevelBlocking } from './updateenableactivitylevelblocking' @@ -162,6 +164,8 @@ export const dispatchParentAction = async ({ action, cache, parentUserId, source await dispatchChangeParentPassword({ action, cache }) } else if (action instanceof IgnoreManipulationAction) { await dispatchIgnoreManipulation({ action, cache }) + } else if (action instanceof UpdateCategoryTimeWarningsAction) { + await dispatchUpdateCategoryTimeWarnings({ action, cache }) } else { throw new Error('unsupported action type') } diff --git a/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytimewarnings.ts b/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytimewarnings.ts new file mode 100644 index 0000000..b611e2a --- /dev/null +++ b/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytimewarnings.ts @@ -0,0 +1,47 @@ +/* + * server component for the TimeLimit App + * Copyright (C) 2019 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { UpdateCategoryTimeWarningsAction } from '../../../../action' +import { Cache } from '../cache' + +export async function dispatchUpdateCategoryTimeWarnings ({ action, cache }: { + action: UpdateCategoryTimeWarningsAction + cache: Cache +}) { + const categoryEntry = await cache.database.category.findOne({ + where: { + familyId: cache.familyId, + categoryId: action.categoryId + }, + transaction: cache.transaction + }) + + if (!categoryEntry) { + throw new Error('invalid category id') + } + + if (action.enable) { + categoryEntry.timeWarningFlags |= action.flags + } else { + categoryEntry.timeWarningFlags &= ~action.flags + } + + await categoryEntry.save({ transaction: cache.transaction }) + + cache.categoriesWithModifiedBaseData.push(action.categoryId) + cache.areChangesImportant = true +}