Skip to content
net.ts 4.46 KiB
Newer Older
export interface ServerToClientEvents {
Grant's avatar
Grant committed
  canvas: (
    start: [x: number, y: number],
    end: [x: number, y: number],
    pixels: string[]
  ) => void;
  clearCanvasChunks: () => void;
  user: (user: AuthSession) => void;
  standing: (standing: IAccountStanding) => void;
  config: (config: ClientConfig) => void;
  pixel: (pixel: Pixel) => void;
  online: (count: { count: number }) => void;
Grant's avatar
Grant committed
  availablePixels: (count: number) => void;
  pixelLastPlaced: (time: number) => void;
Grant's avatar
Grant committed
  undo: (
    data: { available: false } | { available: true; expireAt: number }
  ) => void;
Grant's avatar
Grant committed
  square: (
    start: [x: number, y: number],
    end: [x: number, y: number],
    color: number
  ) => void;
  alert: (alert: IAlert) => void;
  alert_dismiss: (id: string) => void;

Grant's avatar
Grant committed
  recaptcha: (site_key: string) => void;
  recaptcha_challenge: (ack: (token: string) => void) => void;

  /* --- subscribe events --- */

  /**
   * Emitted to room `sub:heatmap`
   * @param heatmap
   * @returns
   */
  heatmap: (heatmap: string) => void;
}

export interface ClientToServerEvents {
Grant's avatar
Grant committed
  place: (
    pixel: Pixel,
    bypassCooldown: boolean,
Grant's avatar
Grant committed
    ack: (
      _: PacketAck<
        Pixel,
Grant's avatar
Grant committed
        | "canvas_frozen"
        | "no_user"
        | "invalid_pixel"
        | "pixel_cooldown"
        | "palette_color_invalid"
        | "you_already_placed_that"
Ategon Dev's avatar
Ategon Dev committed
        | "pixel_already_pending"
Grant's avatar
Grant committed
      >
    ) => void
  ) => void;
Grant's avatar
Grant committed
  undo: (
Grant's avatar
Grant committed
    ack: (
      _: PacketAck<
        {},
        "canvas_frozen" | "no_user" | "unavailable" | "pixel_covered"
      >
    ) => void
Grant's avatar
Grant committed
  ) => void;
  subscribe: (topic: Subscription) => void;
  unsubscribe: (topic: Subscription) => void;
Grant's avatar
Grant committed
  x: number;
  y: number;
Grant's avatar
Grant committed

export type IAccountStanding =
  | {
      banned: false;
    }
  | {
      banned: true;
      /**
       * ISO timestamp
       */
      until: string;
      reason?: string;
    };

/**
 * Typescript magic
 *
 * key => name of the event
 * value => what metadata the message will include
 */
export interface IAlertKeyedMessages {
  banned: {
    /**
     * ISO date
     */
    until: string;
  };
  unbanned: {};
}

export type IAlert<Is extends "toast" | "modal" = "toast" | "modal"> = {
  is: Is;
  action: "system" | "moderation";
  id?: string;
} & (
  | {
      is: "toast";
      severity: "info" | "success" | "warning" | "error" | "default";
      autoDismiss: boolean;
    }
  | {
      is: "modal";
      dismissable: boolean;
    }
) &
  (IAlertKeyed | { title: string; body?: string });

/**
 * Typescript magic
 *
 * #metadata depends on message_key and is mapped via IAlertKeyedMessages
 */
type IAlertKeyed = keyof IAlertKeyedMessages extends infer MessageKey
  ? MessageKey extends keyof IAlertKeyedMessages
    ? {
        message_key: MessageKey;
        metadata: IAlertKeyedMessages[MessageKey];
      }
    : never
  : never;
Grant's avatar
Grant committed

export type Pixel = {
Grant's avatar
Grant committed
  x: number;
  y: number;
Grant's avatar
Grant committed
  /**
   * Palette color ID or -1 for nothing
   */
Grant's avatar
Grant committed
  color: number;
};

export type PalleteColor = {
  id: number;
  name: string;
  hex: string;
Grant's avatar
Grant committed
};

export type CanvasConfig = {
  size: [number, number];
Grant's avatar
Grant committed
  frozen: boolean;
  zoom: number;
Grant's avatar
Grant committed
  pixel: {
    maxStack: number;
    cooldown: number;
    multiplier: number;
  };
Grant's avatar
Grant committed
  undo: {
    /**
     * time in ms to allow undos
     */
    grace_period: number;
  };
Grant's avatar
Grant committed
};

export type ClientConfig = {
  /**
   * Monolith git hash, if it doesn't match, client will reload
   */
  version: string;
  pallete: {
    colors: PalleteColor[];
    pixel_cooldown: number;
  };
  canvas: CanvasConfig;
  chat: {
    enabled: boolean;
    /**
     * @example aftermath.gg
     */
    matrix_homeserver: string;
    /**
     * @example https://chat.fediverse.events
     */
    element_host: string;
    /**
     * URI encoded alias
     * @example %23canvas-general:aftermath.gg
     */
    general_alias: string;
Grant's avatar
Grant committed
};

Grant's avatar
Grant committed
/**
 * @template T the packet data
 * @template E union type of errors possible
 */
export type PacketAck<T, E = string> =
Grant's avatar
Grant committed
  | {
      success: true;
      data: T;
    }
Grant's avatar
Grant committed
  | { success: false; error: E };
Grant's avatar
Grant committed

export type AuthSession = {
  service: {
    software: {
      name: string;
      version: string;
Grant's avatar
Grant committed
      logo_uri?: string;
      repository?: string;
      homepage?: string;
Grant's avatar
Grant committed
    };
    instance: {
      hostname: string;
Grant's avatar
Grant committed
      logo_uri?: string;
      banner_uri?: string;
      name?: string;
Grant's avatar
Grant committed
    };
  };
  user: {
    username: string;
    display_name?: string;
Grant's avatar
Grant committed
    picture_url?: string;
Grant's avatar
Grant committed
  };
};