/* SPDX-FileCopyrightText: © 2025 Hornwitser SPDX-License-Identifier: AGPL-3.0-or-later */ import type { ApiEntity, ApiTombstone } from "~/shared/types/api"; import type { Id } from "~/shared/types/common"; import { DateTime, Zone } from "~/shared/utils/luxon"; export abstract class ClientEntity { /** Millisecond offset used to indicate this is a new entitity. */ static newEntityMillis = -1; /** Timestamp of the entity received from server. If this is a new entity this will have a millisecond offset equal to {@link ClientEntity.newEntityMillis}. */ serverUpdatedAt: DateTime; /** True if the server has deleted this entity, but the client is holding on to it in order to resolve an edit conflcit */ serverDeleted: boolean; constructor( /** Server supplied id of this entity. Each kind of entity has its own namespace of ids. */ public readonly id: Id, /** Server's timestamp of this entity at the time it was modified. If the entity is unmodified this will track {@link serverUpdatedAt}. If this is a new entity it'll have a millesecond offset equal to {@link ClientEntity.newEntityMillis}. */ public updatedAt: DateTime, /** Flag indicating the client intends to delete this entity. */ public deleted: boolean, ) { this.serverUpdatedAt = updatedAt; this.serverDeleted = deleted; } /** True if this entity does not yet exist on the server. */ isNew() { return this.serverUpdatedAt.toMillis() === ClientEntity.newEntityMillis; } /** True if both the server and the client have modified this entity independently of each other. */ isConflict() { return this.serverUpdatedAt.toMillis() !== this.updatedAt.toMillis(); } /** True if this entity has been modified on the client. */ isModified() { return ( this.isNew() || this.deleted || this.serverDeleted ); } /** Discard any client side modifications to this entity. Not allowed if {@link serverDeleted} is true or this is a new entity. */ abstract discard(): void /** Apply an update delivered from the API to this entity. */ abstract apiUpdate(api: Api, opts: { zone: Zone, locale: string }): void /** Serialise this entity to the API format. Not allowed if {@link deleted} is true. */ abstract toApi(): Api | ApiTombstone }