File size: 2,671 Bytes
28ad89a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import type { CompactIdentity } from './identity'

/**
 * Numbered pin stack for comment mode. Saving a pin only appends — it never
 * sends a turn. Numbers are assigned 1..N in add order and stay put if a pin
 * is removed, so Comment 3 in the composer still matches marker 3 on the page.
 */

export type AnnotatePinKind = 'area' | 'element'

export interface AnnotateRect {
  height: number
  width: number
  x: number
  y: number
}

/** What a pin knows about its element. One shape, owned by `identity`. */
export type AnnotateIdentity = CompactIdentity

export interface AnnotatePin {
  id: string
  identity?: AnnotateIdentity
  imageDataUrl: string
  kind: AnnotatePinKind
  note: string
  number: number
  pageTitle: string
  pageUrl: string
  rect: AnnotateRect
}

export interface AnnotateStack {
  nextNumber: number
  pins: AnnotatePin[]
}

export type AnnotatePinDraft = Omit<AnnotatePin, 'id' | 'number'> & { id?: string }

export function emptyAnnotateStack(): AnnotateStack {
  return { nextNumber: 1, pins: [] }
}

export function addAnnotatePin(stack: AnnotateStack, draft: AnnotatePinDraft): AnnotateStack {
  const pin: AnnotatePin = {
    ...draft,
    id: draft.id || `annotate-${stack.nextNumber}`,
    number: stack.nextNumber
  }

  return {
    nextNumber: stack.nextNumber + 1,
    pins: [...stack.pins, pin]
  }
}

export function updateAnnotatePinNote(stack: AnnotateStack, id: string, note: string): AnnotateStack {
  return {
    ...stack,
    pins: stack.pins.map(pin => (pin.id === id ? { ...pin, note } : pin))
  }
}

export function removeAnnotatePin(stack: AnnotateStack, id: string): AnnotateStack {
  return {
    ...stack,
    pins: stack.pins.filter(pin => pin.id !== id)
  }
}

export function clearAnnotateStack(stack: AnnotateStack): AnnotateStack {
  void stack

  return emptyAnnotateStack()
}

/** Clear a flushed batch while keeping comment numbers unique in this page session. */
export function clearAnnotatePins(stack: AnnotateStack): AnnotateStack {
  return { ...stack, pins: [] }
}

export interface AnnotateSession {
  /** In-progress pick that has a crop but no saved note yet. */
  draft: AnnotatePinDraft | null
  mode: boolean
  overlayLive: boolean
  stack: AnnotateStack
}

export function emptyAnnotateSession(): AnnotateSession {
  return { draft: null, mode: false, overlayLive: false, stack: emptyAnnotateStack() }
}

export function beginAnnotateMode(session: AnnotateSession): AnnotateSession {
  return { ...session, mode: true, overlayLive: true }
}

export function endAnnotateMode(session: AnnotateSession): AnnotateSession {
  return { ...session, draft: null, mode: false, overlayLive: false }
}