diff --git a/src/action/updatecategorytemporarilyblocked.ts b/src/action/updatecategorytemporarilyblocked.ts index 1768f16..ae04692 100644 --- a/src/action/updatecategorytemporarilyblocked.ts +++ b/src/action/updatecategorytemporarilyblocked.ts @@ -1,6 +1,6 @@ /* * server component for the TimeLimit App - * Copyright (C) 2019 Jonas Lochmann + * Copyright (C) 2019 - 2020 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 @@ -21,24 +21,41 @@ import { ParentAction } from './basetypes' export class UpdateCategoryTemporarilyBlockedAction extends ParentAction { readonly categoryId: string readonly blocked: boolean + readonly endTime?: number - constructor ({ categoryId, blocked }: {categoryId: string, blocked: boolean}) { + constructor ({ categoryId, blocked, endTime }: { + categoryId: string + blocked: boolean + endTime?: number + }) { super() assertIdWithinFamily(categoryId) + if (endTime !== undefined) { + if (!Number.isSafeInteger(endTime)) { + throw new Error() + } + + if (!blocked) { + throw new Error() + } + } + this.categoryId = categoryId this.blocked = blocked + this.endTime = endTime } serialize = (): SerializedUpdateCategoryTemporarilyBlockedAction => ({ type: 'UPDATE_CATEGORY_TEMPORARILY_BLOCKED', categoryId: this.categoryId, - blocked: this.blocked + blocked: this.blocked, + endTime: this.endTime }) - static parse = ({ categoryId, blocked }: SerializedUpdateCategoryTemporarilyBlockedAction) => ( - new UpdateCategoryTemporarilyBlockedAction({ categoryId, blocked }) + static parse = ({ categoryId, blocked, endTime }: SerializedUpdateCategoryTemporarilyBlockedAction) => ( + new UpdateCategoryTemporarilyBlockedAction({ categoryId, blocked, endTime }) ) } @@ -46,4 +63,5 @@ export interface SerializedUpdateCategoryTemporarilyBlockedAction { type: 'UPDATE_CATEGORY_TEMPORARILY_BLOCKED' categoryId: string blocked: boolean + endTime?: number } diff --git a/src/api/validator.ts b/src/api/validator.ts index 354258a..d2d1386 100644 --- a/src/api/validator.ts +++ b/src/api/validator.ts @@ -853,6 +853,9 @@ const definitions = { }, "blocked": { "type": "boolean" + }, + "endTime": { + "type": "number" } }, "additionalProperties": false, diff --git a/src/database/category.ts b/src/database/category.ts index d88e43a..504de2f 100644 --- a/src/database/category.ts +++ b/src/database/category.ts @@ -17,7 +17,7 @@ import * as Sequelize from 'sequelize' import { serializedBitmaskRegex } from '../util/bitmask' -import { booleanColumn, familyIdColumn, idWithinFamilyColumn, labelColumn, optionalIdWithinFamilyColumn, versionColumn } from './columns' +import { booleanColumn, familyIdColumn, idWithinFamilyColumn, labelColumn, optionalIdWithinFamilyColumn, timestampColumn, versionColumn } from './columns' import { SequelizeAttributes } from './types' export const allowedTimeWarningFlags = 1 | 2 | 4 | 8 | 16 @@ -53,8 +53,13 @@ export interface CategoryAttributesVersion5 { minBatteryMobile: number } +export interface CategoryAttributesVersion6 { + temporarilyBlockedEndTime: number +} + export type CategoryAttributes = CategoryAttributesVersion1 & CategoryAttributesVersion2 & - CategoryAttributesVersion3 & CategoryAttributesVersion4 & CategoryAttributesVersion5 + CategoryAttributesVersion3 & CategoryAttributesVersion4 & CategoryAttributesVersion5 & + CategoryAttributesVersion6 export type CategoryModel = Sequelize.Model & CategoryAttributes export type CategoryModelStatic = typeof Sequelize.Model & { @@ -145,12 +150,20 @@ export const attributesVersion5: SequelizeAttributes } } +export const attributesVersion6: SequelizeAttributes = { + temporarilyBlockedEndTime: { + ...timestampColumn, + defaultValue: 0 + } +} + export const attributes: SequelizeAttributes = { ...attributesVersion1, ...attributesVersion2, ...attributesVersion3, ...attributesVersion4, - ...attributesVersion5 + ...attributesVersion5, + ...attributesVersion6 } export const createCategoryModel = (sequelize: Sequelize.Sequelize): CategoryModelStatic => sequelize.define('Category', attributes) as CategoryModelStatic diff --git a/src/database/migration/migrations/20200203-add-temporarily-blocked-end-time.ts b/src/database/migration/migrations/20200203-add-temporarily-blocked-end-time.ts new file mode 100644 index 0000000..523acd5 --- /dev/null +++ b/src/database/migration/migrations/20200203-add-temporarily-blocked-end-time.ts @@ -0,0 +1,39 @@ +/* + * server component for the TimeLimit App + * Copyright (C) 2019 - 2020 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 { QueryInterface, Sequelize, Transaction } from 'sequelize' +import { attributesVersion6 as categoryAttributes } from '../../category' + +export async function up (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.addColumn('Categories', 'temporarilyBlockedEndTime', { + ...categoryAttributes.temporarilyBlockedEndTime + }, { + transaction + }) + }) +} + +export async function down (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.removeColumn('Categories', 'temporarilyBlockedEndTime', { transaction }) + }) +} diff --git a/src/function/sync/apply-actions/dispatch-parent-action/createcategory.ts b/src/function/sync/apply-actions/dispatch-parent-action/createcategory.ts index 389b341..ee16090 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/createcategory.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/createcategory.ts @@ -45,6 +45,7 @@ export async function dispatchCreateCategory ({ action, cache }: { title: action.title, blockedMinutesInWeek: '', temporarilyBlocked: false, + temporarilyBlockedEndTime: 0, extraTimeInMillis: 0, timeLimitRulesVersion: generateVersionId(), baseVersion: generateVersionId(), diff --git a/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytemporarilyblocked.ts b/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytemporarilyblocked.ts index 20c1506..070dda0 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytemporarilyblocked.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/updatecategorytemporarilyblocked.ts @@ -1,6 +1,6 @@ /* * server component for the TimeLimit App - * Copyright (C) 2019 Jonas Lochmann + * Copyright (C) 2019 - 2020 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 @@ -29,7 +29,8 @@ export async function dispatchUpdateCategoryTemporarilyBlocked ({ action, cache } const [affectedRows] = await cache.database.category.update({ - temporarilyBlocked: action.blocked + temporarilyBlocked: action.blocked, + temporarilyBlockedEndTime: action.blocked ? (action.endTime ?? 0) : 0 }, { where: { familyId: cache.familyId, diff --git a/src/function/sync/get-server-data-status.ts b/src/function/sync/get-server-data-status.ts index e93c379..563f4fa 100644 --- a/src/function/sync/get-server-data-status.ts +++ b/src/function/sync/get-server-data-status.ts @@ -341,7 +341,8 @@ export const generateServerDataStatus = async ({ database, clientStatus, familyI 'blockAllNotifications', 'timeWarningFlags', 'minBatteryCharging', - 'minBatteryMobile' + 'minBatteryMobile', + 'temporarilyBlockedEndTime' ], transaction })).map((item) => ({ @@ -356,7 +357,8 @@ export const generateServerDataStatus = async ({ database, clientStatus, familyI blockAllNotifications: item.blockAllNotifications, timeWarningFlags: item.timeWarningFlags, minBatteryCharging: item.minBatteryCharging, - minBatteryMobile: item.minBatteryMobile + minBatteryMobile: item.minBatteryMobile, + temporarilyBlockedEndTime: item.temporarilyBlockedEndTime })) result.categoryBase = dataForSyncing.map((item): ServerUpdatedCategoryBaseData => ({ @@ -371,7 +373,8 @@ export const generateServerDataStatus = async ({ database, clientStatus, familyI blockAllNotifications: item.blockAllNotifications, timeWarnings: item.timeWarningFlags, mblMobile: item.minBatteryMobile, - mblCharging: item.minBatteryCharging + mblCharging: item.minBatteryCharging, + tempBlockTime: item.temporarilyBlockedEndTime })) } diff --git a/src/object/serverdatastatus.ts b/src/object/serverdatastatus.ts index e514ff1..a58efc4 100644 --- a/src/object/serverdatastatus.ts +++ b/src/object/serverdatastatus.ts @@ -100,6 +100,7 @@ export interface ServerUpdatedCategoryBaseData { blockedTimes: string // blockedMinutesInWeek extraTime: number tempBlocked: boolean + tempBlockTime: number version: string parentCategoryId: string blockAllNotifications: boolean